From 6148652807ba89b0c9af05ebed3e005b626f90eb Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 25 Apr 2023 15:35:39 +0100 Subject: [PATCH] coresight: Enable and disable helper devices adjacent to the path 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 Signed-off-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20230425143542.2305069-13-james.clark@arm.com --- drivers/hwtracing/coresight/coresight-catu.c | 21 ++- drivers/hwtracing/coresight/coresight-core.c | 161 ++++++++++++++++-- .../hwtracing/coresight/coresight-etm-perf.c | 4 +- drivers/hwtracing/coresight/coresight-priv.h | 3 + .../hwtracing/coresight/coresight-tmc-etr.c | 43 +---- include/linux/coresight.h | 11 +- 6 files changed, 188 insertions(+), 55 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index bc90a03f478f..3949ded0d4fa 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -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; } diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 939b7fb751b5..1e9a596a15bc 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -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), diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 89e8ed214ea4..5ca6278baff4 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -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; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 65ae6d161c57..5575014f73e0 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -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 diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 7993398bdcce..766325de0e29 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -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; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index b97edd24f3ec..61dfbab5fa98 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -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 */ -- 2.20.1