coresight: Enable and disable helper devices adjacent to the path
authorJames Clark <james.clark@arm.com>
Tue, 25 Apr 2023 14:35:39 +0000 (15:35 +0100)
committerSuzuki K Poulose <suzuki.poulose@arm.com>
Mon, 5 Jun 2023 14:46:47 +0000 (15:46 +0100)
Currently CATU is the only helper device, and its enable and disable
calls are hard coded. To allow more helper devices to be added in a
generic way, remove these hard coded calls and just enable and disable
all helper devices.

This has to apply to helpers adjacent to the path, because they will
never be in the path. CATU was already discovered in this way, so
there is no change there.

One change that is needed is for CATU to call back into ETR to allocate
the buffer. Because the enable call was previously hard coded, it was
done at a point where the buffer was already allocated, but this is no
longer the case.

Reviewed-by: Mike Leach <mike.leach@linaro.org>
Signed-off-by: James Clark <james.clark@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Link: https://lore.kernel.org/r/20230425143542.2305069-13-james.clark@arm.com
drivers/hwtracing/coresight/coresight-catu.c
drivers/hwtracing/coresight/coresight-core.c
drivers/hwtracing/coresight/coresight-etm-perf.c
drivers/hwtracing/coresight/coresight-priv.h
drivers/hwtracing/coresight/coresight-tmc-etr.c
include/linux/coresight.h

index bc90a03f478fd0364335ddadd32299ac23da7efe..3949ded0d4fa590b3422b109f9fa80346dcccb51 100644 (file)
@@ -395,13 +395,18 @@ static inline int catu_wait_for_ready(struct catu_drvdata *drvdata)
        return coresight_timeout(csa, CATU_STATUS, CATU_STATUS_READY, 1);
 }
 
-static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
+static int catu_enable_hw(struct catu_drvdata *drvdata, enum cs_mode cs_mode,
+                         void *data)
 {
        int rc;
        u32 control, mode;
-       struct etr_buf *etr_buf = data;
+       struct etr_buf *etr_buf = NULL;
        struct device *dev = &drvdata->csdev->dev;
        struct coresight_device *csdev = drvdata->csdev;
+       struct coresight_device *etrdev;
+       union coresight_dev_subtype etr_subtype = {
+               .sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM
+       };
 
        if (catu_wait_for_ready(drvdata))
                dev_warn(dev, "Timeout while waiting for READY\n");
@@ -416,6 +421,13 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
        if (rc)
                return rc;
 
+       etrdev = coresight_find_input_type(
+               csdev->pdata, CORESIGHT_DEV_TYPE_SINK, etr_subtype);
+       if (etrdev) {
+               etr_buf = tmc_etr_get_buffer(etrdev, cs_mode, data);
+               if (IS_ERR(etr_buf))
+                       return PTR_ERR(etr_buf);
+       }
        control |= BIT(CATU_CONTROL_ENABLE);
 
        if (etr_buf && etr_buf->mode == ETR_MODE_CATU) {
@@ -441,13 +453,14 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data)
        return 0;
 }
 
-static int catu_enable(struct coresight_device *csdev, void *data)
+static int catu_enable(struct coresight_device *csdev, enum cs_mode mode,
+                      void *data)
 {
        int rc;
        struct catu_drvdata *catu_drvdata = csdev_to_catu_drvdata(csdev);
 
        CS_UNLOCK(catu_drvdata->base);
-       rc = catu_enable_hw(catu_drvdata, data);
+       rc = catu_enable_hw(catu_drvdata, mode, data);
        CS_LOCK(catu_drvdata->base);
        return rc;
 }
index 939b7fb751b57a47ad53730ea26b96b5a5fcddbf..1e9a596a15bcfce597db2d703bfef91bfbd2601c 100644 (file)
@@ -403,8 +403,8 @@ static void coresight_disable_link(struct coresight_device *csdev,
        csdev->enable = false;
 }
 
-static int coresight_enable_source(struct coresight_device *csdev,
-                                  enum cs_mode mode)
+int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
+                           void *data)
 {
        int ret;
 
@@ -413,7 +413,7 @@ static int coresight_enable_source(struct coresight_device *csdev,
                        ret = coresight_control_assoc_ectdev(csdev, true);
                        if (ret)
                                return ret;
-                       ret = source_ops(csdev)->enable(csdev, NULL, mode);
+                       ret = source_ops(csdev)->enable(csdev, data, mode);
                        if (ret) {
                                coresight_control_assoc_ectdev(csdev, false);
                                return ret;
@@ -426,25 +426,75 @@ static int coresight_enable_source(struct coresight_device *csdev,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(coresight_enable_source);
+
+static bool coresight_is_helper(struct coresight_device *csdev)
+{
+       return csdev->type == CORESIGHT_DEV_TYPE_HELPER;
+}
+
+static int coresight_enable_helper(struct coresight_device *csdev,
+                                  enum cs_mode mode, void *data)
+{
+       int ret;
+
+       if (!helper_ops(csdev)->enable)
+               return 0;
+       ret = helper_ops(csdev)->enable(csdev, mode, data);
+       if (ret)
+               return ret;
+
+       csdev->enable = true;
+       return 0;
+}
+
+static void coresight_disable_helper(struct coresight_device *csdev)
+{
+       int ret;
+
+       if (!helper_ops(csdev)->disable)
+               return;
+
+       ret = helper_ops(csdev)->disable(csdev, NULL);
+       if (ret)
+               return;
+       csdev->enable = false;
+}
+
+static void coresight_disable_helpers(struct coresight_device *csdev)
+{
+       int i;
+       struct coresight_device *helper;
+
+       for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
+               helper = csdev->pdata->out_conns[i]->dest_dev;
+               if (helper && coresight_is_helper(helper))
+                       coresight_disable_helper(helper);
+       }
+}
 
 /**
  *  coresight_disable_source - Drop the reference count by 1 and disable
  *  the device if there are no users left.
  *
  *  @csdev: The coresight device to disable
+ *  @data: Opaque data to pass on to the disable function of the source device.
+ *         For example in perf mode this is a pointer to the struct perf_event.
  *
  *  Returns true if the device has been disabled.
  */
-static bool coresight_disable_source(struct coresight_device *csdev)
+bool coresight_disable_source(struct coresight_device *csdev, void *data)
 {
        if (atomic_dec_return(&csdev->refcnt) == 0) {
                if (source_ops(csdev)->disable)
-                       source_ops(csdev)->disable(csdev, NULL);
+                       source_ops(csdev)->disable(csdev, data);
                coresight_control_assoc_ectdev(csdev, false);
+               coresight_disable_helpers(csdev);
                csdev->enable = false;
        }
        return !csdev->enable;
 }
+EXPORT_SYMBOL_GPL(coresight_disable_source);
 
 /*
  * coresight_disable_path_from : Disable components in the given path beyond
@@ -495,6 +545,9 @@ static void coresight_disable_path_from(struct list_head *path,
                default:
                        break;
                }
+
+               /* Disable all helpers adjacent along the path last */
+               coresight_disable_helpers(csdev);
        }
 }
 
@@ -504,9 +557,28 @@ void coresight_disable_path(struct list_head *path)
 }
 EXPORT_SYMBOL_GPL(coresight_disable_path);
 
-int coresight_enable_path(struct list_head *path, enum cs_mode mode, void *sink_data)
+static int coresight_enable_helpers(struct coresight_device *csdev,
+                                   enum cs_mode mode, void *data)
 {
+       int i, ret = 0;
+       struct coresight_device *helper;
+
+       for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
+               helper = csdev->pdata->out_conns[i]->dest_dev;
+               if (!helper || !coresight_is_helper(helper))
+                       continue;
+
+               ret = coresight_enable_helper(helper, mode, data);
+               if (ret)
+                       return ret;
+       }
 
+       return 0;
+}
+
+int coresight_enable_path(struct list_head *path, enum cs_mode mode,
+                         void *sink_data)
+{
        int ret = 0;
        u32 type;
        struct coresight_node *nd;
@@ -516,6 +588,10 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, void *sink_
                csdev = nd->csdev;
                type = csdev->type;
 
+               /* Enable all helpers adjacent to the path first */
+               ret = coresight_enable_helpers(csdev, mode, sink_data);
+               if (ret)
+                       goto err;
                /*
                 * ETF devices are tricky... They can be a link or a sink,
                 * depending on how they are configured.  If an ETF has been
@@ -710,7 +786,7 @@ static int coresight_grab_device(struct coresight_device *csdev)
                struct coresight_device *child;
 
                child = csdev->pdata->out_conns[i]->dest_dev;
-               if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
+               if (child && coresight_is_helper(child))
                        if (!coresight_get_ref(child))
                                goto err;
        }
@@ -721,7 +797,7 @@ err:
                struct coresight_device *child;
 
                child = csdev->pdata->out_conns[i]->dest_dev;
-               if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
+               if (child && coresight_is_helper(child))
                        coresight_put_ref(child);
        }
        return -ENODEV;
@@ -740,7 +816,7 @@ static void coresight_drop_device(struct coresight_device *csdev)
                struct coresight_device *child;
 
                child = csdev->pdata->out_conns[i]->dest_dev;
-               if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
+               if (child && coresight_is_helper(child))
                        coresight_put_ref(child);
        }
 }
@@ -1102,7 +1178,7 @@ int coresight_enable(struct coresight_device *csdev)
        if (ret)
                goto err_path;
 
-       ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
+       ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL);
        if (ret)
                goto err_source;
 
@@ -1159,7 +1235,7 @@ void coresight_disable(struct coresight_device *csdev)
        if (ret)
                goto out;
 
-       if (!csdev->enable || !coresight_disable_source(csdev))
+       if (!csdev->enable || !coresight_disable_source(csdev, NULL))
                goto out;
 
        switch (csdev->subtype.source_subtype) {
@@ -1644,6 +1720,69 @@ static inline int coresight_search_device_idx(struct coresight_dev_list *dict,
        return -ENOENT;
 }
 
+static bool coresight_compare_type(enum coresight_dev_type type_a,
+                                  union coresight_dev_subtype subtype_a,
+                                  enum coresight_dev_type type_b,
+                                  union coresight_dev_subtype subtype_b)
+{
+       if (type_a != type_b)
+               return false;
+
+       switch (type_a) {
+       case CORESIGHT_DEV_TYPE_SINK:
+               return subtype_a.sink_subtype == subtype_b.sink_subtype;
+       case CORESIGHT_DEV_TYPE_LINK:
+               return subtype_a.link_subtype == subtype_b.link_subtype;
+       case CORESIGHT_DEV_TYPE_LINKSINK:
+               return subtype_a.link_subtype == subtype_b.link_subtype &&
+                      subtype_a.sink_subtype == subtype_b.sink_subtype;
+       case CORESIGHT_DEV_TYPE_SOURCE:
+               return subtype_a.source_subtype == subtype_b.source_subtype;
+       case CORESIGHT_DEV_TYPE_HELPER:
+               return subtype_a.helper_subtype == subtype_b.helper_subtype;
+       default:
+               return false;
+       }
+}
+
+struct coresight_device *
+coresight_find_input_type(struct coresight_platform_data *pdata,
+                         enum coresight_dev_type type,
+                         union coresight_dev_subtype subtype)
+{
+       int i;
+       struct coresight_connection *conn;
+
+       for (i = 0; i < pdata->nr_inconns; ++i) {
+               conn = pdata->in_conns[i];
+               if (conn &&
+                   coresight_compare_type(type, subtype, conn->src_dev->type,
+                                          conn->src_dev->subtype))
+                       return conn->src_dev;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(coresight_find_input_type);
+
+struct coresight_device *
+coresight_find_output_type(struct coresight_platform_data *pdata,
+                          enum coresight_dev_type type,
+                          union coresight_dev_subtype subtype)
+{
+       int i;
+       struct coresight_connection *conn;
+
+       for (i = 0; i < pdata->nr_outconns; ++i) {
+               conn = pdata->out_conns[i];
+               if (conn->dest_dev &&
+                   coresight_compare_type(type, subtype, conn->dest_dev->type,
+                                          conn->dest_dev->subtype))
+                       return conn->dest_dev;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(coresight_find_output_type);
+
 bool coresight_loses_context_with_cpu(struct device *dev)
 {
        return fwnode_property_present(dev_fwnode(dev),
index 89e8ed214ea4967620c20ad1c1a1e39f9d068400..5ca6278baff4fa70ff6856458dce44beeecc6339 100644 (file)
@@ -493,7 +493,7 @@ static void etm_event_start(struct perf_event *event, int flags)
                goto fail_end_stop;
 
        /* Finally enable the tracer */
-       if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
+       if (coresight_enable_source(csdev, CS_MODE_PERF, event))
                goto fail_disable_path;
 
        /*
@@ -587,7 +587,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
                return;
 
        /* stop tracer */
-       source_ops(csdev)->disable(csdev, event);
+       coresight_disable_source(csdev, event);
 
        /* tell the core */
        event->hw.state = PERF_HES_STOPPED;
index 65ae6d161c57158ac943cba17bb00515fee7186d..5575014f73e0ceb4ec7f42e4cb9e5df2c8d95399 100644 (file)
@@ -216,5 +216,8 @@ void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
 
 void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
 struct coresight_device *coresight_get_percpu_sink(int cpu);
+int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
+                           void *data);
+bool coresight_disable_source(struct coresight_device *csdev, void *data);
 
 #endif
index 7993398bdcce7012c5de8ed9103aee27acd75225..766325de0e29bdb0b83a332a1195149eadc284c1 100644 (file)
@@ -775,40 +775,19 @@ static const struct etr_buf_operations etr_sg_buf_ops = {
 struct coresight_device *
 tmc_etr_get_catu_device(struct tmc_drvdata *drvdata)
 {
-       int i;
-       struct coresight_device *tmp, *etr = drvdata->csdev;
+       struct coresight_device *etr = drvdata->csdev;
+       union coresight_dev_subtype catu_subtype = {
+               .helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CATU
+       };
 
        if (!IS_ENABLED(CONFIG_CORESIGHT_CATU))
                return NULL;
 
-       for (i = 0; i < etr->pdata->nr_outconns; i++) {
-               tmp = etr->pdata->out_conns[i]->dest_dev;
-               if (tmp && coresight_is_catu_device(tmp))
-                       return tmp;
-       }
-
-       return NULL;
+       return coresight_find_output_type(etr->pdata, CORESIGHT_DEV_TYPE_HELPER,
+                                         catu_subtype);
 }
 EXPORT_SYMBOL_GPL(tmc_etr_get_catu_device);
 
-static inline int tmc_etr_enable_catu(struct tmc_drvdata *drvdata,
-                                     struct etr_buf *etr_buf)
-{
-       struct coresight_device *catu = tmc_etr_get_catu_device(drvdata);
-
-       if (catu && helper_ops(catu)->enable)
-               return helper_ops(catu)->enable(catu, etr_buf);
-       return 0;
-}
-
-static inline void tmc_etr_disable_catu(struct tmc_drvdata *drvdata)
-{
-       struct coresight_device *catu = tmc_etr_get_catu_device(drvdata);
-
-       if (catu && helper_ops(catu)->disable)
-               helper_ops(catu)->disable(catu, drvdata->etr_buf);
-}
-
 static const struct etr_buf_operations *etr_buf_ops[] = {
        [ETR_MODE_FLAT] = &etr_flat_buf_ops,
        [ETR_MODE_ETR_SG] = &etr_sg_buf_ops,
@@ -1058,13 +1037,6 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
        if (WARN_ON(drvdata->etr_buf))
                return -EBUSY;
 
-       /*
-        * If this ETR is connected to a CATU, enable it before we turn
-        * this on.
-        */
-       rc = tmc_etr_enable_catu(drvdata, etr_buf);
-       if (rc)
-               return rc;
        rc = coresight_claim_device(drvdata->csdev);
        if (!rc) {
                drvdata->etr_buf = etr_buf;
@@ -1072,7 +1044,6 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata,
                if (rc) {
                        drvdata->etr_buf = NULL;
                        coresight_disclaim_device(drvdata->csdev);
-                       tmc_etr_disable_catu(drvdata);
                }
        }
 
@@ -1162,8 +1133,6 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
 {
        __tmc_etr_disable_hw(drvdata);
-       /* Disable CATU device if this ETR is connected to one */
-       tmc_etr_disable_catu(drvdata);
        coresight_disclaim_device(drvdata->csdev);
        /* Reset the ETR buf used by hardware */
        drvdata->etr_buf = NULL;
index b97edd24f3eceb18da2871fae31b3457679f6c59..61dfbab5fa9852c7d8b3056c2e2bba6fe3322a91 100644 (file)
@@ -375,7 +375,8 @@ struct coresight_ops_source {
  * @disable    : Disable the device
  */
 struct coresight_ops_helper {
-       int (*enable)(struct coresight_device *csdev, void *data);
+       int (*enable)(struct coresight_device *csdev, enum cs_mode mode,
+                     void *data);
        int (*disable)(struct coresight_device *csdev, void *data);
 };
 
@@ -646,5 +647,13 @@ coresight_add_out_conn(struct device *dev,
                       struct coresight_platform_data *pdata,
                       const struct coresight_connection *new_conn);
 int coresight_add_in_conn(struct coresight_connection *conn);
+struct coresight_device *
+coresight_find_input_type(struct coresight_platform_data *pdata,
+                         enum coresight_dev_type type,
+                         union coresight_dev_subtype subtype);
+struct coresight_device *
+coresight_find_output_type(struct coresight_platform_data *pdata,
+                          enum coresight_dev_type type,
+                          union coresight_dev_subtype subtype);
 
 #endif         /* _LINUX_COREISGHT_H */