180961525SMike Leach // SPDX-License-Identifier: GPL-2.0
280961525SMike Leach /*
380961525SMike Leach  * Copyright (c) 2019, Linaro Limited, All rights reserved.
480961525SMike Leach  * Author: Mike Leach <mike.leach@linaro.org>
580961525SMike Leach  */
680961525SMike Leach 
780961525SMike Leach #include <linux/device.h>
880961525SMike Leach #include <linux/kernel.h>
980961525SMike Leach 
1080961525SMike Leach #include "coresight-priv.h"
1180961525SMike Leach 
1280961525SMike Leach /*
1380961525SMike Leach  * Connections group - links attribute.
1480961525SMike Leach  * Count of created links between coresight components in the group.
1580961525SMike Leach  */
nr_links_show(struct device * dev,struct device_attribute * attr,char * buf)1680961525SMike Leach static ssize_t nr_links_show(struct device *dev,
1780961525SMike Leach 			     struct device_attribute *attr,
1880961525SMike Leach 			     char *buf)
1980961525SMike Leach {
2080961525SMike Leach 	struct coresight_device *csdev = to_coresight_device(dev);
2180961525SMike Leach 
2280961525SMike Leach 	return sprintf(buf, "%d\n", csdev->nr_links);
2380961525SMike Leach }
2480961525SMike Leach static DEVICE_ATTR_RO(nr_links);
2580961525SMike Leach 
2680961525SMike Leach static struct attribute *coresight_conns_attrs[] = {
2780961525SMike Leach 	&dev_attr_nr_links.attr,
2880961525SMike Leach 	NULL,
2980961525SMike Leach };
3080961525SMike Leach 
3180961525SMike Leach static struct attribute_group coresight_conns_group = {
3280961525SMike Leach 	.attrs = coresight_conns_attrs,
3380961525SMike Leach 	.name = "connections",
3480961525SMike Leach };
3580961525SMike Leach 
3680961525SMike Leach /*
3780961525SMike Leach  * Create connections group for CoreSight devices.
3880961525SMike Leach  * This group will then be used to collate the sysfs links between
3980961525SMike Leach  * devices.
4080961525SMike Leach  */
coresight_create_conns_sysfs_group(struct coresight_device * csdev)4180961525SMike Leach int coresight_create_conns_sysfs_group(struct coresight_device *csdev)
4280961525SMike Leach {
4380961525SMike Leach 	int ret = 0;
4480961525SMike Leach 
4580961525SMike Leach 	if (!csdev)
4680961525SMike Leach 		return -EINVAL;
4780961525SMike Leach 
4880961525SMike Leach 	ret = sysfs_create_group(&csdev->dev.kobj, &coresight_conns_group);
4980961525SMike Leach 	if (ret)
5080961525SMike Leach 		return ret;
5180961525SMike Leach 
5280961525SMike Leach 	csdev->has_conns_grp = true;
5380961525SMike Leach 	return ret;
5480961525SMike Leach }
5580961525SMike Leach 
coresight_remove_conns_sysfs_group(struct coresight_device * csdev)5680961525SMike Leach void coresight_remove_conns_sysfs_group(struct coresight_device *csdev)
5780961525SMike Leach {
5880961525SMike Leach 	if (!csdev)
5980961525SMike Leach 		return;
6080961525SMike Leach 
6180961525SMike Leach 	if (csdev->has_conns_grp) {
6280961525SMike Leach 		sysfs_remove_group(&csdev->dev.kobj, &coresight_conns_group);
6380961525SMike Leach 		csdev->has_conns_grp = false;
6480961525SMike Leach 	}
6580961525SMike Leach }
6680961525SMike Leach 
coresight_add_sysfs_link(struct coresight_sysfs_link * info)6780961525SMike Leach int coresight_add_sysfs_link(struct coresight_sysfs_link *info)
6880961525SMike Leach {
6980961525SMike Leach 	int ret = 0;
7080961525SMike Leach 
7180961525SMike Leach 	if (!info)
7280961525SMike Leach 		return -EINVAL;
7380961525SMike Leach 	if (!info->orig || !info->target ||
7480961525SMike Leach 	    !info->orig_name || !info->target_name)
7580961525SMike Leach 		return -EINVAL;
7680961525SMike Leach 	if (!info->orig->has_conns_grp || !info->target->has_conns_grp)
7780961525SMike Leach 		return -EINVAL;
7880961525SMike Leach 
7980961525SMike Leach 	/* first link orig->target */
8080961525SMike Leach 	ret = sysfs_add_link_to_group(&info->orig->dev.kobj,
8180961525SMike Leach 				      coresight_conns_group.name,
8280961525SMike Leach 				      &info->target->dev.kobj,
8380961525SMike Leach 				      info->orig_name);
8480961525SMike Leach 	if (ret)
8580961525SMike Leach 		return ret;
8680961525SMike Leach 
8780961525SMike Leach 	/* second link target->orig */
8880961525SMike Leach 	ret = sysfs_add_link_to_group(&info->target->dev.kobj,
8980961525SMike Leach 				      coresight_conns_group.name,
9080961525SMike Leach 				      &info->orig->dev.kobj,
9180961525SMike Leach 				      info->target_name);
9280961525SMike Leach 
9380961525SMike Leach 	/* error in second link - remove first - otherwise inc counts */
9480961525SMike Leach 	if (ret) {
9580961525SMike Leach 		sysfs_remove_link_from_group(&info->orig->dev.kobj,
9680961525SMike Leach 					     coresight_conns_group.name,
9780961525SMike Leach 					     info->orig_name);
9880961525SMike Leach 	} else {
9980961525SMike Leach 		info->orig->nr_links++;
10080961525SMike Leach 		info->target->nr_links++;
10180961525SMike Leach 	}
10280961525SMike Leach 
10380961525SMike Leach 	return ret;
10480961525SMike Leach }
105d735d925SMian Yousaf Kaukab EXPORT_SYMBOL_GPL(coresight_add_sysfs_link);
10680961525SMike Leach 
coresight_remove_sysfs_link(struct coresight_sysfs_link * info)10780961525SMike Leach void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)
10880961525SMike Leach {
10980961525SMike Leach 	if (!info)
11080961525SMike Leach 		return;
11180961525SMike Leach 	if (!info->orig || !info->target ||
11280961525SMike Leach 	    !info->orig_name || !info->target_name)
11380961525SMike Leach 		return;
11480961525SMike Leach 
11580961525SMike Leach 	sysfs_remove_link_from_group(&info->orig->dev.kobj,
11680961525SMike Leach 				     coresight_conns_group.name,
11780961525SMike Leach 				     info->orig_name);
11880961525SMike Leach 
11980961525SMike Leach 	sysfs_remove_link_from_group(&info->target->dev.kobj,
12080961525SMike Leach 				     coresight_conns_group.name,
12180961525SMike Leach 				     info->target_name);
12280961525SMike Leach 
12380961525SMike Leach 	info->orig->nr_links--;
12480961525SMike Leach 	info->target->nr_links--;
12580961525SMike Leach }
126d735d925SMian Yousaf Kaukab EXPORT_SYMBOL_GPL(coresight_remove_sysfs_link);
1278a7365c2SSuzuki K Poulose 
1288a7365c2SSuzuki K Poulose /*
1298a7365c2SSuzuki K Poulose  * coresight_make_links: Make a link for a connection from a @orig
1308a7365c2SSuzuki K Poulose  * device to @target, represented by @conn.
1318a7365c2SSuzuki K Poulose  *
1328a7365c2SSuzuki K Poulose  *   e.g, for devOrig[output_X] -> devTarget[input_Y] is represented
1338a7365c2SSuzuki K Poulose  *   as two symbolic links :
1348a7365c2SSuzuki K Poulose  *
1358a7365c2SSuzuki K Poulose  *	/sys/.../devOrig/out:X	-> /sys/.../devTarget/
1368a7365c2SSuzuki K Poulose  *	/sys/.../devTarget/in:Y	-> /sys/.../devOrig/
1378a7365c2SSuzuki K Poulose  *
1388a7365c2SSuzuki K Poulose  * The link names are allocated for a device where it appears. i.e, the
1398a7365c2SSuzuki K Poulose  * "out" link on the master and "in" link on the slave device.
1408a7365c2SSuzuki K Poulose  * The link info is stored in the connection record for avoiding
1418a7365c2SSuzuki K Poulose  * the reconstruction of names for removal.
1428a7365c2SSuzuki K Poulose  */
coresight_make_links(struct coresight_device * orig,struct coresight_connection * conn,struct coresight_device * target)1438a7365c2SSuzuki K Poulose int coresight_make_links(struct coresight_device *orig,
1448a7365c2SSuzuki K Poulose 			 struct coresight_connection *conn,
1458a7365c2SSuzuki K Poulose 			 struct coresight_device *target)
1468a7365c2SSuzuki K Poulose {
1478a7365c2SSuzuki K Poulose 	int ret = -ENOMEM;
1488a7365c2SSuzuki K Poulose 	char *outs = NULL, *ins = NULL;
1498a7365c2SSuzuki K Poulose 	struct coresight_sysfs_link *link = NULL;
1508a7365c2SSuzuki K Poulose 
151*1b5b1646SJames Clark 	/* Helper devices aren't shown in sysfs */
152*1b5b1646SJames Clark 	if (conn->dest_port == -1 && conn->src_port == -1)
153*1b5b1646SJames Clark 		return 0;
154*1b5b1646SJames Clark 
1558a7365c2SSuzuki K Poulose 	do {
1568a7365c2SSuzuki K Poulose 		outs = devm_kasprintf(&orig->dev, GFP_KERNEL,
157d49c9cf1SJames Clark 				      "out:%d", conn->src_port);
1588a7365c2SSuzuki K Poulose 		if (!outs)
1598a7365c2SSuzuki K Poulose 			break;
1608a7365c2SSuzuki K Poulose 		ins = devm_kasprintf(&target->dev, GFP_KERNEL,
161d49c9cf1SJames Clark 				     "in:%d", conn->dest_port);
1628a7365c2SSuzuki K Poulose 		if (!ins)
1638a7365c2SSuzuki K Poulose 			break;
1648a7365c2SSuzuki K Poulose 		link = devm_kzalloc(&orig->dev,
1658a7365c2SSuzuki K Poulose 				    sizeof(struct coresight_sysfs_link),
1668a7365c2SSuzuki K Poulose 				    GFP_KERNEL);
1678a7365c2SSuzuki K Poulose 		if (!link)
1688a7365c2SSuzuki K Poulose 			break;
1698a7365c2SSuzuki K Poulose 
1708a7365c2SSuzuki K Poulose 		link->orig = orig;
1718a7365c2SSuzuki K Poulose 		link->target = target;
1728a7365c2SSuzuki K Poulose 		link->orig_name = outs;
1738a7365c2SSuzuki K Poulose 		link->target_name = ins;
1748a7365c2SSuzuki K Poulose 
1758a7365c2SSuzuki K Poulose 		ret = coresight_add_sysfs_link(link);
1768a7365c2SSuzuki K Poulose 		if (ret)
1778a7365c2SSuzuki K Poulose 			break;
1788a7365c2SSuzuki K Poulose 
1798a7365c2SSuzuki K Poulose 		conn->link = link;
1808a7365c2SSuzuki K Poulose 		return 0;
1818a7365c2SSuzuki K Poulose 	} while (0);
1828a7365c2SSuzuki K Poulose 
1838a7365c2SSuzuki K Poulose 	return ret;
1848a7365c2SSuzuki K Poulose }
1858a7365c2SSuzuki K Poulose 
1868a7365c2SSuzuki K Poulose /*
1878a7365c2SSuzuki K Poulose  * coresight_remove_links: Remove the sysfs links for a given connection @conn,
1888a7365c2SSuzuki K Poulose  * from @orig device to @target device. See coresight_make_links() for more
1898a7365c2SSuzuki K Poulose  * details.
1908a7365c2SSuzuki K Poulose  */
coresight_remove_links(struct coresight_device * orig,struct coresight_connection * conn)1918a7365c2SSuzuki K Poulose void coresight_remove_links(struct coresight_device *orig,
1928a7365c2SSuzuki K Poulose 			    struct coresight_connection *conn)
1938a7365c2SSuzuki K Poulose {
1948a7365c2SSuzuki K Poulose 	if (!orig || !conn->link)
1958a7365c2SSuzuki K Poulose 		return;
1968a7365c2SSuzuki K Poulose 
1978a7365c2SSuzuki K Poulose 	coresight_remove_sysfs_link(conn->link);
1988a7365c2SSuzuki K Poulose 
199d49c9cf1SJames Clark 	devm_kfree(&conn->dest_dev->dev, conn->link->target_name);
2008a7365c2SSuzuki K Poulose 	devm_kfree(&orig->dev, conn->link->orig_name);
2018a7365c2SSuzuki K Poulose 	devm_kfree(&orig->dev, conn->link);
2028a7365c2SSuzuki K Poulose 	conn->link = NULL;
2038a7365c2SSuzuki K Poulose }
204