1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2019, The Linaro Limited. All rights reserved. |
4 | */ |
5 | #include <linux/coresight.h> |
6 | #include <linux/device.h> |
7 | #include <linux/err.h> |
8 | #include <linux/of.h> |
9 | #include <linux/property.h> |
10 | #include <linux/slab.h> |
11 | |
12 | #include <dt-bindings/arm/coresight-cti-dt.h> |
13 | |
14 | #include "coresight-cti.h" |
15 | #include "coresight-priv.h" |
16 | |
17 | /* Number of CTI signals in the v8 architecturally defined connection */ |
18 | #define NR_V8PE_IN_SIGS 2 |
19 | #define NR_V8PE_OUT_SIGS 3 |
20 | #define NR_V8ETM_INOUT_SIGS 4 |
21 | |
22 | /* CTI device tree trigger connection node keyword */ |
23 | #define CTI_DT_CONNS "trig-conns" |
24 | |
25 | /* CTI device tree connection property keywords */ |
26 | #define CTI_DT_V8ARCH_COMPAT "arm,coresight-cti-v8-arch" |
27 | #define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" |
28 | #define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" |
29 | #define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" |
30 | #define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" |
31 | #define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" |
32 | #define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" |
33 | #define CTI_DT_CONN_NAME "arm,trig-conn-name" |
34 | #define CTI_DT_CTM_ID "arm,cti-ctm-id" |
35 | |
36 | #ifdef CONFIG_OF |
37 | /* |
38 | * CTI can be bound to a CPU, or a system device. |
39 | * CPU can be declared at the device top level or in a connections node |
40 | * so need to check relative to node not device. |
41 | */ |
42 | static int of_cti_get_cpu_at_node(const struct device_node *node) |
43 | { |
44 | int cpu; |
45 | struct device_node *dn; |
46 | |
47 | if (node == NULL) |
48 | return -1; |
49 | |
50 | dn = of_parse_phandle(np: node, phandle_name: "cpu" , index: 0); |
51 | /* CTI affinity defaults to no cpu */ |
52 | if (!dn) |
53 | return -1; |
54 | cpu = of_cpu_node_to_id(np: dn); |
55 | of_node_put(node: dn); |
56 | |
57 | /* No Affinity if no cpu nodes are found */ |
58 | return (cpu < 0) ? -1 : cpu; |
59 | } |
60 | |
61 | #else |
62 | static int of_cti_get_cpu_at_node(const struct device_node *node) |
63 | { |
64 | return -1; |
65 | } |
66 | |
67 | #endif |
68 | |
69 | /* |
70 | * CTI can be bound to a CPU, or a system device. |
71 | * CPU can be declared at the device top level or in a connections node |
72 | * so need to check relative to node not device. |
73 | */ |
74 | static int cti_plat_get_cpu_at_node(struct fwnode_handle *fwnode) |
75 | { |
76 | if (is_of_node(fwnode)) |
77 | return of_cti_get_cpu_at_node(to_of_node(fwnode)); |
78 | return -1; |
79 | } |
80 | |
81 | const char *cti_plat_get_node_name(struct fwnode_handle *fwnode) |
82 | { |
83 | if (is_of_node(fwnode)) |
84 | return of_node_full_name(to_of_node(fwnode)); |
85 | return "unknown" ; |
86 | } |
87 | |
88 | /* |
89 | * Extract a name from the fwnode. |
90 | * If the device associated with the node is a coresight_device, then return |
91 | * that name and the coresight_device pointer, otherwise return the node name. |
92 | */ |
93 | static const char * |
94 | cti_plat_get_csdev_or_node_name(struct fwnode_handle *fwnode, |
95 | struct coresight_device **csdev) |
96 | { |
97 | const char *name = NULL; |
98 | *csdev = coresight_find_csdev_by_fwnode(r_fwnode: fwnode); |
99 | if (*csdev) |
100 | name = dev_name(dev: &(*csdev)->dev); |
101 | else |
102 | name = cti_plat_get_node_name(fwnode); |
103 | return name; |
104 | } |
105 | |
106 | static bool cti_plat_node_name_eq(struct fwnode_handle *fwnode, |
107 | const char *name) |
108 | { |
109 | if (is_of_node(fwnode)) |
110 | return of_node_name_eq(to_of_node(fwnode), name); |
111 | return false; |
112 | } |
113 | |
114 | static int cti_plat_create_v8_etm_connection(struct device *dev, |
115 | struct cti_drvdata *drvdata) |
116 | { |
117 | int ret = -ENOMEM, i; |
118 | struct fwnode_handle *root_fwnode, *cs_fwnode; |
119 | const char *assoc_name = NULL; |
120 | struct coresight_device *csdev; |
121 | struct cti_trig_con *tc = NULL; |
122 | |
123 | root_fwnode = dev_fwnode(dev); |
124 | if (IS_ERR_OR_NULL(ptr: root_fwnode)) |
125 | return -EINVAL; |
126 | |
127 | /* Can optionally have an etm node - return if not */ |
128 | cs_fwnode = fwnode_find_reference(fwnode: root_fwnode, CTI_DT_CSDEV_ASSOC, index: 0); |
129 | if (IS_ERR(ptr: cs_fwnode)) |
130 | return 0; |
131 | |
132 | /* allocate memory */ |
133 | tc = cti_allocate_trig_con(dev, NR_V8ETM_INOUT_SIGS, |
134 | NR_V8ETM_INOUT_SIGS); |
135 | if (!tc) |
136 | goto create_v8_etm_out; |
137 | |
138 | /* build connection data */ |
139 | tc->con_in->used_mask = 0xF0; /* sigs <4,5,6,7> */ |
140 | tc->con_out->used_mask = 0xF0; /* sigs <4,5,6,7> */ |
141 | |
142 | /* |
143 | * The EXTOUT type signals from the ETM are connected to a set of input |
144 | * triggers on the CTI, the EXTIN being connected to output triggers. |
145 | */ |
146 | for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) { |
147 | tc->con_in->sig_types[i] = ETM_EXTOUT; |
148 | tc->con_out->sig_types[i] = ETM_EXTIN; |
149 | } |
150 | |
151 | /* |
152 | * We look to see if the ETM coresight device associated with this |
153 | * handle has been registered with the system - i.e. probed before |
154 | * this CTI. If so csdev will be non NULL and we can use the device |
155 | * name and pass the csdev to the connection entry function where |
156 | * the association will be recorded. |
157 | * If not, then simply record the name in the connection data, the |
158 | * probing of the ETM will call into the CTI driver API to update the |
159 | * association then. |
160 | */ |
161 | assoc_name = cti_plat_get_csdev_or_node_name(fwnode: cs_fwnode, csdev: &csdev); |
162 | ret = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_dev_name: assoc_name); |
163 | |
164 | create_v8_etm_out: |
165 | fwnode_handle_put(fwnode: cs_fwnode); |
166 | return ret; |
167 | } |
168 | |
169 | /* |
170 | * Create an architecturally defined v8 connection |
171 | * must have a cpu, can have an ETM. |
172 | */ |
173 | static int cti_plat_create_v8_connections(struct device *dev, |
174 | struct cti_drvdata *drvdata) |
175 | { |
176 | struct cti_device *cti_dev = &drvdata->ctidev; |
177 | struct cti_trig_con *tc = NULL; |
178 | int cpuid = 0; |
179 | char cpu_name_str[16]; |
180 | int ret = -ENOMEM; |
181 | |
182 | /* Must have a cpu node */ |
183 | cpuid = cti_plat_get_cpu_at_node(dev_fwnode(dev)); |
184 | if (cpuid < 0) { |
185 | dev_warn(dev, |
186 | "ARM v8 architectural CTI connection: missing cpu\n" ); |
187 | return -EINVAL; |
188 | } |
189 | cti_dev->cpu = cpuid; |
190 | |
191 | /* Allocate the v8 cpu connection memory */ |
192 | tc = cti_allocate_trig_con(dev, NR_V8PE_IN_SIGS, NR_V8PE_OUT_SIGS); |
193 | if (!tc) |
194 | goto of_create_v8_out; |
195 | |
196 | /* Set the v8 PE CTI connection data */ |
197 | tc->con_in->used_mask = 0x3; /* sigs <0 1> */ |
198 | tc->con_in->sig_types[0] = PE_DBGTRIGGER; |
199 | tc->con_in->sig_types[1] = PE_PMUIRQ; |
200 | tc->con_out->used_mask = 0x7; /* sigs <0 1 2 > */ |
201 | tc->con_out->sig_types[0] = PE_EDBGREQ; |
202 | tc->con_out->sig_types[1] = PE_DBGRESTART; |
203 | tc->con_out->sig_types[2] = PE_CTIIRQ; |
204 | scnprintf(buf: cpu_name_str, size: sizeof(cpu_name_str), fmt: "cpu%d" , cpuid); |
205 | |
206 | ret = cti_add_connection_entry(dev, drvdata, tc, NULL, assoc_dev_name: cpu_name_str); |
207 | if (ret) |
208 | goto of_create_v8_out; |
209 | |
210 | /* Create the v8 ETM associated connection */ |
211 | ret = cti_plat_create_v8_etm_connection(dev, drvdata); |
212 | if (ret) |
213 | goto of_create_v8_out; |
214 | |
215 | /* filter pe_edbgreq - PE trigout sig <0> */ |
216 | drvdata->config.trig_out_filter |= 0x1; |
217 | |
218 | of_create_v8_out: |
219 | return ret; |
220 | } |
221 | |
222 | static int cti_plat_check_v8_arch_compatible(struct device *dev) |
223 | { |
224 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
225 | |
226 | if (is_of_node(fwnode)) |
227 | return of_device_is_compatible(to_of_node(fwnode), |
228 | CTI_DT_V8ARCH_COMPAT); |
229 | return 0; |
230 | } |
231 | |
232 | static int cti_plat_count_sig_elements(const struct fwnode_handle *fwnode, |
233 | const char *name) |
234 | { |
235 | int nr_elem = fwnode_property_count_u32(fwnode, propname: name); |
236 | |
237 | return (nr_elem < 0 ? 0 : nr_elem); |
238 | } |
239 | |
240 | static int cti_plat_read_trig_group(struct cti_trig_grp *tgrp, |
241 | const struct fwnode_handle *fwnode, |
242 | const char *grp_name) |
243 | { |
244 | int idx, err = 0; |
245 | u32 *values; |
246 | |
247 | if (!tgrp->nr_sigs) |
248 | return 0; |
249 | |
250 | values = kcalloc(n: tgrp->nr_sigs, size: sizeof(u32), GFP_KERNEL); |
251 | if (!values) |
252 | return -ENOMEM; |
253 | |
254 | err = fwnode_property_read_u32_array(fwnode, propname: grp_name, |
255 | val: values, nval: tgrp->nr_sigs); |
256 | |
257 | if (!err) { |
258 | /* set the signal usage mask */ |
259 | for (idx = 0; idx < tgrp->nr_sigs; idx++) |
260 | tgrp->used_mask |= BIT(values[idx]); |
261 | } |
262 | |
263 | kfree(objp: values); |
264 | return err; |
265 | } |
266 | |
267 | static int cti_plat_read_trig_types(struct cti_trig_grp *tgrp, |
268 | const struct fwnode_handle *fwnode, |
269 | const char *type_name) |
270 | { |
271 | int items, err = 0, nr_sigs; |
272 | u32 *values = NULL, i; |
273 | |
274 | /* allocate an array according to number of signals in connection */ |
275 | nr_sigs = tgrp->nr_sigs; |
276 | if (!nr_sigs) |
277 | return 0; |
278 | |
279 | /* see if any types have been included in the device description */ |
280 | items = cti_plat_count_sig_elements(fwnode, name: type_name); |
281 | if (items > nr_sigs) |
282 | return -EINVAL; |
283 | |
284 | /* need an array to store the values iff there are any */ |
285 | if (items) { |
286 | values = kcalloc(n: items, size: sizeof(u32), GFP_KERNEL); |
287 | if (!values) |
288 | return -ENOMEM; |
289 | |
290 | err = fwnode_property_read_u32_array(fwnode, propname: type_name, |
291 | val: values, nval: items); |
292 | if (err) |
293 | goto read_trig_types_out; |
294 | } |
295 | |
296 | /* |
297 | * Match type id to signal index, 1st type to 1st index etc. |
298 | * If fewer types than signals default remainder to GEN_IO. |
299 | */ |
300 | for (i = 0; i < nr_sigs; i++) { |
301 | if (i < items) { |
302 | tgrp->sig_types[i] = |
303 | values[i] < CTI_TRIG_MAX ? values[i] : GEN_IO; |
304 | } else { |
305 | tgrp->sig_types[i] = GEN_IO; |
306 | } |
307 | } |
308 | |
309 | read_trig_types_out: |
310 | kfree(objp: values); |
311 | return err; |
312 | } |
313 | |
314 | static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata, |
315 | const struct fwnode_handle *fwnode) |
316 | { |
317 | struct cti_trig_grp *tg = NULL; |
318 | int err = 0, nr_filter_sigs; |
319 | |
320 | nr_filter_sigs = cti_plat_count_sig_elements(fwnode, |
321 | CTI_DT_FILTER_OUT_SIGS); |
322 | if (nr_filter_sigs == 0) |
323 | return 0; |
324 | |
325 | if (nr_filter_sigs > drvdata->config.nr_trig_max) |
326 | return -EINVAL; |
327 | |
328 | tg = kzalloc(size: sizeof(*tg), GFP_KERNEL); |
329 | if (!tg) |
330 | return -ENOMEM; |
331 | |
332 | err = cti_plat_read_trig_group(tgrp: tg, fwnode, CTI_DT_FILTER_OUT_SIGS); |
333 | if (!err) |
334 | drvdata->config.trig_out_filter |= tg->used_mask; |
335 | |
336 | kfree(objp: tg); |
337 | return err; |
338 | } |
339 | |
340 | static int cti_plat_create_connection(struct device *dev, |
341 | struct cti_drvdata *drvdata, |
342 | struct fwnode_handle *fwnode) |
343 | { |
344 | struct cti_trig_con *tc = NULL; |
345 | int cpuid = -1, err = 0; |
346 | struct coresight_device *csdev = NULL; |
347 | const char *assoc_name = "unknown" ; |
348 | char cpu_name_str[16]; |
349 | int nr_sigs_in, nr_sigs_out; |
350 | |
351 | /* look to see how many in and out signals we have */ |
352 | nr_sigs_in = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGIN_SIGS); |
353 | nr_sigs_out = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGOUT_SIGS); |
354 | |
355 | if ((nr_sigs_in > drvdata->config.nr_trig_max) || |
356 | (nr_sigs_out > drvdata->config.nr_trig_max)) |
357 | return -EINVAL; |
358 | |
359 | tc = cti_allocate_trig_con(dev, in_sigs: nr_sigs_in, out_sigs: nr_sigs_out); |
360 | if (!tc) |
361 | return -ENOMEM; |
362 | |
363 | /* look for the signals properties. */ |
364 | err = cti_plat_read_trig_group(tgrp: tc->con_in, fwnode, |
365 | CTI_DT_TRIGIN_SIGS); |
366 | if (err) |
367 | goto create_con_err; |
368 | |
369 | err = cti_plat_read_trig_types(tgrp: tc->con_in, fwnode, |
370 | CTI_DT_TRIGIN_TYPES); |
371 | if (err) |
372 | goto create_con_err; |
373 | |
374 | err = cti_plat_read_trig_group(tgrp: tc->con_out, fwnode, |
375 | CTI_DT_TRIGOUT_SIGS); |
376 | if (err) |
377 | goto create_con_err; |
378 | |
379 | err = cti_plat_read_trig_types(tgrp: tc->con_out, fwnode, |
380 | CTI_DT_TRIGOUT_TYPES); |
381 | if (err) |
382 | goto create_con_err; |
383 | |
384 | err = cti_plat_process_filter_sigs(drvdata, fwnode); |
385 | if (err) |
386 | goto create_con_err; |
387 | |
388 | /* read the connection name if set - may be overridden by later */ |
389 | fwnode_property_read_string(fwnode, CTI_DT_CONN_NAME, val: &assoc_name); |
390 | |
391 | /* associated cpu ? */ |
392 | cpuid = cti_plat_get_cpu_at_node(fwnode); |
393 | if (cpuid >= 0) { |
394 | drvdata->ctidev.cpu = cpuid; |
395 | scnprintf(buf: cpu_name_str, size: sizeof(cpu_name_str), fmt: "cpu%d" , cpuid); |
396 | assoc_name = cpu_name_str; |
397 | } else { |
398 | /* associated device ? */ |
399 | struct fwnode_handle *cs_fwnode = fwnode_find_reference(fwnode, |
400 | CTI_DT_CSDEV_ASSOC, |
401 | index: 0); |
402 | if (!IS_ERR(ptr: cs_fwnode)) { |
403 | assoc_name = cti_plat_get_csdev_or_node_name(fwnode: cs_fwnode, |
404 | csdev: &csdev); |
405 | fwnode_handle_put(fwnode: cs_fwnode); |
406 | } |
407 | } |
408 | /* set up a connection */ |
409 | err = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_dev_name: assoc_name); |
410 | |
411 | create_con_err: |
412 | return err; |
413 | } |
414 | |
415 | static int cti_plat_create_impdef_connections(struct device *dev, |
416 | struct cti_drvdata *drvdata) |
417 | { |
418 | int rc = 0; |
419 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
420 | struct fwnode_handle *child = NULL; |
421 | |
422 | if (IS_ERR_OR_NULL(ptr: fwnode)) |
423 | return -EINVAL; |
424 | |
425 | fwnode_for_each_child_node(fwnode, child) { |
426 | if (cti_plat_node_name_eq(fwnode: child, CTI_DT_CONNS)) |
427 | rc = cti_plat_create_connection(dev, drvdata, |
428 | fwnode: child); |
429 | if (rc != 0) |
430 | break; |
431 | } |
432 | fwnode_handle_put(fwnode: child); |
433 | |
434 | return rc; |
435 | } |
436 | |
437 | /* get the hardware configuration & connection data. */ |
438 | static int cti_plat_get_hw_data(struct device *dev, struct cti_drvdata *drvdata) |
439 | { |
440 | int rc = 0; |
441 | struct cti_device *cti_dev = &drvdata->ctidev; |
442 | |
443 | /* get any CTM ID - defaults to 0 */ |
444 | device_property_read_u32(dev, CTI_DT_CTM_ID, val: &cti_dev->ctm_id); |
445 | |
446 | /* check for a v8 architectural CTI device */ |
447 | if (cti_plat_check_v8_arch_compatible(dev)) |
448 | rc = cti_plat_create_v8_connections(dev, drvdata); |
449 | else |
450 | rc = cti_plat_create_impdef_connections(dev, drvdata); |
451 | if (rc) |
452 | return rc; |
453 | |
454 | /* if no connections, just add a single default based on max IN-OUT */ |
455 | if (cti_dev->nr_trig_con == 0) |
456 | rc = cti_add_default_connection(dev, drvdata); |
457 | return rc; |
458 | } |
459 | |
460 | struct coresight_platform_data * |
461 | coresight_cti_get_platform_data(struct device *dev) |
462 | { |
463 | int ret = -ENOENT; |
464 | struct coresight_platform_data *pdata = NULL; |
465 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
466 | struct cti_drvdata *drvdata = dev_get_drvdata(dev); |
467 | |
468 | if (IS_ERR_OR_NULL(ptr: fwnode)) |
469 | goto error; |
470 | |
471 | /* |
472 | * Alloc platform data but leave it zero init. CTI does not use the |
473 | * same connection infrastructuree as trace path components but an |
474 | * empty struct enables us to use the standard coresight component |
475 | * registration code. |
476 | */ |
477 | pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL); |
478 | if (!pdata) { |
479 | ret = -ENOMEM; |
480 | goto error; |
481 | } |
482 | |
483 | /* get some CTI specifics */ |
484 | ret = cti_plat_get_hw_data(dev, drvdata); |
485 | |
486 | if (!ret) |
487 | return pdata; |
488 | error: |
489 | return ERR_PTR(error: ret); |
490 | } |
491 | |