1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2018 Linaro Limited, All rights reserved. |
4 | * Author: Mike Leach <mike.leach@linaro.org> |
5 | */ |
6 | |
7 | #include <linux/amba/bus.h> |
8 | #include <linux/atomic.h> |
9 | #include <linux/bits.h> |
10 | #include <linux/coresight.h> |
11 | #include <linux/cpu_pm.h> |
12 | #include <linux/cpuhotplug.h> |
13 | #include <linux/device.h> |
14 | #include <linux/io.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/list.h> |
17 | #include <linux/mutex.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/property.h> |
20 | #include <linux/spinlock.h> |
21 | |
22 | #include "coresight-priv.h" |
23 | #include "coresight-cti.h" |
24 | |
25 | /* |
26 | * CTI devices can be associated with a PE, or be connected to CoreSight |
27 | * hardware. We have a list of all CTIs irrespective of CPU bound or |
28 | * otherwise. |
29 | * |
30 | * We assume that the non-CPU CTIs are always powered as we do with sinks etc. |
31 | * |
32 | * We leave the client to figure out if all the CTIs are interconnected with |
33 | * the same CTM, in general this is the case but does not always have to be. |
34 | */ |
35 | |
36 | /* net of CTI devices connected via CTM */ |
37 | static LIST_HEAD(ect_net); |
38 | |
39 | /* protect the list */ |
40 | static DEFINE_MUTEX(ect_mutex); |
41 | |
42 | #define csdev_to_cti_drvdata(csdev) \ |
43 | dev_get_drvdata(csdev->dev.parent) |
44 | |
45 | /* power management handling */ |
46 | static int nr_cti_cpu; |
47 | |
48 | /* quick lookup list for CPU bound CTIs when power handling */ |
49 | static struct cti_drvdata *cti_cpu_drvdata[NR_CPUS]; |
50 | |
51 | /* |
52 | * CTI naming. CTI bound to cores will have the name cti_cpu<N> where |
53 | * N is the CPU ID. System CTIs will have the name cti_sys<I> where I |
54 | * is an index allocated by order of discovery. |
55 | * |
56 | * CTI device name list - for CTI not bound to cores. |
57 | */ |
58 | DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys" ); |
59 | |
60 | /* write set of regs to hardware - call with spinlock claimed */ |
61 | void cti_write_all_hw_regs(struct cti_drvdata *drvdata) |
62 | { |
63 | struct cti_config *config = &drvdata->config; |
64 | int i; |
65 | |
66 | CS_UNLOCK(addr: drvdata->base); |
67 | |
68 | /* disable CTI before writing registers */ |
69 | writel_relaxed(0, drvdata->base + CTICONTROL); |
70 | |
71 | /* write the CTI trigger registers */ |
72 | for (i = 0; i < config->nr_trig_max; i++) { |
73 | writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i)); |
74 | writel_relaxed(config->ctiouten[i], |
75 | drvdata->base + CTIOUTEN(i)); |
76 | } |
77 | |
78 | /* other regs */ |
79 | writel_relaxed(config->ctigate, drvdata->base + CTIGATE); |
80 | writel_relaxed(config->asicctl, drvdata->base + ASICCTL); |
81 | writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET); |
82 | |
83 | /* re-enable CTI */ |
84 | writel_relaxed(1, drvdata->base + CTICONTROL); |
85 | |
86 | CS_LOCK(addr: drvdata->base); |
87 | } |
88 | |
89 | /* write regs to hardware and enable */ |
90 | static int cti_enable_hw(struct cti_drvdata *drvdata) |
91 | { |
92 | struct cti_config *config = &drvdata->config; |
93 | unsigned long flags; |
94 | int rc = 0; |
95 | |
96 | spin_lock_irqsave(&drvdata->spinlock, flags); |
97 | |
98 | /* no need to do anything if enabled or unpowered*/ |
99 | if (config->hw_enabled || !config->hw_powered) |
100 | goto cti_state_unchanged; |
101 | |
102 | /* claim the device */ |
103 | rc = coresight_claim_device(csdev: drvdata->csdev); |
104 | if (rc) |
105 | goto cti_err_not_enabled; |
106 | |
107 | cti_write_all_hw_regs(drvdata); |
108 | |
109 | config->hw_enabled = true; |
110 | drvdata->config.enable_req_count++; |
111 | spin_unlock_irqrestore(lock: &drvdata->spinlock, flags); |
112 | return rc; |
113 | |
114 | cti_state_unchanged: |
115 | drvdata->config.enable_req_count++; |
116 | |
117 | /* cannot enable due to error */ |
118 | cti_err_not_enabled: |
119 | spin_unlock_irqrestore(lock: &drvdata->spinlock, flags); |
120 | return rc; |
121 | } |
122 | |
123 | /* re-enable CTI on CPU when using CPU hotplug */ |
124 | static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata) |
125 | { |
126 | struct cti_config *config = &drvdata->config; |
127 | |
128 | spin_lock(lock: &drvdata->spinlock); |
129 | config->hw_powered = true; |
130 | |
131 | /* no need to do anything if no enable request */ |
132 | if (!drvdata->config.enable_req_count) |
133 | goto cti_hp_not_enabled; |
134 | |
135 | /* try to claim the device */ |
136 | if (coresight_claim_device(csdev: drvdata->csdev)) |
137 | goto cti_hp_not_enabled; |
138 | |
139 | cti_write_all_hw_regs(drvdata); |
140 | config->hw_enabled = true; |
141 | spin_unlock(lock: &drvdata->spinlock); |
142 | return; |
143 | |
144 | /* did not re-enable due to no claim / no request */ |
145 | cti_hp_not_enabled: |
146 | spin_unlock(lock: &drvdata->spinlock); |
147 | } |
148 | |
149 | /* disable hardware */ |
150 | static int cti_disable_hw(struct cti_drvdata *drvdata) |
151 | { |
152 | struct cti_config *config = &drvdata->config; |
153 | struct coresight_device *csdev = drvdata->csdev; |
154 | int ret = 0; |
155 | |
156 | spin_lock(lock: &drvdata->spinlock); |
157 | |
158 | /* don't allow negative refcounts, return an error */ |
159 | if (!drvdata->config.enable_req_count) { |
160 | ret = -EINVAL; |
161 | goto cti_not_disabled; |
162 | } |
163 | |
164 | /* check refcount - disable on 0 */ |
165 | if (--drvdata->config.enable_req_count > 0) |
166 | goto cti_not_disabled; |
167 | |
168 | /* no need to do anything if disabled or cpu unpowered */ |
169 | if (!config->hw_enabled || !config->hw_powered) |
170 | goto cti_not_disabled; |
171 | |
172 | CS_UNLOCK(addr: drvdata->base); |
173 | |
174 | /* disable CTI */ |
175 | writel_relaxed(0, drvdata->base + CTICONTROL); |
176 | config->hw_enabled = false; |
177 | |
178 | coresight_disclaim_device_unlocked(csdev); |
179 | CS_LOCK(addr: drvdata->base); |
180 | spin_unlock(lock: &drvdata->spinlock); |
181 | return ret; |
182 | |
183 | /* not disabled this call */ |
184 | cti_not_disabled: |
185 | spin_unlock(lock: &drvdata->spinlock); |
186 | return ret; |
187 | } |
188 | |
189 | void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) |
190 | { |
191 | CS_UNLOCK(addr: drvdata->base); |
192 | writel_relaxed(value, drvdata->base + offset); |
193 | CS_LOCK(addr: drvdata->base); |
194 | } |
195 | |
196 | void cti_write_intack(struct device *dev, u32 ackval) |
197 | { |
198 | struct cti_drvdata *drvdata = dev_get_drvdata(dev: dev->parent); |
199 | struct cti_config *config = &drvdata->config; |
200 | |
201 | spin_lock(lock: &drvdata->spinlock); |
202 | /* write if enabled */ |
203 | if (cti_active(cfg: config)) |
204 | cti_write_single_reg(drvdata, CTIINTACK, value: ackval); |
205 | spin_unlock(lock: &drvdata->spinlock); |
206 | } |
207 | |
208 | /* |
209 | * Look at the HW DEVID register for some of the HW settings. |
210 | * DEVID[15:8] - max number of in / out triggers. |
211 | */ |
212 | #define CTI_DEVID_MAXTRIGS(devid_val) ((int) BMVAL(devid_val, 8, 15)) |
213 | |
214 | /* DEVID[19:16] - number of CTM channels */ |
215 | #define CTI_DEVID_CTMCHANNELS(devid_val) ((int) BMVAL(devid_val, 16, 19)) |
216 | |
217 | static void cti_set_default_config(struct device *dev, |
218 | struct cti_drvdata *drvdata) |
219 | { |
220 | struct cti_config *config = &drvdata->config; |
221 | u32 devid; |
222 | |
223 | devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); |
224 | config->nr_trig_max = CTI_DEVID_MAXTRIGS(devid); |
225 | |
226 | /* |
227 | * no current hardware should exceed this, but protect the driver |
228 | * in case of fault / out of spec hw |
229 | */ |
230 | if (config->nr_trig_max > CTIINOUTEN_MAX) { |
231 | dev_warn_once(dev, |
232 | "Limiting HW MaxTrig value(%d) to driver max(%d)\n" , |
233 | config->nr_trig_max, CTIINOUTEN_MAX); |
234 | config->nr_trig_max = CTIINOUTEN_MAX; |
235 | } |
236 | |
237 | config->nr_ctm_channels = CTI_DEVID_CTMCHANNELS(devid); |
238 | |
239 | /* Most regs default to 0 as zalloc'ed except...*/ |
240 | config->trig_filter_enable = true; |
241 | config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0); |
242 | config->enable_req_count = 0; |
243 | } |
244 | |
245 | /* |
246 | * Add a connection entry to the list of connections for this |
247 | * CTI device. |
248 | */ |
249 | int cti_add_connection_entry(struct device *dev, struct cti_drvdata *drvdata, |
250 | struct cti_trig_con *tc, |
251 | struct coresight_device *csdev, |
252 | const char *assoc_dev_name) |
253 | { |
254 | struct cti_device *cti_dev = &drvdata->ctidev; |
255 | |
256 | tc->con_dev = csdev; |
257 | /* |
258 | * Prefer actual associated CS device dev name to supplied value - |
259 | * which is likely to be node name / other conn name. |
260 | */ |
261 | if (csdev) |
262 | tc->con_dev_name = dev_name(dev: &csdev->dev); |
263 | else if (assoc_dev_name != NULL) { |
264 | tc->con_dev_name = devm_kstrdup(dev, |
265 | s: assoc_dev_name, GFP_KERNEL); |
266 | if (!tc->con_dev_name) |
267 | return -ENOMEM; |
268 | } |
269 | list_add_tail(new: &tc->node, head: &cti_dev->trig_cons); |
270 | cti_dev->nr_trig_con++; |
271 | |
272 | /* add connection usage bit info to overall info */ |
273 | drvdata->config.trig_in_use |= tc->con_in->used_mask; |
274 | drvdata->config.trig_out_use |= tc->con_out->used_mask; |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | /* create a trigger connection with appropriately sized signal groups */ |
280 | struct cti_trig_con *cti_allocate_trig_con(struct device *dev, int in_sigs, |
281 | int out_sigs) |
282 | { |
283 | struct cti_trig_con *tc = NULL; |
284 | struct cti_trig_grp *in = NULL, *out = NULL; |
285 | |
286 | tc = devm_kzalloc(dev, size: sizeof(struct cti_trig_con), GFP_KERNEL); |
287 | if (!tc) |
288 | return tc; |
289 | |
290 | in = devm_kzalloc(dev, |
291 | offsetof(struct cti_trig_grp, sig_types[in_sigs]), |
292 | GFP_KERNEL); |
293 | if (!in) |
294 | return NULL; |
295 | |
296 | out = devm_kzalloc(dev, |
297 | offsetof(struct cti_trig_grp, sig_types[out_sigs]), |
298 | GFP_KERNEL); |
299 | if (!out) |
300 | return NULL; |
301 | |
302 | tc->con_in = in; |
303 | tc->con_out = out; |
304 | tc->con_in->nr_sigs = in_sigs; |
305 | tc->con_out->nr_sigs = out_sigs; |
306 | return tc; |
307 | } |
308 | |
309 | /* |
310 | * Add a default connection if nothing else is specified. |
311 | * single connection based on max in/out info, no assoc device |
312 | */ |
313 | int cti_add_default_connection(struct device *dev, struct cti_drvdata *drvdata) |
314 | { |
315 | int ret = 0; |
316 | int n_trigs = drvdata->config.nr_trig_max; |
317 | u32 n_trig_mask = GENMASK(n_trigs - 1, 0); |
318 | struct cti_trig_con *tc = NULL; |
319 | |
320 | /* |
321 | * Assume max trigs for in and out, |
322 | * all used, default sig types allocated |
323 | */ |
324 | tc = cti_allocate_trig_con(dev, in_sigs: n_trigs, out_sigs: n_trigs); |
325 | if (!tc) |
326 | return -ENOMEM; |
327 | |
328 | tc->con_in->used_mask = n_trig_mask; |
329 | tc->con_out->used_mask = n_trig_mask; |
330 | ret = cti_add_connection_entry(dev, drvdata, tc, NULL, assoc_dev_name: "default" ); |
331 | return ret; |
332 | } |
333 | |
334 | /** cti channel api **/ |
335 | /* attach/detach channel from trigger - write through if enabled. */ |
336 | int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, |
337 | enum cti_trig_dir direction, u32 channel_idx, |
338 | u32 trigger_idx) |
339 | { |
340 | struct cti_drvdata *drvdata = dev_get_drvdata(dev: dev->parent); |
341 | struct cti_config *config = &drvdata->config; |
342 | u32 trig_bitmask; |
343 | u32 chan_bitmask; |
344 | u32 reg_value; |
345 | int reg_offset; |
346 | |
347 | /* ensure indexes in range */ |
348 | if ((channel_idx >= config->nr_ctm_channels) || |
349 | (trigger_idx >= config->nr_trig_max)) |
350 | return -EINVAL; |
351 | |
352 | trig_bitmask = BIT(trigger_idx); |
353 | |
354 | /* ensure registered triggers and not out filtered */ |
355 | if (direction == CTI_TRIG_IN) { |
356 | if (!(trig_bitmask & config->trig_in_use)) |
357 | return -EINVAL; |
358 | } else { |
359 | if (!(trig_bitmask & config->trig_out_use)) |
360 | return -EINVAL; |
361 | |
362 | if ((config->trig_filter_enable) && |
363 | (config->trig_out_filter & trig_bitmask)) |
364 | return -EINVAL; |
365 | } |
366 | |
367 | /* update the local register values */ |
368 | chan_bitmask = BIT(channel_idx); |
369 | reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) : |
370 | CTIOUTEN(trigger_idx)); |
371 | |
372 | spin_lock(lock: &drvdata->spinlock); |
373 | |
374 | /* read - modify write - the trigger / channel enable value */ |
375 | reg_value = direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] : |
376 | config->ctiouten[trigger_idx]; |
377 | if (op == CTI_CHAN_ATTACH) |
378 | reg_value |= chan_bitmask; |
379 | else |
380 | reg_value &= ~chan_bitmask; |
381 | |
382 | /* write local copy */ |
383 | if (direction == CTI_TRIG_IN) |
384 | config->ctiinen[trigger_idx] = reg_value; |
385 | else |
386 | config->ctiouten[trigger_idx] = reg_value; |
387 | |
388 | /* write through if enabled */ |
389 | if (cti_active(cfg: config)) |
390 | cti_write_single_reg(drvdata, offset: reg_offset, value: reg_value); |
391 | spin_unlock(lock: &drvdata->spinlock); |
392 | return 0; |
393 | } |
394 | |
395 | int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op, |
396 | u32 channel_idx) |
397 | { |
398 | struct cti_drvdata *drvdata = dev_get_drvdata(dev: dev->parent); |
399 | struct cti_config *config = &drvdata->config; |
400 | u32 chan_bitmask; |
401 | u32 reg_value; |
402 | int err = 0; |
403 | |
404 | if (channel_idx >= config->nr_ctm_channels) |
405 | return -EINVAL; |
406 | |
407 | chan_bitmask = BIT(channel_idx); |
408 | |
409 | spin_lock(lock: &drvdata->spinlock); |
410 | reg_value = config->ctigate; |
411 | switch (op) { |
412 | case CTI_GATE_CHAN_ENABLE: |
413 | reg_value |= chan_bitmask; |
414 | break; |
415 | |
416 | case CTI_GATE_CHAN_DISABLE: |
417 | reg_value &= ~chan_bitmask; |
418 | break; |
419 | |
420 | default: |
421 | err = -EINVAL; |
422 | break; |
423 | } |
424 | if (err == 0) { |
425 | config->ctigate = reg_value; |
426 | if (cti_active(cfg: config)) |
427 | cti_write_single_reg(drvdata, CTIGATE, value: reg_value); |
428 | } |
429 | spin_unlock(lock: &drvdata->spinlock); |
430 | return err; |
431 | } |
432 | |
433 | int cti_channel_setop(struct device *dev, enum cti_chan_set_op op, |
434 | u32 channel_idx) |
435 | { |
436 | struct cti_drvdata *drvdata = dev_get_drvdata(dev: dev->parent); |
437 | struct cti_config *config = &drvdata->config; |
438 | u32 chan_bitmask; |
439 | u32 reg_value; |
440 | u32 reg_offset; |
441 | int err = 0; |
442 | |
443 | if (channel_idx >= config->nr_ctm_channels) |
444 | return -EINVAL; |
445 | |
446 | chan_bitmask = BIT(channel_idx); |
447 | |
448 | spin_lock(lock: &drvdata->spinlock); |
449 | reg_value = config->ctiappset; |
450 | switch (op) { |
451 | case CTI_CHAN_SET: |
452 | config->ctiappset |= chan_bitmask; |
453 | reg_value = config->ctiappset; |
454 | reg_offset = CTIAPPSET; |
455 | break; |
456 | |
457 | case CTI_CHAN_CLR: |
458 | config->ctiappset &= ~chan_bitmask; |
459 | reg_value = chan_bitmask; |
460 | reg_offset = CTIAPPCLEAR; |
461 | break; |
462 | |
463 | case CTI_CHAN_PULSE: |
464 | config->ctiappset &= ~chan_bitmask; |
465 | reg_value = chan_bitmask; |
466 | reg_offset = CTIAPPPULSE; |
467 | break; |
468 | |
469 | default: |
470 | err = -EINVAL; |
471 | break; |
472 | } |
473 | |
474 | if ((err == 0) && cti_active(cfg: config)) |
475 | cti_write_single_reg(drvdata, offset: reg_offset, value: reg_value); |
476 | spin_unlock(lock: &drvdata->spinlock); |
477 | |
478 | return err; |
479 | } |
480 | |
481 | static bool cti_add_sysfs_link(struct cti_drvdata *drvdata, |
482 | struct cti_trig_con *tc) |
483 | { |
484 | struct coresight_sysfs_link link_info; |
485 | int link_err = 0; |
486 | |
487 | link_info.orig = drvdata->csdev; |
488 | link_info.orig_name = tc->con_dev_name; |
489 | link_info.target = tc->con_dev; |
490 | link_info.target_name = dev_name(dev: &drvdata->csdev->dev); |
491 | |
492 | link_err = coresight_add_sysfs_link(info: &link_info); |
493 | if (link_err) |
494 | dev_warn(&drvdata->csdev->dev, |
495 | "Failed to set CTI sysfs link %s<=>%s\n" , |
496 | link_info.orig_name, link_info.target_name); |
497 | return !link_err; |
498 | } |
499 | |
500 | static void cti_remove_sysfs_link(struct cti_drvdata *drvdata, |
501 | struct cti_trig_con *tc) |
502 | { |
503 | struct coresight_sysfs_link link_info; |
504 | |
505 | link_info.orig = drvdata->csdev; |
506 | link_info.orig_name = tc->con_dev_name; |
507 | link_info.target = tc->con_dev; |
508 | link_info.target_name = dev_name(dev: &drvdata->csdev->dev); |
509 | coresight_remove_sysfs_link(info: &link_info); |
510 | } |
511 | |
512 | /* |
513 | * Look for a matching connection device name in the list of connections. |
514 | * If found then swap in the csdev name, set trig con association pointer |
515 | * and return found. |
516 | */ |
517 | static bool |
518 | cti_match_fixup_csdev(struct cti_device *ctidev, const char *node_name, |
519 | struct coresight_device *csdev) |
520 | { |
521 | struct cti_trig_con *tc; |
522 | struct cti_drvdata *drvdata = container_of(ctidev, struct cti_drvdata, |
523 | ctidev); |
524 | |
525 | list_for_each_entry(tc, &ctidev->trig_cons, node) { |
526 | if (tc->con_dev_name) { |
527 | if (!strcmp(node_name, tc->con_dev_name)) { |
528 | /* match: so swap in csdev name & dev */ |
529 | tc->con_dev_name = dev_name(dev: &csdev->dev); |
530 | tc->con_dev = csdev; |
531 | /* try to set sysfs link */ |
532 | if (cti_add_sysfs_link(drvdata, tc)) |
533 | return true; |
534 | /* link failed - remove CTI reference */ |
535 | tc->con_dev = NULL; |
536 | break; |
537 | } |
538 | } |
539 | } |
540 | return false; |
541 | } |
542 | |
543 | /* |
544 | * Search the cti list to add an associated CTI into the supplied CS device |
545 | * This will set the association if CTI declared before the CS device. |
546 | * (called from coresight_register() without coresight_mutex locked). |
547 | */ |
548 | static void cti_add_assoc_to_csdev(struct coresight_device *csdev) |
549 | { |
550 | struct cti_drvdata *ect_item; |
551 | struct cti_device *ctidev; |
552 | const char *node_name = NULL; |
553 | |
554 | /* protect the list */ |
555 | mutex_lock(&ect_mutex); |
556 | |
557 | /* exit if current is an ECT device.*/ |
558 | if ((csdev->type == CORESIGHT_DEV_TYPE_HELPER && |
559 | csdev->subtype.helper_subtype == |
560 | CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI) || |
561 | list_empty(head: &ect_net)) |
562 | goto cti_add_done; |
563 | |
564 | /* if we didn't find the csdev previously we used the fwnode name */ |
565 | node_name = cti_plat_get_node_name(dev_fwnode(csdev->dev.parent)); |
566 | if (!node_name) |
567 | goto cti_add_done; |
568 | |
569 | /* for each CTI in list... */ |
570 | list_for_each_entry(ect_item, &ect_net, node) { |
571 | ctidev = &ect_item->ctidev; |
572 | if (cti_match_fixup_csdev(ctidev, node_name, csdev)) { |
573 | /* |
574 | * if we found a matching csdev then update the ECT |
575 | * association pointer for the device with this CTI. |
576 | */ |
577 | coresight_add_helper(csdev, helper: ect_item->csdev); |
578 | break; |
579 | } |
580 | } |
581 | cti_add_done: |
582 | mutex_unlock(lock: &ect_mutex); |
583 | } |
584 | |
585 | /* |
586 | * Removing the associated devices is easier. |
587 | */ |
588 | static void cti_remove_assoc_from_csdev(struct coresight_device *csdev) |
589 | { |
590 | struct cti_drvdata *ctidrv; |
591 | struct cti_trig_con *tc; |
592 | struct cti_device *ctidev; |
593 | union coresight_dev_subtype cti_subtype = { |
594 | .helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI |
595 | }; |
596 | struct coresight_device *cti_csdev = coresight_find_output_type( |
597 | pdata: csdev->pdata, type: CORESIGHT_DEV_TYPE_HELPER, subtype: cti_subtype); |
598 | |
599 | if (!cti_csdev) |
600 | return; |
601 | |
602 | mutex_lock(&ect_mutex); |
603 | ctidrv = csdev_to_cti_drvdata(cti_csdev); |
604 | ctidev = &ctidrv->ctidev; |
605 | list_for_each_entry(tc, &ctidev->trig_cons, node) { |
606 | if (tc->con_dev == csdev) { |
607 | cti_remove_sysfs_link(drvdata: ctidrv, tc); |
608 | tc->con_dev = NULL; |
609 | break; |
610 | } |
611 | } |
612 | mutex_unlock(lock: &ect_mutex); |
613 | } |
614 | |
615 | /* |
616 | * Operations to add and remove associated CTI. |
617 | * Register to coresight core driver as call back function. |
618 | */ |
619 | static struct cti_assoc_op cti_assoc_ops = { |
620 | .add = cti_add_assoc_to_csdev, |
621 | .remove = cti_remove_assoc_from_csdev |
622 | }; |
623 | |
624 | /* |
625 | * Update the cross references where the associated device was found |
626 | * while we were building the connection info. This will occur if the |
627 | * assoc device was registered before the CTI. |
628 | */ |
629 | static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) |
630 | { |
631 | struct cti_trig_con *tc; |
632 | struct cti_device *ctidev = &drvdata->ctidev; |
633 | |
634 | list_for_each_entry(tc, &ctidev->trig_cons, node) { |
635 | if (tc->con_dev) { |
636 | /* if we can set the sysfs link */ |
637 | if (cti_add_sysfs_link(drvdata, tc)) |
638 | /* set the CTI/csdev association */ |
639 | coresight_add_helper(csdev: tc->con_dev, |
640 | helper: drvdata->csdev); |
641 | else |
642 | /* otherwise remove reference from CTI */ |
643 | tc->con_dev = NULL; |
644 | } |
645 | } |
646 | } |
647 | |
648 | static void cti_remove_conn_xrefs(struct cti_drvdata *drvdata) |
649 | { |
650 | struct cti_trig_con *tc; |
651 | struct cti_device *ctidev = &drvdata->ctidev; |
652 | |
653 | list_for_each_entry(tc, &ctidev->trig_cons, node) { |
654 | if (tc->con_dev) { |
655 | cti_remove_sysfs_link(drvdata, tc); |
656 | tc->con_dev = NULL; |
657 | } |
658 | } |
659 | } |
660 | |
661 | /** cti PM callbacks **/ |
662 | static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, |
663 | void *v) |
664 | { |
665 | struct cti_drvdata *drvdata; |
666 | struct coresight_device *csdev; |
667 | unsigned int cpu = smp_processor_id(); |
668 | int notify_res = NOTIFY_OK; |
669 | |
670 | if (!cti_cpu_drvdata[cpu]) |
671 | return NOTIFY_OK; |
672 | |
673 | drvdata = cti_cpu_drvdata[cpu]; |
674 | csdev = drvdata->csdev; |
675 | |
676 | if (WARN_ON_ONCE(drvdata->ctidev.cpu != cpu)) |
677 | return NOTIFY_BAD; |
678 | |
679 | spin_lock(lock: &drvdata->spinlock); |
680 | |
681 | switch (cmd) { |
682 | case CPU_PM_ENTER: |
683 | /* CTI regs all static - we have a copy & nothing to save */ |
684 | drvdata->config.hw_powered = false; |
685 | if (drvdata->config.hw_enabled) |
686 | coresight_disclaim_device(csdev); |
687 | break; |
688 | |
689 | case CPU_PM_ENTER_FAILED: |
690 | drvdata->config.hw_powered = true; |
691 | if (drvdata->config.hw_enabled) { |
692 | if (coresight_claim_device(csdev)) |
693 | drvdata->config.hw_enabled = false; |
694 | } |
695 | break; |
696 | |
697 | case CPU_PM_EXIT: |
698 | /* write hardware registers to re-enable. */ |
699 | drvdata->config.hw_powered = true; |
700 | drvdata->config.hw_enabled = false; |
701 | |
702 | /* check enable reference count to enable HW */ |
703 | if (drvdata->config.enable_req_count) { |
704 | /* check we can claim the device as we re-power */ |
705 | if (coresight_claim_device(csdev)) |
706 | goto cti_notify_exit; |
707 | |
708 | drvdata->config.hw_enabled = true; |
709 | cti_write_all_hw_regs(drvdata); |
710 | } |
711 | break; |
712 | |
713 | default: |
714 | notify_res = NOTIFY_DONE; |
715 | break; |
716 | } |
717 | |
718 | cti_notify_exit: |
719 | spin_unlock(lock: &drvdata->spinlock); |
720 | return notify_res; |
721 | } |
722 | |
723 | static struct notifier_block cti_cpu_pm_nb = { |
724 | .notifier_call = cti_cpu_pm_notify, |
725 | }; |
726 | |
727 | /* CPU HP handlers */ |
728 | static int cti_starting_cpu(unsigned int cpu) |
729 | { |
730 | struct cti_drvdata *drvdata = cti_cpu_drvdata[cpu]; |
731 | |
732 | if (!drvdata) |
733 | return 0; |
734 | |
735 | cti_cpuhp_enable_hw(drvdata); |
736 | return 0; |
737 | } |
738 | |
739 | static int cti_dying_cpu(unsigned int cpu) |
740 | { |
741 | struct cti_drvdata *drvdata = cti_cpu_drvdata[cpu]; |
742 | |
743 | if (!drvdata) |
744 | return 0; |
745 | |
746 | spin_lock(lock: &drvdata->spinlock); |
747 | drvdata->config.hw_powered = false; |
748 | if (drvdata->config.hw_enabled) |
749 | coresight_disclaim_device(csdev: drvdata->csdev); |
750 | spin_unlock(lock: &drvdata->spinlock); |
751 | return 0; |
752 | } |
753 | |
754 | static int cti_pm_setup(struct cti_drvdata *drvdata) |
755 | { |
756 | int ret; |
757 | |
758 | if (drvdata->ctidev.cpu == -1) |
759 | return 0; |
760 | |
761 | if (nr_cti_cpu) |
762 | goto done; |
763 | |
764 | cpus_read_lock(); |
765 | ret = cpuhp_setup_state_nocalls_cpuslocked( |
766 | state: CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, |
767 | name: "arm/coresight_cti:starting" , |
768 | startup: cti_starting_cpu, teardown: cti_dying_cpu); |
769 | if (ret) { |
770 | cpus_read_unlock(); |
771 | return ret; |
772 | } |
773 | |
774 | ret = cpu_pm_register_notifier(nb: &cti_cpu_pm_nb); |
775 | cpus_read_unlock(); |
776 | if (ret) { |
777 | cpuhp_remove_state_nocalls(state: CPUHP_AP_ARM_CORESIGHT_CTI_STARTING); |
778 | return ret; |
779 | } |
780 | |
781 | done: |
782 | nr_cti_cpu++; |
783 | cti_cpu_drvdata[drvdata->ctidev.cpu] = drvdata; |
784 | |
785 | return 0; |
786 | } |
787 | |
788 | /* release PM registrations */ |
789 | static void cti_pm_release(struct cti_drvdata *drvdata) |
790 | { |
791 | if (drvdata->ctidev.cpu == -1) |
792 | return; |
793 | |
794 | cti_cpu_drvdata[drvdata->ctidev.cpu] = NULL; |
795 | if (--nr_cti_cpu == 0) { |
796 | cpu_pm_unregister_notifier(nb: &cti_cpu_pm_nb); |
797 | cpuhp_remove_state_nocalls(state: CPUHP_AP_ARM_CORESIGHT_CTI_STARTING); |
798 | } |
799 | } |
800 | |
801 | /** cti ect operations **/ |
802 | int cti_enable(struct coresight_device *csdev, enum cs_mode mode, void *data) |
803 | { |
804 | struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); |
805 | |
806 | return cti_enable_hw(drvdata); |
807 | } |
808 | |
809 | int cti_disable(struct coresight_device *csdev, void *data) |
810 | { |
811 | struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); |
812 | |
813 | return cti_disable_hw(drvdata); |
814 | } |
815 | |
816 | static const struct coresight_ops_helper cti_ops_ect = { |
817 | .enable = cti_enable, |
818 | .disable = cti_disable, |
819 | }; |
820 | |
821 | static const struct coresight_ops cti_ops = { |
822 | .helper_ops = &cti_ops_ect, |
823 | }; |
824 | |
825 | /* |
826 | * Free up CTI specific resources |
827 | * called by dev->release, need to call down to underlying csdev release. |
828 | */ |
829 | static void cti_device_release(struct device *dev) |
830 | { |
831 | struct cti_drvdata *drvdata = dev_get_drvdata(dev: dev->parent); |
832 | struct cti_drvdata *ect_item, *ect_tmp; |
833 | |
834 | mutex_lock(&ect_mutex); |
835 | cti_pm_release(drvdata); |
836 | |
837 | /* remove from the list */ |
838 | list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, node) { |
839 | if (ect_item == drvdata) { |
840 | list_del(entry: &ect_item->node); |
841 | break; |
842 | } |
843 | } |
844 | mutex_unlock(lock: &ect_mutex); |
845 | |
846 | if (drvdata->csdev_release) |
847 | drvdata->csdev_release(dev); |
848 | } |
849 | static void cti_remove(struct amba_device *adev) |
850 | { |
851 | struct cti_drvdata *drvdata = dev_get_drvdata(dev: &adev->dev); |
852 | |
853 | mutex_lock(&ect_mutex); |
854 | cti_remove_conn_xrefs(drvdata); |
855 | mutex_unlock(lock: &ect_mutex); |
856 | |
857 | coresight_unregister(csdev: drvdata->csdev); |
858 | } |
859 | |
860 | static int cti_probe(struct amba_device *adev, const struct amba_id *id) |
861 | { |
862 | int ret = 0; |
863 | void __iomem *base; |
864 | struct device *dev = &adev->dev; |
865 | struct cti_drvdata *drvdata = NULL; |
866 | struct coresight_desc cti_desc; |
867 | struct coresight_platform_data *pdata = NULL; |
868 | struct resource *res = &adev->res; |
869 | |
870 | /* driver data*/ |
871 | drvdata = devm_kzalloc(dev, size: sizeof(*drvdata), GFP_KERNEL); |
872 | if (!drvdata) |
873 | return -ENOMEM; |
874 | |
875 | /* Validity for the resource is already checked by the AMBA core */ |
876 | base = devm_ioremap_resource(dev, res); |
877 | if (IS_ERR(ptr: base)) |
878 | return PTR_ERR(ptr: base); |
879 | |
880 | drvdata->base = base; |
881 | cti_desc.access = CSDEV_ACCESS_IOMEM(base); |
882 | |
883 | dev_set_drvdata(dev, data: drvdata); |
884 | |
885 | /* default CTI device info */ |
886 | drvdata->ctidev.cpu = -1; |
887 | drvdata->ctidev.nr_trig_con = 0; |
888 | drvdata->ctidev.ctm_id = 0; |
889 | INIT_LIST_HEAD(list: &drvdata->ctidev.trig_cons); |
890 | |
891 | spin_lock_init(&drvdata->spinlock); |
892 | |
893 | /* initialise CTI driver config values */ |
894 | cti_set_default_config(dev, drvdata); |
895 | |
896 | pdata = coresight_cti_get_platform_data(dev); |
897 | if (IS_ERR(ptr: pdata)) { |
898 | dev_err(dev, "coresight_cti_get_platform_data err\n" ); |
899 | return PTR_ERR(ptr: pdata); |
900 | } |
901 | |
902 | /* default to powered - could change on PM notifications */ |
903 | drvdata->config.hw_powered = true; |
904 | |
905 | /* set up device name - will depend if cpu bound or otherwise */ |
906 | if (drvdata->ctidev.cpu >= 0) |
907 | cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, fmt: "cti_cpu%d" , |
908 | drvdata->ctidev.cpu); |
909 | else |
910 | cti_desc.name = coresight_alloc_device_name(devs: &cti_sys_devs, dev); |
911 | if (!cti_desc.name) |
912 | return -ENOMEM; |
913 | |
914 | /* setup CPU power management handling for CPU bound CTI devices. */ |
915 | ret = cti_pm_setup(drvdata); |
916 | if (ret) |
917 | return ret; |
918 | |
919 | /* create dynamic attributes for connections */ |
920 | ret = cti_create_cons_sysfs(dev, drvdata); |
921 | if (ret) { |
922 | dev_err(dev, "%s: create dynamic sysfs entries failed\n" , |
923 | cti_desc.name); |
924 | goto pm_release; |
925 | } |
926 | |
927 | /* set up coresight component description */ |
928 | cti_desc.pdata = pdata; |
929 | cti_desc.type = CORESIGHT_DEV_TYPE_HELPER; |
930 | cti_desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI; |
931 | cti_desc.ops = &cti_ops; |
932 | cti_desc.groups = drvdata->ctidev.con_groups; |
933 | cti_desc.dev = dev; |
934 | drvdata->csdev = coresight_register(desc: &cti_desc); |
935 | if (IS_ERR(ptr: drvdata->csdev)) { |
936 | ret = PTR_ERR(ptr: drvdata->csdev); |
937 | goto pm_release; |
938 | } |
939 | |
940 | /* add to list of CTI devices */ |
941 | mutex_lock(&ect_mutex); |
942 | list_add(new: &drvdata->node, head: &ect_net); |
943 | /* set any cross references */ |
944 | cti_update_conn_xrefs(drvdata); |
945 | mutex_unlock(lock: &ect_mutex); |
946 | |
947 | /* set up release chain */ |
948 | drvdata->csdev_release = drvdata->csdev->dev.release; |
949 | drvdata->csdev->dev.release = cti_device_release; |
950 | |
951 | /* all done - dec pm refcount */ |
952 | pm_runtime_put(dev: &adev->dev); |
953 | dev_info(&drvdata->csdev->dev, "CTI initialized\n" ); |
954 | return 0; |
955 | |
956 | pm_release: |
957 | cti_pm_release(drvdata); |
958 | return ret; |
959 | } |
960 | |
961 | static struct amba_cs_uci_id uci_id_cti[] = { |
962 | { |
963 | /* CTI UCI data */ |
964 | .devarch = 0x47701a14, /* CTI v2 */ |
965 | .devarch_mask = 0xfff0ffff, |
966 | .devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */ |
967 | } |
968 | }; |
969 | |
970 | static const struct amba_id cti_ids[] = { |
971 | CS_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */ |
972 | CS_AMBA_ID(0x000bb922), /* CTI - C-A8 */ |
973 | CS_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */ |
974 | CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */ |
975 | CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */ |
976 | CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */ |
977 | { 0, 0, NULL }, |
978 | }; |
979 | |
980 | MODULE_DEVICE_TABLE(amba, cti_ids); |
981 | |
982 | static struct amba_driver cti_driver = { |
983 | .drv = { |
984 | .name = "coresight-cti" , |
985 | .owner = THIS_MODULE, |
986 | .suppress_bind_attrs = true, |
987 | }, |
988 | .probe = cti_probe, |
989 | .remove = cti_remove, |
990 | .id_table = cti_ids, |
991 | }; |
992 | |
993 | static int __init cti_init(void) |
994 | { |
995 | int ret; |
996 | |
997 | ret = amba_driver_register(drv: &cti_driver); |
998 | if (ret) |
999 | pr_info("Error registering cti driver\n" ); |
1000 | coresight_set_cti_ops(cti_op: &cti_assoc_ops); |
1001 | return ret; |
1002 | } |
1003 | |
1004 | static void __exit cti_exit(void) |
1005 | { |
1006 | coresight_remove_cti_ops(); |
1007 | amba_driver_unregister(drv: &cti_driver); |
1008 | } |
1009 | |
1010 | module_init(cti_init); |
1011 | module_exit(cti_exit); |
1012 | |
1013 | MODULE_AUTHOR("Mike Leach <mike.leach@linaro.org>" ); |
1014 | MODULE_DESCRIPTION("Arm CoreSight CTI Driver" ); |
1015 | MODULE_LICENSE("GPL v2" ); |
1016 | |