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");
        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) {
        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;
 }
 
        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;
 
                        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;
 
        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
                default:
                        break;
                }
+
+               /* Disable all helpers adjacent along the path last */
+               coresight_disable_helpers(csdev);
        }
 }
 
 }
 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;
                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
                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;
        }
                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;
                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);
        }
 }
        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;
 
        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) {
        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),
 
 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,
        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;
                if (rc) {
                        drvdata->etr_buf = NULL;
                        coresight_disclaim_device(drvdata->csdev);
-                       tmc_etr_disable_catu(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;