Skip to content

Commit

Permalink
coresight: cti: Add device tree support for custom CTI
Browse files Browse the repository at this point in the history
Adds support for CTIs whose connections are implementation defined at
hardware design time, and not constrained by v8 architecture.

These CTIs have no standard connection setup, all the settings have to
be defined in the device tree files. The patch creates a set of connections
and trigger signals based on the information provided.

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-8-mathieu.poirier@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Mike Leach authored and Greg Kroah-Hartman committed Mar 21, 2020
1 parent cffd054 commit a561477
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 4 deletions.
235 changes: 231 additions & 4 deletions drivers/hwtracing/coresight/coresight-cti-platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,19 @@
#define NR_V8PE_OUT_SIGS 3
#define NR_V8ETM_INOUT_SIGS 4

/* CTI device tree trigger connection node keyword */
#define CTI_DT_CONNS "trig-conns"

/* CTI device tree connection property keywords */
#define CTI_DT_V8ARCH_COMPAT "arm,coresight-cti-v8-arch"
#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc"
#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs"
#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs"
#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types"
#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types"
#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters"
#define CTI_DT_CONN_NAME "arm,trig-conn-name"
#define CTI_DT_CTM_ID "arm,cti-ctm-id"

#ifdef CONFIG_OF
/*
Expand Down Expand Up @@ -87,6 +97,14 @@ cti_plat_get_csdev_or_node_name(struct fwnode_handle *fwnode,
return name;
}

static bool cti_plat_node_name_eq(struct fwnode_handle *fwnode,
const char *name)
{
if (is_of_node(fwnode))
return of_node_name_eq(to_of_node(fwnode), name);
return false;
}

static int cti_plat_create_v8_etm_connection(struct device *dev,
struct cti_drvdata *drvdata)
{
Expand Down Expand Up @@ -205,19 +223,228 @@ static int cti_plat_check_v8_arch_compatible(struct device *dev)
return 0;
}

static int cti_plat_count_sig_elements(const struct fwnode_handle *fwnode,
const char *name)
{
int nr_elem = fwnode_property_count_u32(fwnode, name);

return (nr_elem < 0 ? 0 : nr_elem);
}

static int cti_plat_read_trig_group(struct cti_trig_grp *tgrp,
const struct fwnode_handle *fwnode,
const char *grp_name)
{
int idx, err = 0;
u32 *values;

if (!tgrp->nr_sigs)
return 0;

values = kcalloc(tgrp->nr_sigs, sizeof(u32), GFP_KERNEL);
if (!values)
return -ENOMEM;

err = fwnode_property_read_u32_array(fwnode, grp_name,
values, tgrp->nr_sigs);

if (!err) {
/* set the signal usage mask */
for (idx = 0; idx < tgrp->nr_sigs; idx++)
tgrp->used_mask |= BIT(values[idx]);
}

kfree(values);
return err;
}

static int cti_plat_read_trig_types(struct cti_trig_grp *tgrp,
const struct fwnode_handle *fwnode,
const char *type_name)
{
int items, err = 0, nr_sigs;
u32 *values = NULL, i;

/* allocate an array according to number of signals in connection */
nr_sigs = tgrp->nr_sigs;
if (!nr_sigs)
return 0;

/* see if any types have been included in the device description */
items = cti_plat_count_sig_elements(fwnode, type_name);
if (items > nr_sigs)
return -EINVAL;

/* need an array to store the values iff there are any */
if (items) {
values = kcalloc(items, sizeof(u32), GFP_KERNEL);
if (!values)
return -ENOMEM;

err = fwnode_property_read_u32_array(fwnode, type_name,
values, items);
if (err)
goto read_trig_types_out;
}

/*
* Match type id to signal index, 1st type to 1st index etc.
* If fewer types than signals default remainder to GEN_IO.
*/
for (i = 0; i < nr_sigs; i++) {
if (i < items) {
tgrp->sig_types[i] =
values[i] < CTI_TRIG_MAX ? values[i] : GEN_IO;
} else {
tgrp->sig_types[i] = GEN_IO;
}
}

read_trig_types_out:
kfree(values);
return err;
}

static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata,
const struct fwnode_handle *fwnode)
{
struct cti_trig_grp *tg = NULL;
int err = 0, nr_filter_sigs;

nr_filter_sigs = cti_plat_count_sig_elements(fwnode,
CTI_DT_FILTER_OUT_SIGS);
if (nr_filter_sigs == 0)
return 0;

if (nr_filter_sigs > drvdata->config.nr_trig_max)
return -EINVAL;

tg = kzalloc(sizeof(*tg), GFP_KERNEL);
if (!tg)
return -ENOMEM;

err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS);
if (!err)
drvdata->config.trig_out_filter |= tg->used_mask;

kfree(tg);
return err;
}

static int cti_plat_create_connection(struct device *dev,
struct cti_drvdata *drvdata,
struct fwnode_handle *fwnode)
{
struct cti_trig_con *tc = NULL;
int cpuid = -1, err = 0;
struct fwnode_handle *cs_fwnode = NULL;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
int nr_sigs_in, nr_sigs_out;

/* look to see how many in and out signals we have */
nr_sigs_in = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGIN_SIGS);
nr_sigs_out = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGOUT_SIGS);

if ((nr_sigs_in > drvdata->config.nr_trig_max) ||
(nr_sigs_out > drvdata->config.nr_trig_max))
return -EINVAL;

tc = cti_allocate_trig_con(dev, nr_sigs_in, nr_sigs_out);
if (!tc)
return -ENOMEM;

/* look for the signals properties. */
err = cti_plat_read_trig_group(tc->con_in, fwnode,
CTI_DT_TRIGIN_SIGS);
if (err)
goto create_con_err;

err = cti_plat_read_trig_types(tc->con_in, fwnode,
CTI_DT_TRIGIN_TYPES);
if (err)
goto create_con_err;

err = cti_plat_read_trig_group(tc->con_out, fwnode,
CTI_DT_TRIGOUT_SIGS);
if (err)
goto create_con_err;

err = cti_plat_read_trig_types(tc->con_out, fwnode,
CTI_DT_TRIGOUT_TYPES);
if (err)
goto create_con_err;

err = cti_plat_process_filter_sigs(drvdata, fwnode);
if (err)
goto create_con_err;

/* read the connection name if set - may be overridden by later */
fwnode_property_read_string(fwnode, CTI_DT_CONN_NAME, &assoc_name);

/* associated cpu ? */
cpuid = cti_plat_get_cpu_at_node(fwnode);
if (cpuid >= 0) {
drvdata->ctidev.cpu = cpuid;
scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_fwnode = fwnode_find_reference(fwnode,
CTI_DT_CSDEV_ASSOC, 0);
if (!IS_ERR_OR_NULL(cs_fwnode)) {
assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode,
&csdev);
fwnode_handle_put(cs_fwnode);
}
}
/* set up a connection */
err = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name);

create_con_err:
return err;
}

static int cti_plat_create_impdef_connections(struct device *dev,
struct cti_drvdata *drvdata)
{
int rc = 0;
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct fwnode_handle *child = NULL;

if (IS_ERR_OR_NULL(fwnode))
return -EINVAL;

fwnode_for_each_child_node(fwnode, child) {
if (cti_plat_node_name_eq(child, CTI_DT_CONNS))
rc = cti_plat_create_connection(dev, drvdata,
child);
if (rc != 0)
break;
}
fwnode_handle_put(child);

return rc;
}

/* get the hardware configuration & connection data. */
int cti_plat_get_hw_data(struct device *dev,
struct cti_drvdata *drvdata)
{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;

/* get any CTM ID - defaults to 0 */
device_property_read_u32(dev, CTI_DT_CTM_ID, &cti_dev->ctm_id);

/* check for a v8 architectural CTI device */
if (cti_plat_check_v8_arch_compatible(dev)) {
if (cti_plat_check_v8_arch_compatible(dev))
rc = cti_plat_create_v8_connections(dev, drvdata);
if (rc)
return rc;
}
else
rc = cti_plat_create_impdef_connections(dev, drvdata);
if (rc)
return rc;

/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
Expand Down
10 changes: 10 additions & 0 deletions drivers/hwtracing/coresight/coresight-cti-sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,20 @@ static ssize_t powered_show(struct device *dev,
}
static DEVICE_ATTR_RO(powered);

static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);

return sprintf(buf, "%d\n", drvdata->ctidev.ctm_id);
}
static DEVICE_ATTR_RO(ctmid);

/* attribute and group sysfs tables. */
static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_powered.attr,
&dev_attr_ctmid.attr,
NULL,
};

Expand Down

0 comments on commit a561477

Please sign in to comment.