1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019, Linaro Limited, All rights reserved.
4 * Author: Mike Leach <mike.leach@linaro.org>
5 */
6
7#include <linux/device.h>
8#include <linux/idr.h>
9#include <linux/kernel.h>
10
11#include "coresight-priv.h"
12
13/*
14 * Use IDR to map the hash of the source's device name
15 * to the pointer of path for the source. The idr is for
16 * the sources which aren't associated with CPU.
17 */
18static DEFINE_IDR(path_idr);
19
20/*
21 * When operating Coresight drivers from the sysFS interface, only a single
22 * path can exist from a tracer (associated to a CPU) to a sink.
23 */
24static DEFINE_PER_CPU(struct list_head *, tracer_path);
25
26ssize_t coresight_simple_show_pair(struct device *_dev,
27 struct device_attribute *attr, char *buf)
28{
29 struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
30 struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);
31 u64 val;
32
33 pm_runtime_get_sync(dev: _dev->parent);
34 val = csdev_access_relaxed_read_pair(csa: &csdev->access, lo_offset: cs_attr->lo_off, hi_offset: cs_attr->hi_off);
35 pm_runtime_put_sync(dev: _dev->parent);
36 return sysfs_emit(buf, fmt: "0x%llx\n", val);
37}
38EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
39
40ssize_t coresight_simple_show32(struct device *_dev,
41 struct device_attribute *attr, char *buf)
42{
43 struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
44 struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);
45 u64 val;
46
47 pm_runtime_get_sync(dev: _dev->parent);
48 val = csdev_access_relaxed_read32(csa: &csdev->access, offset: cs_attr->off);
49 pm_runtime_put_sync(dev: _dev->parent);
50 return sysfs_emit(buf, fmt: "0x%llx\n", val);
51}
52EXPORT_SYMBOL_GPL(coresight_simple_show32);
53
54static int coresight_enable_source_sysfs(struct coresight_device *csdev,
55 enum cs_mode mode, void *data)
56{
57 int ret;
58
59 /*
60 * Comparison with CS_MODE_SYSFS works without taking any device
61 * specific spinlock because the truthyness of that comparison can only
62 * change with coresight_mutex held, which we already have here.
63 */
64 lockdep_assert_held(&coresight_mutex);
65 if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
66 ret = source_ops(csdev)->enable(csdev, data, mode);
67 if (ret)
68 return ret;
69 }
70
71 csdev->refcnt++;
72
73 return 0;
74}
75
76/**
77 * coresight_disable_source_sysfs - Drop the reference count by 1 and disable
78 * the device if there are no users left.
79 *
80 * @csdev: The coresight device to disable
81 * @data: Opaque data to pass on to the disable function of the source device.
82 * For example in perf mode this is a pointer to the struct perf_event.
83 *
84 * Returns true if the device has been disabled.
85 */
86static bool coresight_disable_source_sysfs(struct coresight_device *csdev,
87 void *data)
88{
89 lockdep_assert_held(&coresight_mutex);
90 if (coresight_get_mode(csdev) != CS_MODE_SYSFS)
91 return false;
92
93 csdev->refcnt--;
94 if (csdev->refcnt == 0) {
95 coresight_disable_source(csdev, data);
96 return true;
97 }
98 return false;
99}
100
101/**
102 * coresight_find_activated_sysfs_sink - returns the first sink activated via
103 * sysfs using connection based search starting from the source reference.
104 *
105 * @csdev: Coresight source device reference
106 */
107static struct coresight_device *
108coresight_find_activated_sysfs_sink(struct coresight_device *csdev)
109{
110 int i;
111 struct coresight_device *sink = NULL;
112
113 if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
114 csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
115 csdev->sysfs_sink_activated)
116 return csdev;
117
118 /*
119 * Recursively explore each port found on this element.
120 */
121 for (i = 0; i < csdev->pdata->nr_outconns; i++) {
122 struct coresight_device *child_dev;
123
124 child_dev = csdev->pdata->out_conns[i]->dest_dev;
125 if (child_dev)
126 sink = coresight_find_activated_sysfs_sink(csdev: child_dev);
127 if (sink)
128 return sink;
129 }
130
131 return NULL;
132}
133
134/** coresight_validate_source - make sure a source has the right credentials to
135 * be used via sysfs.
136 * @csdev: the device structure for a source.
137 * @function: the function this was called from.
138 *
139 * Assumes the coresight_mutex is held.
140 */
141static int coresight_validate_source_sysfs(struct coresight_device *csdev,
142 const char *function)
143{
144 u32 type, subtype;
145
146 type = csdev->type;
147 subtype = csdev->subtype.source_subtype;
148
149 if (type != CORESIGHT_DEV_TYPE_SOURCE) {
150 dev_err(&csdev->dev, "wrong device type in %s\n", function);
151 return -EINVAL;
152 }
153
154 if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
155 subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
156 subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&
157 subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
158 dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
159 return -EINVAL;
160 }
161
162 return 0;
163}
164
165int coresight_enable_sysfs(struct coresight_device *csdev)
166{
167 int cpu, ret = 0;
168 struct coresight_device *sink;
169 struct list_head *path;
170 enum coresight_dev_subtype_source subtype;
171 u32 hash;
172
173 subtype = csdev->subtype.source_subtype;
174
175 mutex_lock(&coresight_mutex);
176
177 ret = coresight_validate_source_sysfs(csdev, function: __func__);
178 if (ret)
179 goto out;
180
181 /*
182 * mode == SYSFS implies that it's already enabled. Don't look at the
183 * refcount to determine this because we don't claim the source until
184 * coresight_enable_source() so can still race with Perf mode which
185 * doesn't hold coresight_mutex.
186 */
187 if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
188 /*
189 * There could be multiple applications driving the software
190 * source. So keep the refcount for each such user when the
191 * source is already enabled.
192 */
193 if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
194 csdev->refcnt++;
195 goto out;
196 }
197
198 sink = coresight_find_activated_sysfs_sink(csdev);
199 if (!sink) {
200 ret = -EINVAL;
201 goto out;
202 }
203
204 path = coresight_build_path(csdev, sink);
205 if (IS_ERR(ptr: path)) {
206 pr_err("building path(s) failed\n");
207 ret = PTR_ERR(ptr: path);
208 goto out;
209 }
210
211 ret = coresight_enable_path(path, mode: CS_MODE_SYSFS, NULL);
212 if (ret)
213 goto err_path;
214
215 ret = coresight_enable_source_sysfs(csdev, mode: CS_MODE_SYSFS, NULL);
216 if (ret)
217 goto err_source;
218
219 switch (subtype) {
220 case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
221 /*
222 * When working from sysFS it is important to keep track
223 * of the paths that were created so that they can be
224 * undone in 'coresight_disable()'. Since there can only
225 * be a single session per tracer (when working from sysFS)
226 * a per-cpu variable will do just fine.
227 */
228 cpu = source_ops(csdev)->cpu_id(csdev);
229 per_cpu(tracer_path, cpu) = path;
230 break;
231 case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
232 case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
233 case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
234 /*
235 * Use the hash of source's device name as ID
236 * and map the ID to the pointer of the path.
237 */
238 hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
239 ret = idr_alloc_u32(&path_idr, ptr: path, id: &hash, max: hash, GFP_KERNEL);
240 if (ret)
241 goto err_source;
242 break;
243 default:
244 /* We can't be here */
245 break;
246 }
247
248out:
249 mutex_unlock(lock: &coresight_mutex);
250 return ret;
251
252err_source:
253 coresight_disable_path(path);
254
255err_path:
256 coresight_release_path(path);
257 goto out;
258}
259EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
260
261void coresight_disable_sysfs(struct coresight_device *csdev)
262{
263 int cpu, ret;
264 struct list_head *path = NULL;
265 u32 hash;
266
267 mutex_lock(&coresight_mutex);
268
269 ret = coresight_validate_source_sysfs(csdev, function: __func__);
270 if (ret)
271 goto out;
272
273 if (!coresight_disable_source_sysfs(csdev, NULL))
274 goto out;
275
276 switch (csdev->subtype.source_subtype) {
277 case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
278 cpu = source_ops(csdev)->cpu_id(csdev);
279 path = per_cpu(tracer_path, cpu);
280 per_cpu(tracer_path, cpu) = NULL;
281 break;
282 case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
283 case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
284 case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
285 hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
286 /* Find the path by the hash. */
287 path = idr_find(&path_idr, id: hash);
288 if (path == NULL) {
289 pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
290 goto out;
291 }
292 idr_remove(&path_idr, id: hash);
293 break;
294 default:
295 /* We can't be here */
296 break;
297 }
298
299 coresight_disable_path(path);
300 coresight_release_path(path);
301
302out:
303 mutex_unlock(lock: &coresight_mutex);
304}
305EXPORT_SYMBOL_GPL(coresight_disable_sysfs);
306
307static ssize_t enable_sink_show(struct device *dev,
308 struct device_attribute *attr, char *buf)
309{
310 struct coresight_device *csdev = to_coresight_device(dev);
311
312 return scnprintf(buf, PAGE_SIZE, fmt: "%u\n", csdev->sysfs_sink_activated);
313}
314
315static ssize_t enable_sink_store(struct device *dev,
316 struct device_attribute *attr,
317 const char *buf, size_t size)
318{
319 int ret;
320 unsigned long val;
321 struct coresight_device *csdev = to_coresight_device(dev);
322
323 ret = kstrtoul(s: buf, base: 10, res: &val);
324 if (ret)
325 return ret;
326
327 csdev->sysfs_sink_activated = !!val;
328
329 return size;
330
331}
332static DEVICE_ATTR_RW(enable_sink);
333
334static ssize_t enable_source_show(struct device *dev,
335 struct device_attribute *attr, char *buf)
336{
337 struct coresight_device *csdev = to_coresight_device(dev);
338
339 guard(mutex)(T: &coresight_mutex);
340 return scnprintf(buf, PAGE_SIZE, fmt: "%u\n",
341 coresight_get_mode(csdev) == CS_MODE_SYSFS);
342}
343
344static ssize_t enable_source_store(struct device *dev,
345 struct device_attribute *attr,
346 const char *buf, size_t size)
347{
348 int ret = 0;
349 unsigned long val;
350 struct coresight_device *csdev = to_coresight_device(dev);
351
352 ret = kstrtoul(s: buf, base: 10, res: &val);
353 if (ret)
354 return ret;
355
356 if (val) {
357 ret = coresight_enable_sysfs(csdev);
358 if (ret)
359 return ret;
360 } else {
361 coresight_disable_sysfs(csdev);
362 }
363
364 return size;
365}
366static DEVICE_ATTR_RW(enable_source);
367
368static struct attribute *coresight_sink_attrs[] = {
369 &dev_attr_enable_sink.attr,
370 NULL,
371};
372ATTRIBUTE_GROUPS(coresight_sink);
373
374static struct attribute *coresight_source_attrs[] = {
375 &dev_attr_enable_source.attr,
376 NULL,
377};
378ATTRIBUTE_GROUPS(coresight_source);
379
380struct device_type coresight_dev_type[] = {
381 [CORESIGHT_DEV_TYPE_SINK] = {
382 .name = "sink",
383 .groups = coresight_sink_groups,
384 },
385 [CORESIGHT_DEV_TYPE_LINK] = {
386 .name = "link",
387 },
388 [CORESIGHT_DEV_TYPE_LINKSINK] = {
389 .name = "linksink",
390 .groups = coresight_sink_groups,
391 },
392 [CORESIGHT_DEV_TYPE_SOURCE] = {
393 .name = "source",
394 .groups = coresight_source_groups,
395 },
396 [CORESIGHT_DEV_TYPE_HELPER] = {
397 .name = "helper",
398 }
399};
400/* Ensure the enum matches the names and groups */
401static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
402
403/*
404 * Connections group - links attribute.
405 * Count of created links between coresight components in the group.
406 */
407static ssize_t nr_links_show(struct device *dev,
408 struct device_attribute *attr,
409 char *buf)
410{
411 struct coresight_device *csdev = to_coresight_device(dev);
412
413 return sprintf(buf, fmt: "%d\n", csdev->nr_links);
414}
415static DEVICE_ATTR_RO(nr_links);
416
417static struct attribute *coresight_conns_attrs[] = {
418 &dev_attr_nr_links.attr,
419 NULL,
420};
421
422static struct attribute_group coresight_conns_group = {
423 .attrs = coresight_conns_attrs,
424 .name = "connections",
425};
426
427/*
428 * Create connections group for CoreSight devices.
429 * This group will then be used to collate the sysfs links between
430 * devices.
431 */
432int coresight_create_conns_sysfs_group(struct coresight_device *csdev)
433{
434 int ret = 0;
435
436 if (!csdev)
437 return -EINVAL;
438
439 ret = sysfs_create_group(kobj: &csdev->dev.kobj, grp: &coresight_conns_group);
440 if (ret)
441 return ret;
442
443 csdev->has_conns_grp = true;
444 return ret;
445}
446
447void coresight_remove_conns_sysfs_group(struct coresight_device *csdev)
448{
449 if (!csdev)
450 return;
451
452 if (csdev->has_conns_grp) {
453 sysfs_remove_group(kobj: &csdev->dev.kobj, grp: &coresight_conns_group);
454 csdev->has_conns_grp = false;
455 }
456}
457
458int coresight_add_sysfs_link(struct coresight_sysfs_link *info)
459{
460 int ret = 0;
461
462 if (!info)
463 return -EINVAL;
464 if (!info->orig || !info->target ||
465 !info->orig_name || !info->target_name)
466 return -EINVAL;
467 if (!info->orig->has_conns_grp || !info->target->has_conns_grp)
468 return -EINVAL;
469
470 /* first link orig->target */
471 ret = sysfs_add_link_to_group(kobj: &info->orig->dev.kobj,
472 group_name: coresight_conns_group.name,
473 target: &info->target->dev.kobj,
474 link_name: info->orig_name);
475 if (ret)
476 return ret;
477
478 /* second link target->orig */
479 ret = sysfs_add_link_to_group(kobj: &info->target->dev.kobj,
480 group_name: coresight_conns_group.name,
481 target: &info->orig->dev.kobj,
482 link_name: info->target_name);
483
484 /* error in second link - remove first - otherwise inc counts */
485 if (ret) {
486 sysfs_remove_link_from_group(kobj: &info->orig->dev.kobj,
487 group_name: coresight_conns_group.name,
488 link_name: info->orig_name);
489 } else {
490 info->orig->nr_links++;
491 info->target->nr_links++;
492 }
493
494 return ret;
495}
496EXPORT_SYMBOL_GPL(coresight_add_sysfs_link);
497
498void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)
499{
500 if (!info)
501 return;
502 if (!info->orig || !info->target ||
503 !info->orig_name || !info->target_name)
504 return;
505
506 sysfs_remove_link_from_group(kobj: &info->orig->dev.kobj,
507 group_name: coresight_conns_group.name,
508 link_name: info->orig_name);
509
510 sysfs_remove_link_from_group(kobj: &info->target->dev.kobj,
511 group_name: coresight_conns_group.name,
512 link_name: info->target_name);
513
514 info->orig->nr_links--;
515 info->target->nr_links--;
516}
517EXPORT_SYMBOL_GPL(coresight_remove_sysfs_link);
518
519/*
520 * coresight_make_links: Make a link for a connection from a @orig
521 * device to @target, represented by @conn.
522 *
523 * e.g, for devOrig[output_X] -> devTarget[input_Y] is represented
524 * as two symbolic links :
525 *
526 * /sys/.../devOrig/out:X -> /sys/.../devTarget/
527 * /sys/.../devTarget/in:Y -> /sys/.../devOrig/
528 *
529 * The link names are allocated for a device where it appears. i.e, the
530 * "out" link on the master and "in" link on the slave device.
531 * The link info is stored in the connection record for avoiding
532 * the reconstruction of names for removal.
533 */
534int coresight_make_links(struct coresight_device *orig,
535 struct coresight_connection *conn,
536 struct coresight_device *target)
537{
538 int ret = -ENOMEM;
539 char *outs = NULL, *ins = NULL;
540 struct coresight_sysfs_link *link = NULL;
541
542 /* Helper devices aren't shown in sysfs */
543 if (conn->dest_port == -1 && conn->src_port == -1)
544 return 0;
545
546 do {
547 outs = devm_kasprintf(dev: &orig->dev, GFP_KERNEL,
548 fmt: "out:%d", conn->src_port);
549 if (!outs)
550 break;
551 ins = devm_kasprintf(dev: &target->dev, GFP_KERNEL,
552 fmt: "in:%d", conn->dest_port);
553 if (!ins)
554 break;
555 link = devm_kzalloc(dev: &orig->dev,
556 size: sizeof(struct coresight_sysfs_link),
557 GFP_KERNEL);
558 if (!link)
559 break;
560
561 link->orig = orig;
562 link->target = target;
563 link->orig_name = outs;
564 link->target_name = ins;
565
566 ret = coresight_add_sysfs_link(link);
567 if (ret)
568 break;
569
570 conn->link = link;
571 return 0;
572 } while (0);
573
574 return ret;
575}
576
577/*
578 * coresight_remove_links: Remove the sysfs links for a given connection @conn,
579 * from @orig device to @target device. See coresight_make_links() for more
580 * details.
581 */
582void coresight_remove_links(struct coresight_device *orig,
583 struct coresight_connection *conn)
584{
585 if (!orig || !conn->link)
586 return;
587
588 coresight_remove_sysfs_link(conn->link);
589
590 devm_kfree(dev: &conn->dest_dev->dev, p: conn->link->target_name);
591 devm_kfree(dev: &orig->dev, p: conn->link->orig_name);
592 devm_kfree(dev: &orig->dev, p: conn->link);
593 conn->link = NULL;
594}
595

source code of linux/drivers/hwtracing/coresight/coresight-sysfs.c