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->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);
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->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);
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(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, __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(path)) {
206		pr_err("building path(s) failed\n");
207		ret = PTR_ERR(path);
208		goto out;
209	}
210
211	ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
212	if (ret)
213		goto err_path;
214
215	ret = coresight_enable_source_sysfs(csdev, 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, path, &hash, 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(&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, __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, 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, 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(&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, "%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(buf, 10, &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)(&coresight_mutex);
340	return scnprintf(buf, PAGE_SIZE, "%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(buf, 10, &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, "%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(&csdev->dev.kobj, &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(&csdev->dev.kobj, &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(&info->orig->dev.kobj,
472				      coresight_conns_group.name,
473				      &info->target->dev.kobj,
474				      info->orig_name);
475	if (ret)
476		return ret;
477
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,
482				      info->target_name);
483
484	/* error in second link - remove first - otherwise inc counts */
485	if (ret) {
486		sysfs_remove_link_from_group(&info->orig->dev.kobj,
487					     coresight_conns_group.name,
488					     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(&info->orig->dev.kobj,
507				     coresight_conns_group.name,
508				     info->orig_name);
509
510	sysfs_remove_link_from_group(&info->target->dev.kobj,
511				     coresight_conns_group.name,
512				     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(&orig->dev, GFP_KERNEL,
548				      "out:%d", conn->src_port);
549		if (!outs)
550			break;
551		ins = devm_kasprintf(&target->dev, GFP_KERNEL,
552				     "in:%d", conn->dest_port);
553		if (!ins)
554			break;
555		link = devm_kzalloc(&orig->dev,
556				    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(&conn->dest_dev->dev, conn->link->target_name);
591	devm_kfree(&orig->dev, conn->link->orig_name);
592	devm_kfree(&orig->dev, conn->link);
593	conn->link = NULL;
594}
595