Skip to content

Commit

Permalink
coresight: cti: Enable CTI associated with devices
Browse files Browse the repository at this point in the history
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 coresight_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 associated
device element is used to enable CTI in conjunction with the path elements.

CTI devices 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>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/20200320165303.13681-9-mathieu.poirier@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
mikel-armbb authored and gregkh committed Mar 21, 2020
1 parent a561477 commit 177af82
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 5 deletions.
125 changes: 125 additions & 0 deletions drivers/hwtracing/coresight/coresight-cti.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Author: Mike Leach <mike.leach@linaro.org>
*/

#include <linux/property.h>
#include "coresight-cti.h"

/**
Expand Down Expand Up @@ -441,6 +442,127 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
return err;
}

/*
* Look for a matching connection device name in the list of connections.
* If found then swap in the csdev name, set trig con association pointer
* and return found.
*/
static bool
cti_match_fixup_csdev(struct cti_device *ctidev, const char *node_name,
struct coresight_device *csdev)
{
struct cti_trig_con *tc;

list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev_name) {
if (!strcmp(node_name, tc->con_dev_name)) {
/* match: so swap in csdev name & dev */
tc->con_dev_name = dev_name(&csdev->dev);
tc->con_dev = csdev;
return true;
}
}
}
return false;
}

/*
* 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.
* (called from coresight_register() with coresight_mutex locked).
*/
void cti_add_assoc_to_csdev(struct coresight_device *csdev)
{
struct cti_drvdata *ect_item;
struct cti_device *ctidev;
const char *node_name = NULL;

/* protect the list */
mutex_lock(&ect_mutex);

/* exit if current is an ECT device.*/
if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) || list_empty(&ect_net))
goto cti_add_done;

/* if we didn't find the csdev previously we used the fwnode name */
node_name = cti_plat_get_node_name(dev_fwnode(csdev->dev.parent));
if (!node_name)
goto cti_add_done;

/* for each CTI in list... */
list_for_each_entry(ect_item, &ect_net, node) {
ctidev = &ect_item->ctidev;
if (cti_match_fixup_csdev(ctidev, node_name, csdev)) {
/*
* if we found a matching csdev then update the ECT
* association pointer for the device with this CTI.
*/
csdev->ect_dev = ect_item->csdev;
break;
}
}
cti_add_done:
mutex_unlock(&ect_mutex);
}
EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);

/*
* Removing the associated devices is easier.
* A CTI will not have a value for csdev->ect_dev.
*/
void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
{
struct cti_drvdata *ctidrv;
struct cti_trig_con *tc;
struct cti_device *ctidev;

mutex_lock(&ect_mutex);
if (csdev->ect_dev) {
ctidrv = csdev_to_cti_drvdata(csdev->ect_dev);
ctidev = &ctidrv->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev == csdev->ect_dev) {
tc->con_dev = NULL;
break;
}
}
csdev->ect_dev = NULL;
}
mutex_unlock(&ect_mutex);
}
EXPORT_SYMBOL_GPL(cti_remove_assoc_from_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)
/* set tc->con_dev->ect_dev */
coresight_set_assoc_ectdev_mutex(tc->con_dev,
drvdata->csdev);
}
}

static void cti_remove_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) {
coresight_set_assoc_ectdev_mutex(tc->con_dev,
NULL);
}
}
}

/** cti ect operations **/
int cti_enable(struct coresight_device *csdev)
{
Expand Down Expand Up @@ -475,6 +597,7 @@ static void cti_device_release(struct device *dev)
struct cti_drvdata *ect_item, *ect_tmp;

mutex_lock(&ect_mutex);
cti_remove_conn_xrefs(drvdata);

/* remove from the list */
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, node) {
Expand Down Expand Up @@ -566,6 +689,8 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
list_add(&drvdata->node, &ect_net);
/* set any cross references */
cti_update_conn_xrefs(drvdata);
mutex_unlock(&ect_mutex);

/* set up release chain */
Expand Down
1 change: 1 addition & 0 deletions drivers/hwtracing/coresight/coresight-cti.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
u32 channel_idx);
struct coresight_platform_data *
coresight_cti_get_platform_data(struct device *dev);
const char *cti_plat_get_node_name(struct fwnode_handle *fwnode);

/* cti powered and enabled */
static inline bool cti_active(struct cti_config *cfg)
Expand Down
12 changes: 12 additions & 0 deletions drivers/hwtracing/coresight/coresight-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#endif

#ifdef CONFIG_CORESIGHT_CTI
extern void cti_add_assoc_to_csdev(struct coresight_device *csdev);
extern void cti_remove_assoc_from_csdev(struct coresight_device *csdev);

#else
static inline void cti_add_assoc_to_csdev(struct coresight_device *csdev) {}
static inline void
cti_remove_assoc_from_csdev(struct coresight_device *csdev) {}
#endif

/*
* Macros and inline functions to handle CoreSight UCI data and driver
* private data in AMBA ID table entries, and extract data values.
Expand Down Expand Up @@ -204,5 +214,7 @@ static inline void *coresight_get_uci_data(const struct amba_id *id)
void coresight_release_platform_data(struct coresight_platform_data *pdata);
struct coresight_device *
coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode);
void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
struct coresight_device *ect_csdev);

#endif
71 changes: 66 additions & 5 deletions drivers/hwtracing/coresight/coresight.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,44 @@ 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, bool 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);
} else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev);
}

/* 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;
}

/*
* Set the associated ect / cti device while holding the coresight_mutex
* to avoid a race with coresight_enable that may try to use this value.
*/
void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
struct coresight_device *ect_csdev)
{
mutex_lock(&coresight_mutex);
csdev->ect_dev = ect_csdev;
mutex_unlock(&coresight_mutex);
}

static int coresight_enable_sink(struct coresight_device *csdev,
u32 mode, void *data)
{
Expand All @@ -228,9 +266,14 @@ static int coresight_enable_sink(struct coresight_device *csdev,
if (!sink_ops(csdev)->enable)
return -EINVAL;

ret = sink_ops(csdev)->enable(csdev, mode, data);
ret = coresight_control_assoc_ectdev(csdev, true);
if (ret)
return ret;
ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret) {
coresight_control_assoc_ectdev(csdev, false);
return ret;
}
csdev->enable = true;

return 0;
Expand All @@ -246,6 +289,7 @@ static void coresight_disable_sink(struct coresight_device *csdev)
ret = sink_ops(csdev)->disable(csdev);
if (ret)
return;
coresight_control_assoc_ectdev(csdev, false);
csdev->enable = false;
}

Expand All @@ -269,8 +313,15 @@ static int coresight_enable_link(struct coresight_device *csdev,
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && outport < 0)
return outport;

if (link_ops(csdev)->enable)
ret = link_ops(csdev)->enable(csdev, inport, outport);
if (link_ops(csdev)->enable) {
ret = coresight_control_assoc_ectdev(csdev, true);
if (!ret) {
ret = link_ops(csdev)->enable(csdev, inport, outport);
if (ret)
coresight_control_assoc_ectdev(csdev, false);
}
}

if (!ret)
csdev->enable = true;

Expand Down Expand Up @@ -300,8 +351,10 @@ static void coresight_disable_link(struct coresight_device *csdev,
nr_conns = 1;
}

if (link_ops(csdev)->disable)
if (link_ops(csdev)->disable) {
link_ops(csdev)->disable(csdev, inport, outport);
coresight_control_assoc_ectdev(csdev, false);
}

for (i = 0; i < nr_conns; i++)
if (atomic_read(&csdev->refcnt[i]) != 0)
Expand All @@ -322,9 +375,14 @@ 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);
ret = coresight_control_assoc_ectdev(csdev, true);
if (ret)
return ret;
ret = source_ops(csdev)->enable(csdev, NULL, mode);
if (ret) {
coresight_control_assoc_ectdev(csdev, false);
return ret;
};
}
csdev->enable = true;
}
Expand All @@ -347,6 +405,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, false);
csdev->enable = false;
}
return !csdev->enable;
Expand Down Expand Up @@ -964,6 +1023,7 @@ static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);

cti_remove_assoc_from_csdev(csdev);
fwnode_handle_put(csdev->dev.fwnode);
kfree(csdev->refcnt);
kfree(csdev);
Expand Down Expand Up @@ -1246,6 +1306,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);

Expand Down
4 changes: 4 additions & 0 deletions include/linux/coresight.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,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_platform_data *pdata;
Expand All @@ -176,6 +178,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;
};

/*
Expand Down

0 comments on commit 177af82

Please sign in to comment.