linux/drivers/hwtracing/coresight/coresight-sysfs.c
<<
>>
Prefs
   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/kernel.h>
   9
  10#include "coresight-priv.h"
  11
  12/*
  13 * Connections group - links attribute.
  14 * Count of created links between coresight components in the group.
  15 */
  16static ssize_t nr_links_show(struct device *dev,
  17                             struct device_attribute *attr,
  18                             char *buf)
  19{
  20        struct coresight_device *csdev = to_coresight_device(dev);
  21
  22        return sprintf(buf, "%d\n", csdev->nr_links);
  23}
  24static DEVICE_ATTR_RO(nr_links);
  25
  26static struct attribute *coresight_conns_attrs[] = {
  27        &dev_attr_nr_links.attr,
  28        NULL,
  29};
  30
  31static struct attribute_group coresight_conns_group = {
  32        .attrs = coresight_conns_attrs,
  33        .name = "connections",
  34};
  35
  36/*
  37 * Create connections group for CoreSight devices.
  38 * This group will then be used to collate the sysfs links between
  39 * devices.
  40 */
  41int coresight_create_conns_sysfs_group(struct coresight_device *csdev)
  42{
  43        int ret = 0;
  44
  45        if (!csdev)
  46                return -EINVAL;
  47
  48        ret = sysfs_create_group(&csdev->dev.kobj, &coresight_conns_group);
  49        if (ret)
  50                return ret;
  51
  52        csdev->has_conns_grp = true;
  53        return ret;
  54}
  55
  56void coresight_remove_conns_sysfs_group(struct coresight_device *csdev)
  57{
  58        if (!csdev)
  59                return;
  60
  61        if (csdev->has_conns_grp) {
  62                sysfs_remove_group(&csdev->dev.kobj, &coresight_conns_group);
  63                csdev->has_conns_grp = false;
  64        }
  65}
  66
  67int coresight_add_sysfs_link(struct coresight_sysfs_link *info)
  68{
  69        int ret = 0;
  70
  71        if (!info)
  72                return -EINVAL;
  73        if (!info->orig || !info->target ||
  74            !info->orig_name || !info->target_name)
  75                return -EINVAL;
  76        if (!info->orig->has_conns_grp || !info->target->has_conns_grp)
  77                return -EINVAL;
  78
  79        /* first link orig->target */
  80        ret = sysfs_add_link_to_group(&info->orig->dev.kobj,
  81                                      coresight_conns_group.name,
  82                                      &info->target->dev.kobj,
  83                                      info->orig_name);
  84        if (ret)
  85                return ret;
  86
  87        /* second link target->orig */
  88        ret = sysfs_add_link_to_group(&info->target->dev.kobj,
  89                                      coresight_conns_group.name,
  90                                      &info->orig->dev.kobj,
  91                                      info->target_name);
  92
  93        /* error in second link - remove first - otherwise inc counts */
  94        if (ret) {
  95                sysfs_remove_link_from_group(&info->orig->dev.kobj,
  96                                             coresight_conns_group.name,
  97                                             info->orig_name);
  98        } else {
  99                info->orig->nr_links++;
 100                info->target->nr_links++;
 101        }
 102
 103        return ret;
 104}
 105
 106void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)
 107{
 108        if (!info)
 109                return;
 110        if (!info->orig || !info->target ||
 111            !info->orig_name || !info->target_name)
 112                return;
 113
 114        sysfs_remove_link_from_group(&info->orig->dev.kobj,
 115                                     coresight_conns_group.name,
 116                                     info->orig_name);
 117
 118        sysfs_remove_link_from_group(&info->target->dev.kobj,
 119                                     coresight_conns_group.name,
 120                                     info->target_name);
 121
 122        info->orig->nr_links--;
 123        info->target->nr_links--;
 124}
 125
 126/*
 127 * coresight_make_links: Make a link for a connection from a @orig
 128 * device to @target, represented by @conn.
 129 *
 130 *   e.g, for devOrig[output_X] -> devTarget[input_Y] is represented
 131 *   as two symbolic links :
 132 *
 133 *      /sys/.../devOrig/out:X  -> /sys/.../devTarget/
 134 *      /sys/.../devTarget/in:Y -> /sys/.../devOrig/
 135 *
 136 * The link names are allocated for a device where it appears. i.e, the
 137 * "out" link on the master and "in" link on the slave device.
 138 * The link info is stored in the connection record for avoiding
 139 * the reconstruction of names for removal.
 140 */
 141int coresight_make_links(struct coresight_device *orig,
 142                         struct coresight_connection *conn,
 143                         struct coresight_device *target)
 144{
 145        int ret = -ENOMEM;
 146        char *outs = NULL, *ins = NULL;
 147        struct coresight_sysfs_link *link = NULL;
 148
 149        do {
 150                outs = devm_kasprintf(&orig->dev, GFP_KERNEL,
 151                                      "out:%d", conn->outport);
 152                if (!outs)
 153                        break;
 154                ins = devm_kasprintf(&target->dev, GFP_KERNEL,
 155                                     "in:%d", conn->child_port);
 156                if (!ins)
 157                        break;
 158                link = devm_kzalloc(&orig->dev,
 159                                    sizeof(struct coresight_sysfs_link),
 160                                    GFP_KERNEL);
 161                if (!link)
 162                        break;
 163
 164                link->orig = orig;
 165                link->target = target;
 166                link->orig_name = outs;
 167                link->target_name = ins;
 168
 169                ret = coresight_add_sysfs_link(link);
 170                if (ret)
 171                        break;
 172
 173                conn->link = link;
 174
 175                /*
 176                 * Install the device connection. This also indicates that
 177                 * the links are operational on both ends.
 178                 */
 179                conn->child_dev = target;
 180                return 0;
 181        } while (0);
 182
 183        return ret;
 184}
 185
 186/*
 187 * coresight_remove_links: Remove the sysfs links for a given connection @conn,
 188 * from @orig device to @target device. See coresight_make_links() for more
 189 * details.
 190 */
 191void coresight_remove_links(struct coresight_device *orig,
 192                            struct coresight_connection *conn)
 193{
 194        if (!orig || !conn->link)
 195                return;
 196
 197        coresight_remove_sysfs_link(conn->link);
 198
 199        devm_kfree(&conn->child_dev->dev, conn->link->target_name);
 200        devm_kfree(&orig->dev, conn->link->orig_name);
 201        devm_kfree(&orig->dev, conn->link);
 202        conn->link = NULL;
 203        conn->child_dev = NULL;
 204}
 205