Linux Audio

Check our new training course

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