[RFC PATCH v2 06/13] drivers: coresight: cti: Add connection information to sysfs

Mike Leach mike.leach at linaro.org
Wed May 1 08:49:38 UTC 2019


Dynamically adds sysfs attributes for all connections defined in the CTI.

Each connection has _name, _trgin_type, _trgout_type, _trgin_sig and
_trgout_sig, preceded by a connection index (e.g. 0_name is the name of
connection 0).

Additionally each device has a nr_cons and trigout_filtered parameter.
This allows clients to explore the connection and trigger signal details
without needing to refer to device tree or specification of the device.

Standardised type information is provided for certain common functions -
e.g. snk_full for a trigger from a sink indicating full. Otherwise type
defaults to genio.

Signed-off-by: Mike Leach <mike.leach at linaro.org>
---
 .../hwtracing/coresight/coresight-cti-sysfs.c | 338 ++++++++++++++++++
 drivers/hwtracing/coresight/coresight-cti.c   |  11 +
 drivers/hwtracing/coresight/coresight-cti.h   |   4 +
 3 files changed, 353 insertions(+)

diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
index ff8dad02a779..e4b23e291114 100644
--- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
@@ -883,6 +883,343 @@ static struct attribute *coresight_cti_channel_attrs[] = {
 	NULL,
 };
 
+/* Create the connections group attrs dynamically */
+
+/* group is static - .attrs will be built dynamically */
+static struct attribute_group coresight_cti_conns_group = {
+	.name = "connections",
+};
+
+/*
+ *Each connection has dynamic name, trigin/out sigs/types attrs,
+ * + each device has static nr conns + filter attr
+ */
+
+static ssize_t trigout_filtered_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct cti_config *cfg = &drvdata->config;
+	int b_sz = PAGE_SIZE;
+	u32 sig_mask;
+	int sig_idx, used = 0, nr_trig_max = cfg->nr_trig_max;
+
+	if (cfg->trig_out_filter) {
+		sig_mask = 0x1;
+		for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
+			if (sig_mask & cfg->trig_out_filter) {
+				used += scnprintf(buf+used, b_sz-used, "%d ",
+						 sig_idx);
+			}
+			sig_mask <<= 1;
+		}
+		used += scnprintf(buf+used, b_sz-used, "\n");
+	} else
+		used += scnprintf(buf, b_sz, "none\n");
+	return used;
+}
+static DEVICE_ATTR_RO(trigout_filtered);
+
+static ssize_t nr_cons_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->ctidev.nr_trig_con);
+}
+static DEVICE_ATTR_RO(nr_cons);
+
+/* static attrs - 2  +  null terminator */
+#define CTI_SYSFS_CONS_ST_ATTR		3
+/* dynamic attr - per connection */
+#define CTI_SYSFS_CONS_DYN_ATTR	5
+
+static ssize_t con_name_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
+	struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", con->con_dev_name);
+}
+
+static ssize_t trigin_sig_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
+	struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct cti_config *cfg = &drvdata->config;
+
+	u32 sig_mask;
+	int sig_idx, used = 0, b_sz = PAGE_SIZE;
+
+	sig_mask = 0x1;
+	for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
+		if (sig_mask & con->con_in->used_mask) {
+			used += scnprintf(buf+used, b_sz-used, "%d ",
+					  sig_idx);
+		}
+		sig_mask <<= 1;
+	}
+	used += scnprintf(buf+used, b_sz-used, "\n");
+	return used;
+}
+
+static ssize_t trigout_sig_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
+	struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	struct cti_config *cfg = &drvdata->config;
+
+	u32 sig_mask;
+	int sig_idx, used = 0, b_sz = PAGE_SIZE;
+
+	sig_mask = 0x1;
+	for (sig_idx = 0; sig_idx < cfg->nr_trig_max; sig_idx++) {
+		if (sig_mask & con->con_out->used_mask) {
+			used += scnprintf(buf+used, b_sz-used, "%d ",
+					  sig_idx);
+		}
+		sig_mask <<= 1;
+	}
+	used += scnprintf(buf+used, b_sz-used, "\n");
+	return used;
+}
+
+/* convert a sig type id to a name */
+static const char *
+cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in)
+{
+	static const char * const sig_type_names[] = {
+		"genio", "intreq", "intack", "haltreq", "restartreq",
+		"pe_edbgreq", "pe_dbgrestart", "pe_ctiirq", "pe_pmuirq",
+		"pe_dbgtrigger", "etm_extout",  "etm_extin", "snk_full",
+		"snk_acqcomp", "snk_flushcomp", "snk_flushin", "snk_trigin",
+		"stm_asyncout", "stm_tout_spte", "stm_tout_sw", "stm_tout_hete",
+		"stm_hwevent", "ela_tstart", "ela_tstop", "ela_dbgreq",
+	};
+	int idx = 0;
+	struct cti_trig_grp *grp = in ? con->con_in : con->con_out;
+
+	if (grp->sig_types) {
+		if (used_count < grp->nr_sigs)
+			idx = grp->sig_types[used_count];
+	}
+	return sig_type_names[idx];
+}
+
+static ssize_t trigin_type_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
+	struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+
+	int sig_idx, used = 0, b_sz = PAGE_SIZE;
+	const char *name;
+
+	for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) {
+		name = cti_sig_type_name(con, sig_idx, true);
+		used += scnprintf(buf+used, b_sz-used, "%s ", name);
+	}
+	used += scnprintf(buf+used, b_sz-used, "\n");
+	return used;
+}
+
+static ssize_t trigout_type_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct dev_ext_attribute *ext_attr = (struct dev_ext_attribute *)attr;
+	struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+
+	int sig_idx, used = 0, b_sz = PAGE_SIZE;
+	const char *name;
+
+	for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) {
+		name = cti_sig_type_name(con, sig_idx, false);
+		used += scnprintf(buf+used, b_sz-used, "%s ", name);
+	}
+	used += scnprintf(buf+used, b_sz-used, "\n");
+	return used;
+}
+
+typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr,
+			     char *buf);
+
+enum cti_conn_attr_type {
+	CTI_CON_ATTR_NAME,
+	CTI_CON_ATTR_TRIGIN_SIG,
+	CTI_CON_ATTR_TRIGOUT_SIG,
+	CTI_CON_ATTR_TRIGIN_TYPES,
+	CTI_CON_ATTR_TRIGOUT_TYPES
+};
+
+static p_show_fn show_fns[] = {
+	con_name_show,
+	trigin_sig_show,
+	trigout_sig_show,
+	trigin_type_show,
+	trigout_type_show,
+};
+
+static const char * const base_names[] = {
+	"_name",
+	"_trgin_sig",
+	"_trgout_sig",
+	"_trgin_type",
+	"_trgout_type",
+};
+
+struct dev_ext_attribute *
+cti_create_con_sysfs_attr(int con_idx, enum cti_conn_attr_type attr_type)
+{
+	struct dev_ext_attribute *dev_ext_attr = 0;
+	char *name = 0;
+
+	dev_ext_attr = kzalloc(sizeof(struct dev_ext_attribute), GFP_KERNEL);
+	if (dev_ext_attr) {
+		name = kzalloc(sizeof(char) * 16, GFP_KERNEL);
+		if (name) {
+			/* fill out the underlying attribute struct */
+			sprintf(name, "%d%s", con_idx, base_names[attr_type]);
+			dev_ext_attr->attr.attr.name = name;
+			dev_ext_attr->attr.attr.mode = 0444;
+
+			/* now the device_attribute struct */
+			dev_ext_attr->attr.show = show_fns[attr_type];
+		} else {
+			kfree(dev_ext_attr);
+			dev_ext_attr = 0;
+		}
+	}
+	return dev_ext_attr;
+}
+
+int cti_create_con_attr_set(int con_idx, int *next_attr_idx,
+			    struct cti_device *ctidev,
+			    struct cti_trig_con *con)
+{
+	struct dev_ext_attribute *dev_ext_attr;
+	int attr_idx = *next_attr_idx;
+
+	dev_ext_attr = cti_create_con_sysfs_attr(con_idx, CTI_CON_ATTR_NAME);
+	if (!dev_ext_attr)
+		goto create_con_attr_err;
+	dev_ext_attr->var = con;
+	ctidev->con_attrs[attr_idx++] = dev_ext_attr;
+
+	if (con->con_in->nr_sigs > 0) {
+		dev_ext_attr =
+			cti_create_con_sysfs_attr(con_idx,
+						  CTI_CON_ATTR_TRIGIN_SIG);
+		if (!dev_ext_attr)
+			goto create_con_attr_err;
+		dev_ext_attr->var = con;
+		ctidev->con_attrs[attr_idx++] = dev_ext_attr;
+
+		dev_ext_attr =
+			cti_create_con_sysfs_attr(con_idx,
+						  CTI_CON_ATTR_TRIGIN_TYPES);
+		if (!dev_ext_attr)
+			goto create_con_attr_err;
+		dev_ext_attr->var = con;
+		ctidev->con_attrs[attr_idx++] = dev_ext_attr;
+
+	}
+
+	if (con->con_in->nr_sigs > 0) {
+
+		dev_ext_attr =
+			cti_create_con_sysfs_attr(con_idx,
+						  CTI_CON_ATTR_TRIGOUT_SIG);
+		if (!dev_ext_attr)
+			goto create_con_attr_err;
+		dev_ext_attr->var = con;
+		ctidev->con_attrs[attr_idx++] = dev_ext_attr;
+
+		dev_ext_attr =
+			cti_create_con_sysfs_attr(con_idx,
+						  CTI_CON_ATTR_TRIGOUT_TYPES);
+		if (!dev_ext_attr)
+			goto create_con_attr_err;
+		dev_ext_attr->var = con;
+		ctidev->con_attrs[attr_idx++] = dev_ext_attr;
+	}
+	*next_attr_idx = attr_idx;
+	return 0;
+
+create_con_attr_err:
+	cti_destroy_cons_sysfs(ctidev);
+	return -ENOMEM;
+}
+
+int cti_create_cons_sysfs(struct cti_drvdata *drvdata)
+{
+	struct cti_device *ctidev = &drvdata->ctidev;
+	int err = 0, n_attrs = 0, con_idx = 0;
+	int next_attr_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
+	struct cti_trig_con *tc;
+
+	n_attrs = CTI_SYSFS_CONS_ST_ATTR +
+		(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
+	ctidev->con_attrs =
+		kcalloc(n_attrs, sizeof(struct dev_ext_attribute *),
+			GFP_KERNEL);
+	if (ctidev->con_attrs != 0) {
+		ctidev->con_attrs[0] =	(struct dev_ext_attribute *)
+			&dev_attr_trigout_filtered.attr;
+		ctidev->con_attrs[1] = (struct dev_ext_attribute *)
+			&dev_attr_nr_cons.attr;
+
+		/* add dynamic set for each connection */
+		list_for_each_entry(tc,  &ctidev->trig_cons, node) {
+			err = cti_create_con_attr_set(con_idx++,
+						      &next_attr_idx,
+						      ctidev,
+						      tc);
+			/* if create con fails it will free all prior cons */
+			if (err)
+				return err;
+		}
+		coresight_cti_conns_group.attrs =
+			(struct attribute **)ctidev->con_attrs;
+	} else
+		err = -ENOMEM;
+	return err;
+}
+
+void cti_destroy_cons_sysfs(struct cti_device *ctidev)
+{
+	int n_attrs = CTI_SYSFS_CONS_ST_ATTR +
+		(CTI_SYSFS_CONS_DYN_ATTR * ctidev->nr_trig_con);
+	int dyn_idx = CTI_SYSFS_CONS_ST_ATTR - 1;
+	bool eol = false;
+	struct dev_ext_attribute *dev_ext_attr = 0;
+
+	if (ctidev->con_attrs) {
+		while (!eol && (dyn_idx < n_attrs)) {
+			dev_ext_attr = ctidev->con_attrs[dyn_idx];
+			if (dev_ext_attr) {
+				kfree(dev_ext_attr->attr.attr.name);
+				kfree(dev_ext_attr);
+			} else
+				eol = true;
+			dyn_idx++;
+		}
+		kfree(ctidev->con_attrs);
+		ctidev->con_attrs = 0;
+	}
+}
+
 /* attribute and group sysfs tables. */
 static const struct attribute_group coresight_cti_group = {
 	.attrs = coresight_cti_attrs,
@@ -908,5 +1245,6 @@ const struct attribute_group *coresight_cti_groups[] = {
 	&coresight_cti_mgmt_group,
 	&coresight_cti_regs_group,
 	&coresight_cti_channels_group,
+	&coresight_cti_conns_group,
 	NULL,
 };
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c
index 6d452ca3725c..e0192fd50804 100644
--- a/drivers/hwtracing/coresight/coresight-cti.c
+++ b/drivers/hwtracing/coresight/coresight-cti.c
@@ -671,6 +671,14 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
 	/* setup cpu related CTI devices, otherwise assume powered */
 	drvdata->config.hw_powered = true;
 
+	/* create dynamic attributes for connections */
+	ret = cti_create_cons_sysfs(drvdata);
+	if (ret) {
+		pr_err("%s: create dynamic sysfs entries failed\n",
+		       pdata->name);
+		goto err_out;
+	}
+
 	/* set up coresight component description */
 	cti_desc.pdata = pdata;
 	cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
@@ -746,6 +754,9 @@ void cti_device_release(struct device *dev)
 	if (drvdata->ctidev.cpu >= 0)
 		cti_cpu_drv[drvdata->ctidev.cpu] = 0;
 
+	/* clear the dynamic sysfs associate with connections */
+	cti_destroy_cons_sysfs(&drvdata->ctidev);
+
 	/* clear the connection list items */
 	cti_free_conn_info(drvdata);
 
diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h
index 7342f998f4c5..2a3557bfcd16 100644
--- a/drivers/hwtracing/coresight/coresight-cti.h
+++ b/drivers/hwtracing/coresight/coresight-cti.h
@@ -125,6 +125,7 @@ struct cti_trig_con {
  *          assumed there is a single CTM per SoC, ID 0).
  * @trig_cons: list of connections to this device.
  * @cpu: CPU ID if associated with CPU, -1 otherwise.
+ * @con_attrs: Dymanic sysfs attributes created to display connection info.
  *
  */
 struct cti_device {
@@ -132,6 +133,7 @@ struct cti_device {
 	u32			ctm_id;
 	struct list_head	trig_cons;
 	int			cpu;
+	struct dev_ext_attribute **con_attrs;
 };
 
 /**
@@ -252,6 +254,8 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
 int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
 		      u32 channel_idx);
 void cti_write_intack(struct device *dev, u32 ackval);
+int cti_create_cons_sysfs(struct cti_drvdata *drvdata);
+void cti_destroy_cons_sysfs(struct cti_device *ctidev);
 
 /* cti powered and enabled */
 #define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
-- 
2.20.1



More information about the CoreSight mailing list