1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2019, Linaro Limited, All rights reserved.
4 * Author: Mike Leach <mike.leach@linaro.org>
7 #include <linux/device.h>
9 #include <linux/kernel.h>
11 #include "coresight-priv.h"
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.
18 static DEFINE_IDR(path_idr);
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.
24 static DEFINE_PER_CPU(struct list_head *, tracer_path);
26 ssize_t coresight_simple_show_pair(struct device *_dev,
27 struct device_attribute *attr, char *buf)
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);
33 pm_runtime_get_sync(_dev->parent);
34 val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off);
35 pm_runtime_put_sync(_dev->parent);
36 return sysfs_emit(buf, "0x%llx\n", val);
38 EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
40 ssize_t coresight_simple_show32(struct device *_dev,
41 struct device_attribute *attr, char *buf)
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);
47 pm_runtime_get_sync(_dev->parent);
48 val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off);
49 pm_runtime_put_sync(_dev->parent);
50 return sysfs_emit(buf, "0x%llx\n", val);
52 EXPORT_SYMBOL_GPL(coresight_simple_show32);
54 static int coresight_enable_source_sysfs(struct coresight_device *csdev,
55 enum cs_mode mode, void *data)
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.
64 lockdep_assert_held(&coresight_mutex);
65 if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
66 ret = source_ops(csdev)->enable(csdev, data, mode);
77 * coresight_disable_source_sysfs - Drop the reference count by 1 and disable
78 * the device if there are no users left.
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.
84 * Returns true if the device has been disabled.
86 static bool coresight_disable_source_sysfs(struct coresight_device *csdev,
89 lockdep_assert_held(&coresight_mutex);
90 if (coresight_get_mode(csdev) != CS_MODE_SYSFS)
94 if (csdev->refcnt == 0) {
95 coresight_disable_source(csdev, data);
102 * coresight_find_activated_sysfs_sink - returns the first sink activated via
103 * sysfs using connection based search starting from the source reference.
105 * @csdev: Coresight source device reference
107 static struct coresight_device *
108 coresight_find_activated_sysfs_sink(struct coresight_device *csdev)
111 struct coresight_device *sink = NULL;
113 if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
114 csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
115 csdev->sysfs_sink_activated)
119 * Recursively explore each port found on this element.
121 for (i = 0; i < csdev->pdata->nr_outconns; i++) {
122 struct coresight_device *child_dev;
124 child_dev = csdev->pdata->out_conns[i]->dest_dev;
126 sink = coresight_find_activated_sysfs_sink(child_dev);
134 /** coresight_validate_source - make sure a source has the right credentials to
136 * @csdev: the device structure for a source.
137 * @function: the function this was called from.
139 * Assumes the coresight_mutex is held.
141 static int coresight_validate_source_sysfs(struct coresight_device *csdev,
142 const char *function)
147 subtype = csdev->subtype.source_subtype;
149 if (type != CORESIGHT_DEV_TYPE_SOURCE) {
150 dev_err(&csdev->dev, "wrong device type in %s\n", function);
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);
165 int coresight_enable_sysfs(struct coresight_device *csdev)
168 struct coresight_device *sink;
169 struct list_head *path;
170 enum coresight_dev_subtype_source subtype;
173 subtype = csdev->subtype.source_subtype;
175 mutex_lock(&coresight_mutex);
177 ret = coresight_validate_source_sysfs(csdev, __func__);
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.
187 if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
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.
193 if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
198 sink = coresight_find_activated_sysfs_sink(csdev);
204 path = coresight_build_path(csdev, sink);
206 pr_err("building path(s) failed\n");
211 ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
215 ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL);
220 case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
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.
228 cpu = source_ops(csdev)->cpu_id(csdev);
229 per_cpu(tracer_path, cpu) = path;
231 case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
232 case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
233 case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
235 * Use the hash of source's device name as ID
236 * and map the ID to the pointer of the path.
238 hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
239 ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
244 /* We can't be here */
249 mutex_unlock(&coresight_mutex);
253 coresight_disable_path(path);
256 coresight_release_path(path);
259 EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
261 void coresight_disable_sysfs(struct coresight_device *csdev)
264 struct list_head *path = NULL;
267 mutex_lock(&coresight_mutex);
269 ret = coresight_validate_source_sysfs(csdev, __func__);
273 if (!coresight_disable_source_sysfs(csdev, NULL))
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;
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, hash);
289 pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
292 idr_remove(&path_idr, hash);
295 /* We can't be here */
299 coresight_disable_path(path);
300 coresight_release_path(path);
303 mutex_unlock(&coresight_mutex);
305 EXPORT_SYMBOL_GPL(coresight_disable_sysfs);
307 static ssize_t enable_sink_show(struct device *dev,
308 struct device_attribute *attr, char *buf)
310 struct coresight_device *csdev = to_coresight_device(dev);
312 return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated);
315 static ssize_t enable_sink_store(struct device *dev,
316 struct device_attribute *attr,
317 const char *buf, size_t size)
321 struct coresight_device *csdev = to_coresight_device(dev);
323 ret = kstrtoul(buf, 10, &val);
327 csdev->sysfs_sink_activated = !!val;
332 static DEVICE_ATTR_RW(enable_sink);
334 static ssize_t enable_source_show(struct device *dev,
335 struct device_attribute *attr, char *buf)
337 struct coresight_device *csdev = to_coresight_device(dev);
339 guard(mutex)(&coresight_mutex);
340 return scnprintf(buf, PAGE_SIZE, "%u\n",
341 coresight_get_mode(csdev) == CS_MODE_SYSFS);
344 static ssize_t enable_source_store(struct device *dev,
345 struct device_attribute *attr,
346 const char *buf, size_t size)
350 struct coresight_device *csdev = to_coresight_device(dev);
352 ret = kstrtoul(buf, 10, &val);
357 ret = coresight_enable_sysfs(csdev);
361 coresight_disable_sysfs(csdev);
366 static DEVICE_ATTR_RW(enable_source);
368 static struct attribute *coresight_sink_attrs[] = {
369 &dev_attr_enable_sink.attr,
372 ATTRIBUTE_GROUPS(coresight_sink);
374 static struct attribute *coresight_source_attrs[] = {
375 &dev_attr_enable_source.attr,
378 ATTRIBUTE_GROUPS(coresight_source);
380 struct device_type coresight_dev_type[] = {
381 [CORESIGHT_DEV_TYPE_SINK] = {
383 .groups = coresight_sink_groups,
385 [CORESIGHT_DEV_TYPE_LINK] = {
388 [CORESIGHT_DEV_TYPE_LINKSINK] = {
390 .groups = coresight_sink_groups,
392 [CORESIGHT_DEV_TYPE_SOURCE] = {
394 .groups = coresight_source_groups,
396 [CORESIGHT_DEV_TYPE_HELPER] = {
400 /* Ensure the enum matches the names and groups */
401 static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
404 * Connections group - links attribute.
405 * Count of created links between coresight components in the group.
407 static ssize_t nr_links_show(struct device *dev,
408 struct device_attribute *attr,
411 struct coresight_device *csdev = to_coresight_device(dev);
413 return sprintf(buf, "%d\n", csdev->nr_links);
415 static DEVICE_ATTR_RO(nr_links);
417 static struct attribute *coresight_conns_attrs[] = {
418 &dev_attr_nr_links.attr,
422 static struct attribute_group coresight_conns_group = {
423 .attrs = coresight_conns_attrs,
424 .name = "connections",
428 * Create connections group for CoreSight devices.
429 * This group will then be used to collate the sysfs links between
432 int coresight_create_conns_sysfs_group(struct coresight_device *csdev)
439 ret = sysfs_create_group(&csdev->dev.kobj, &coresight_conns_group);
443 csdev->has_conns_grp = true;
447 void coresight_remove_conns_sysfs_group(struct coresight_device *csdev)
452 if (csdev->has_conns_grp) {
453 sysfs_remove_group(&csdev->dev.kobj, &coresight_conns_group);
454 csdev->has_conns_grp = false;
458 int coresight_add_sysfs_link(struct coresight_sysfs_link *info)
464 if (!info->orig || !info->target ||
465 !info->orig_name || !info->target_name)
467 if (!info->orig->has_conns_grp || !info->target->has_conns_grp)
470 /* first link orig->target */
471 ret = sysfs_add_link_to_group(&info->orig->dev.kobj,
472 coresight_conns_group.name,
473 &info->target->dev.kobj,
478 /* second link target->orig */
479 ret = sysfs_add_link_to_group(&info->target->dev.kobj,
480 coresight_conns_group.name,
481 &info->orig->dev.kobj,
484 /* error in second link - remove first - otherwise inc counts */
486 sysfs_remove_link_from_group(&info->orig->dev.kobj,
487 coresight_conns_group.name,
490 info->orig->nr_links++;
491 info->target->nr_links++;
496 EXPORT_SYMBOL_GPL(coresight_add_sysfs_link);
498 void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)
502 if (!info->orig || !info->target ||
503 !info->orig_name || !info->target_name)
506 sysfs_remove_link_from_group(&info->orig->dev.kobj,
507 coresight_conns_group.name,
510 sysfs_remove_link_from_group(&info->target->dev.kobj,
511 coresight_conns_group.name,
514 info->orig->nr_links--;
515 info->target->nr_links--;
517 EXPORT_SYMBOL_GPL(coresight_remove_sysfs_link);
520 * coresight_make_links: Make a link for a connection from a @orig
521 * device to @target, represented by @conn.
523 * e.g, for devOrig[output_X] -> devTarget[input_Y] is represented
524 * as two symbolic links :
526 * /sys/.../devOrig/out:X -> /sys/.../devTarget/
527 * /sys/.../devTarget/in:Y -> /sys/.../devOrig/
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.
534 int coresight_make_links(struct coresight_device *orig,
535 struct coresight_connection *conn,
536 struct coresight_device *target)
539 char *outs = NULL, *ins = NULL;
540 struct coresight_sysfs_link *link = NULL;
542 /* Helper devices aren't shown in sysfs */
543 if (conn->dest_port == -1 && conn->src_port == -1)
547 outs = devm_kasprintf(&orig->dev, GFP_KERNEL,
548 "out:%d", conn->src_port);
551 ins = devm_kasprintf(&target->dev, GFP_KERNEL,
552 "in:%d", conn->dest_port);
555 link = devm_kzalloc(&orig->dev,
556 sizeof(struct coresight_sysfs_link),
562 link->target = target;
563 link->orig_name = outs;
564 link->target_name = ins;
566 ret = coresight_add_sysfs_link(link);
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
582 void coresight_remove_links(struct coresight_device *orig,
583 struct coresight_connection *conn)
585 if (!orig || !conn->link)
588 coresight_remove_sysfs_link(conn->link);
590 devm_kfree(&conn->dest_dev->dev, conn->link->target_name);
591 devm_kfree(&orig->dev, conn->link->orig_name);
592 devm_kfree(&orig->dev, conn->link);