The CoreSight subsystem enables a path of devices from source to sink. Any CTI devices associated with the path devices must be enabled at the same time.
This patch adds an associated device element to the main coresight device structure, and uses this to create associations between the CTI and other devices based on the device tree data. The assoc dev element is used to enable CTI in conjunction with the path elements. CTI are reference counted so where a single CTI is associated with multiple elements on the path, it will be enabled on the first associated device enable, and disabled with the last associated device disable.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.c | 90 ++++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + drivers/hwtracing/coresight/coresight.c | 39 ++++++++- include/linux/coresight.h | 4 + 4 files changed, 135 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c index 32d9bf0bf0b9..6d452ca3725c 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -464,6 +464,93 @@ void cti_write_intack(struct device *dev, u32 ackval) spin_unlock(&drvdata->spinlock); }
+/* + * Look for a matching connection device name in the list of + * connections. If found then swap in the csdev name and return + * found. + */ +static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name, + const char *csdev_name) +{ + int found = 0; + struct cti_trig_con *trig_con; + + list_for_each_entry(trig_con, &ctidev->trig_cons, node) { + if (trig_con->con_dev_name) { + if (!strcmp(node_name, trig_con->con_dev_name)) { + found = 1; + /* match: so swap in csdev name */ + kfree(trig_con->con_dev_name); + trig_con->con_dev_name = + kstrdup(csdev_name, GFP_KERNEL); + goto cti_con_name_match; + } + } + } +cti_con_name_match: + return found; +} + +/* + * Search the cti list to add an associated CTI into the supplied CS device + * This will set the association if CTI declared before the CS device + */ +int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ + struct ect_node *ect_item; + struct cti_device *ctidev; + const char *node_name = NULL, *csdev_name; + + /* exit early for no CTIs or current is an ECT device.*/ + if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) + return 0; + + if (!csdev->dev.parent->of_node) + return 0; + + node_name = csdev->dev.parent->of_node->full_name; + + if (!node_name) + return 0; + + csdev_name = dev_name(&csdev->dev); + + /* for each CTI in list... */ + mutex_lock(&ect_mutex); + list_for_each_entry(ect_item, &ect_net, next) { + ctidev = &ect_item->cti_drv->ctidev; + if (cti_match_con_name(ctidev, node_name, csdev_name)) { + /* + * if we found a matching name then update the + * association pointers. + */ + csdev->ect_dev = ect_item->cti_drv->csdev; + goto cti_found; + } + } +cti_found: + mutex_unlock(&ect_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev); + +/* + * Update the cross references where the associated device was found + * while we were building the connection info. This will occur if the + * assoc device was registered before the CTI. + */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{ + struct cti_trig_con *tc; + struct cti_device *ctidev = &drvdata->ctidev; + + list_for_each_entry(tc, &ctidev->trig_cons, node) { + if (tc->con_dev) + tc->con_dev->ect_dev = drvdata->csdev; + } +} + /** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -609,6 +696,9 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id) } mutex_unlock(&ect_mutex);
+ /* set any cross references */ + cti_update_conn_xrefs(drvdata); + /* all done - dec pm refcount */ pm_runtime_put(&adev->dev); if (drvdata->ctidev.cpu >= 0) { diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 9b497e2f8725..b1fca5c4bdb2 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -165,8 +165,11 @@ static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#ifdef CONFIG_CORESIGHT_CTI extern void cti_device_release(struct device *dev); +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); #else static inline void cti_device_release(struct device *dev) {}; +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } #endif
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8db3d1a5314f..9d56b3b32172 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -216,6 +216,31 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); }
+/* enable or disable an associated CTI device of the supplied CS device */ +static int +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{ + int ect_ret = 0; + struct coresight_device *ect_csdev = csdev->ect_dev; + + if (!ect_csdev) + return 0; + + if (enable) { + if (ect_ops(ect_csdev)->enable) + ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL); + } else { + if (ect_ops(ect_csdev)->disable) + ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL); + } + + /* output warning if ECT enable is preventing trace operation */ + if (ect_ret) + dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n", + dev_name(&ect_csdev->dev), enable ? "enable" : "disable"); + return ect_ret; +} + static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +252,9 @@ static int coresight_enable_sink(struct coresight_device *csdev, */ if (sink_ops(csdev)->enable) { ret = sink_ops(csdev)->enable(csdev, mode, data); + if (ret) + return ret; + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret; csdev->enable = true; @@ -242,6 +270,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev); + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } } @@ -275,6 +304,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport); + if (!ret) + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) { atomic_dec(&csdev->refcnt[refport]); return ret; @@ -314,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, }
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) { - if (link_ops(csdev)->disable) + if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport); + coresight_control_assoc_ectdev(csdev, 0); + } }
for (i = 0; i < nr_conns; i++) @@ -338,6 +371,8 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { ret = source_ops(csdev)->enable(csdev, NULL, mode); + if (!ret) + ret = coresight_control_assoc_ectdev(csdev, 1); if (ret) return ret; } @@ -362,6 +397,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL); + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } return !csdev->enable; @@ -1238,6 +1274,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev); + cti_add_assoc_to_csdev(csdev);
mutex_unlock(&coresight_mutex);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d1c1ed17d2ca..50be479c35d1 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -167,6 +167,8 @@ struct coresight_connection { * activated but not yet enabled. Enabling for a _sink_ * appens when a source has been selected for that it. * @ea: Device attribute for sink representation under PMU directory. + * @ect_dev: Associated cross trigger device. Not part of the trace data + * path or connections. */ struct coresight_device { struct coresight_connection *conns; @@ -182,6 +184,8 @@ struct coresight_device { /* sink specific fields */ bool activated; /* true only if a sink is part of a path */ struct dev_ext_attribute *ea; + /* cross trigger handling */ + struct coresight_device *ect_dev; };
#define to_coresight_device(d) container_of(d, struct coresight_device, dev)