Subject: Re: [PATCH v5 04/14] coresight: cti: Add sysfs trigger / channel programming API

From: Mathieu Poirier <[email-masked]>
Date: Fri, 22 Nov 2019 11:40:56 -0700

On Tue, Nov 19, 2019 at 11:19:02PM +0000, Mike Leach wrote:
> Adds a user API to allow programming of CTI by trigger ID and
> channel number. This will take the channel and trigger ID supplied
> by the user and program the appropriate register values.
> 
> Signed-off-by: Mike Leach <[email-masked]>
> ---
>  .../hwtracing/coresight/coresight-cti-sysfs.c | 349 ++++++++++++++++++
>  drivers/hwtracing/coresight/coresight-cti.c   | 147 ++++++++
>  drivers/hwtracing/coresight/coresight-cti.h   |  32 ++
>  3 files changed, 528 insertions(+)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
> index 02d3ee0c1278..98de8a4768fc 100644
> --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c
> +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
> @@ -464,6 +464,349 @@ static struct attribute *coresight_cti_regs_attrs[] = {
>  	NULL,
>  };
>  
> +/* CTI channel x-trigger programming */
> +static int
> +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
> +		  enum cti_trig_dir dir, const char *buf, size_t size)
> +{
> +	u32 chan_idx;
> +	u32 trig_idx;
> +	int items, err = -EINVAL;
> +
> +	/* extract chan idx and trigger idx */
> +	items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
> +	if (items == 2) {
> +		err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
> +		if (!err)
> +			err = size;
> +	}
> +	return err;
> +}
> +
> +static ssize_t trigin_attach_store(struct device *dev,
> +				   struct device_attribute *attr,
> +				   const char *buf, size_t size)
> +{
> +	return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
> +				 buf, size);
> +}
> +static DEVICE_ATTR_WO(trigin_attach);
> +
> +static ssize_t trigin_detach_store(struct device *dev,
> +				   struct device_attribute *attr,
> +				   const char *buf, size_t size)
> +{
> +	return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
> +				 buf, size);
> +}
> +static DEVICE_ATTR_WO(trigin_detach);
> +
> +static ssize_t trigout_attach_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t size)
> +{
> +	return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
> +				 buf, size);
> +}
> +static DEVICE_ATTR_WO(trigout_attach);
> +
> +static ssize_t trigout_detach_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t size)
> +{
> +	return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
> +				 buf, size);
> +}
> +static DEVICE_ATTR_WO(trigout_detach);
> +
> +
> +static ssize_t chan_gate_enable_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t size)
> +{
> +	int err = 0, channel = 0;
> +
> +	if (kstrtoint(buf, 0, &channel))
> +		return -EINVAL;
> +
> +	err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel);
> +	return err ? err : size;
> +}
> +
> +static ssize_t chan_gate_enable_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;
> +	unsigned long ctigate_bitmask = cfg->ctigate;
> +	int size = 0;
> +
> +	if (cfg->ctigate == 0)
> +		size = scnprintf(buf, PAGE_SIZE, "\n");
> +	else
> +		size = bitmap_print_to_pagebuf(true, buf, &ctigate_bitmask,
> +					       cfg->nr_ctm_channels);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(chan_gate_enable);
> +
> +static ssize_t chan_gate_disable_store(struct device *dev,
> +				       struct device_attribute *attr,
> +				       const char *buf, size_t size)
> +{
> +	int err = 0, channel = 0;
> +
> +	if (kstrtoint(buf, 0, &channel))
> +		return -EINVAL;
> +
> +	err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, channel);
> +	return err ? err : size;
> +}
> +static DEVICE_ATTR_WO(chan_gate_disable);
> +
> +static int
> +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf)
> +{
> +	int err = 0, channel = 0;
> +
> +	if (kstrtoint(buf, 0, &channel))
> +		return -EINVAL;
> +
> +	err = cti_channel_setop(dev, op, channel);
> +	return err;
> +
> +}
> +
> +static ssize_t chan_set_store(struct device *dev,
> +			      struct device_attribute *attr,
> +			      const char *buf, size_t size)
> +{
> +	int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
> +
> +	return err ? err : size;
> +}
> +static DEVICE_ATTR_WO(chan_set);
> +
> +static ssize_t chan_clear_store(struct device *dev,
> +				struct device_attribute *attr,
> +				const char *buf, size_t size)
> +{
> +	int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
> +
> +	return err ? err : size;
> +}
> +static DEVICE_ATTR_WO(chan_clear);
> +
> +static ssize_t chan_pulse_store(struct device *dev,
> +				struct device_attribute *attr,
> +				const char *buf, size_t size)
> +{
> +	int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
> +
> +	return err ? err : size;
> +}
> +static DEVICE_ATTR_WO(chan_pulse);
> +
> +static ssize_t trig_filter_enable_show(struct device *dev,
> +				       struct device_attribute *attr,
> +				       char *buf)
> +{
> +	u32 val;
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	spin_lock(&drvdata->spinlock);
> +	val = drvdata->config.trig_filter_enable;
> +	spin_unlock(&drvdata->spinlock);
> +	return scnprintf(buf, PAGE_SIZE, "%d\n", val);
> +}
> +
> +static ssize_t trig_filter_enable_store(struct device *dev,
> +					struct device_attribute *attr,
> +					const char *buf, size_t size)
> +{
> +	unsigned long val;
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (kstrtoul(buf, 0, &val))
> +		return -EINVAL;
> +
> +	spin_lock(&drvdata->spinlock);
> +	drvdata->config.trig_filter_enable = !!val;
> +	spin_unlock(&drvdata->spinlock);
> +	return size;
> +}
> +static DEVICE_ATTR_RW(trig_filter_enable);
> +
> +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 size = 0, nr_trig_max = cfg->nr_trig_max;
> +	unsigned long mask = cfg->trig_out_filter;
> +
> +	if (mask)
> +		size = bitmap_print_to_pagebuf(true, buf, &mask, nr_trig_max);
> +	return size;
> +}
> +static DEVICE_ATTR_RO(trigout_filtered);
> +
> +/* clear all xtrigger / channel programming */
> +static ssize_t chan_xtrigs_reset_store(struct device *dev,
> +				       struct device_attribute *attr,
> +				       const char *buf, size_t size)
> +{
> +	int i;
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	struct cti_config *config = &drvdata->config;
> +
> +	spin_lock(&drvdata->spinlock);
> +
> +	/* clear the CTI trigger / channel programming registers */
> +	for (i = 0; i < config->nr_trig_max; i++) {
> +		config->ctiinen[i] = 0;
> +		config->ctiouten[i] = 0;
> +	}
> +
> +	/* clear the other regs */
> +	config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
> +	config->asicctl = 0;
> +	config->ctiappset = 0;
> +	config->ctiinout_sel = 0;
> +	config->xtrig_rchan_sel = 0;
> +
> +	/* if enabled then write through */
> +	if (CTI_PWR_ENA(config))
> +		cti_write_all_hw_regs(drvdata);
> +
> +	spin_unlock(&drvdata->spinlock);
> +	return size;
> +}
> +static DEVICE_ATTR_WO(chan_xtrigs_reset);
> +
> +/*
> + * Write to select a channel to view, read to display the
> + * cross triggers for the selected channel.
> + */
> +static ssize_t chan_xtrigs_view_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t size)
> +{
> +	unsigned long val;
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> +	if (kstrtoul(buf, 0, &val))
> +		return -EINVAL;
> +	if (val > (drvdata->config.nr_ctm_channels - 1))
> +		return -EINVAL;
> +
> +	spin_lock(&drvdata->spinlock);
> +	drvdata->config.xtrig_rchan_sel = val;
> +	spin_unlock(&drvdata->spinlock);
> +	return size;
> +}
> +
> +static ssize_t chan_xtrigs_view_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 used = 0, reg_idx;
> +	int buf_sz = PAGE_SIZE;
> +	u32 chan_mask = BIT(cfg->xtrig_rchan_sel);
> +
> +	used += scnprintf(buf, buf_sz, "[%d] IN: ", cfg->xtrig_rchan_sel);
> +	for (reg_idx = 0;
> +	     reg_idx < drvdata->config.nr_trig_max;
> +	     reg_idx++) {
> +		if (chan_mask & cfg->ctiinen[reg_idx]) {
> +			used += scnprintf(buf + used, buf_sz - used, "%d ",
> +					  reg_idx);
> +		}
> +	}
> +
> +	used += scnprintf(buf + used, buf_sz - used, "OUT: ");
> +	for (reg_idx = 0;
> +	     reg_idx < drvdata->config.nr_trig_max;
> +	     reg_idx++) {
> +		if (chan_mask & cfg->ctiouten[reg_idx]) {
> +			used += scnprintf(buf + used, buf_sz - used, "%d ",
> +					  reg_idx);
> +		}
> +	}
> +	used += scnprintf(buf + used, buf_sz - used, "\n");
> +	return used;
> +}
> +static DEVICE_ATTR_RW(chan_xtrigs_view);
> +
> +static ssize_t print_chan_list(struct device *dev,
> +			       char *buf, bool inuse)
> +{
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	struct cti_config *config = &drvdata->config;
> +	int size, i;
> +	unsigned long inuse_bits = 0, chan_mask;
> +
> +	/* scan regs to get bitmap of channels in use. */
> +	spin_lock(&drvdata->spinlock);
> +	for (i = 0; i < config->nr_trig_max; i++) {
> +		inuse_bits |= config->ctiinen[i];
> +		inuse_bits |= config->ctiouten[i];
> +	}
> +	spin_unlock(&drvdata->spinlock);
> +
> +	/* inverse bits if printing free channels */
> +	if (!inuse)
> +		inuse_bits = ~inuse_bits;
> +
> +	/* list of channels, or 'none' */
> +	chan_mask = GENMASK(config->nr_ctm_channels - 1, 0);
> +	if (inuse_bits & chan_mask)
> +		size = bitmap_print_to_pagebuf(true, buf, &inuse_bits,
> +					       config->nr_ctm_channels);
> +	else
> +		size = scnprintf(buf, PAGE_SIZE, "\n");
> +	return size;
> +}
> +
> +static ssize_t chan_inuse_show(struct device *dev,
> +			       struct device_attribute *attr,
> +			       char *buf)
> +{
> +	return print_chan_list(dev, buf, true);
> +}
> +static DEVICE_ATTR_RO(chan_inuse);
> +
> +static ssize_t chan_free_show(struct device *dev,
> +			      struct device_attribute *attr,
> +			      char *buf)
> +{
> +	return print_chan_list(dev, buf, false);
> +}
> +static DEVICE_ATTR_RO(chan_free);
> +
> +static struct attribute *coresight_cti_channel_attrs[] = {
> +	&dev_attr_trigin_attach.attr,
> +	&dev_attr_trigin_detach.attr,
> +	&dev_attr_trigout_attach.attr,
> +	&dev_attr_trigout_detach.attr,
> +	&dev_attr_trig_filter_enable.attr,
> +	&dev_attr_trigout_filtered.attr,
> +	&dev_attr_chan_gate_enable.attr,
> +	&dev_attr_chan_gate_disable.attr,
> +	&dev_attr_chan_set.attr,
> +	&dev_attr_chan_clear.attr,
> +	&dev_attr_chan_pulse.attr,
> +	&dev_attr_chan_inuse.attr,
> +	&dev_attr_chan_free.attr,
> +	&dev_attr_chan_xtrigs_view.attr,
> +	&dev_attr_chan_xtrigs_reset.attr,
> +	NULL,
> +};
> +
>  /* sysfs groups */
>  static const struct attribute_group coresight_cti_group = {
>  	.attrs = coresight_cti_attrs,
> @@ -479,9 +822,15 @@ static const struct attribute_group coresight_cti_regs_group = {
>  	.name = "regs",
>  };
>  
> +static const struct attribute_group coresight_cti_channels_group = {
> +	.attrs = coresight_cti_channel_attrs,
> +	.name = "channels",
> +};
> +
>  const struct attribute_group *coresight_cti_groups[] = {
>  	&coresight_cti_group,
>  	&coresight_cti_mgmt_group,
>  	&coresight_cti_regs_group,
> +	&coresight_cti_channels_group,
>  	NULL,
>  };
> diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c
> index b016b1e67fb1..369488dd7b8e 100644
> --- a/drivers/hwtracing/coresight/coresight-cti.c
> +++ b/drivers/hwtracing/coresight/coresight-cti.c
> @@ -293,6 +293,153 @@ int cti_add_default_connection(struct device *dev, struct cti_drvdata *drvdata)
>  	return ret;
>  }
>  
> +/** cti channel api **/
> +/* attach/detach channel from trigger - write through if enabled. */
> +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
> +			enum cti_trig_dir direction, u32 channel_idx,
> +			u32 trigger_idx)
> +{
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	struct cti_config *config = &drvdata->config;
> +	u32 trig_bitmask;
> +	u32 chan_bitmask;
> +	u32 reg_value;
> +	int reg_offset;
> +
> +	/* ensure indexes in range */
> +	if ((channel_idx >= config->nr_ctm_channels) ||
> +	   (trigger_idx >= config->nr_trig_max))
> +		return -EINVAL;
> +
> +	trig_bitmask = BIT(trigger_idx);
> +
> +	/* ensure registered triggers and not out filtered */
> +	if (direction == CTI_TRIG_IN)	{
> +		if (!(trig_bitmask & config->trig_in_use))
> +			return -EINVAL;
> +	} else {
> +		if (!(trig_bitmask & config->trig_out_use))
> +			return -EINVAL;
> +
> +		if ((config->trig_filter_enable) &&
> +		    (config->trig_out_filter & trig_bitmask))
> +			return -EINVAL;
> +	}
> +
> +	/* update the local register values */
> +	chan_bitmask = BIT(channel_idx);
> +	reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
> +		      CTIOUTEN(trigger_idx));
> +
> +	spin_lock(&drvdata->spinlock);
> +
> +	/* read - modify write - the trigger / channel enable value */
> +	reg_value = direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
> +		     config->ctiouten[trigger_idx];
> +	if (op == CTI_CHAN_ATTACH)
> +		reg_value |= chan_bitmask;
> +	else
> +		reg_value &= ~chan_bitmask;
> +
> +	/* write local copy */
> +	if (direction == CTI_TRIG_IN)
> +		config->ctiinen[trigger_idx] = reg_value;
> +	else
> +		config->ctiouten[trigger_idx] = reg_value;
> +
> +	/* write through if enabled */
> +	if (CTI_PWR_ENA(config))
> +		cti_write_single_reg(drvdata, reg_offset, reg_value);
> +	spin_unlock(&drvdata->spinlock);
> +	return 0;
> +}
> +
> +int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
> +			u32 channel_idx)
> +{
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	struct cti_config *config = &drvdata->config;
> +	u32 chan_bitmask;
> +	u32 reg_value;
> +	int err = 0;
> +
> +	if (channel_idx >= config->nr_ctm_channels)
> +		return -EINVAL;
> +
> +	chan_bitmask = BIT(channel_idx);
> +
> +	spin_lock(&drvdata->spinlock);
> +	reg_value = config->ctigate;
> +	switch (op) {
> +	case CTI_GATE_CHAN_ENABLE:
> +		reg_value |= chan_bitmask;
> +		break;
> +
> +	case CTI_GATE_CHAN_DISABLE:
> +		reg_value &= ~chan_bitmask;
> +		break;
> +
> +	default:
> +		err = -EINVAL;
> +		break;
> +	}
> +	if (err == 0) {
> +		config->ctigate = reg_value;
> +		if (CTI_PWR_ENA(config))
> +			cti_write_single_reg(drvdata, CTIGATE, reg_value);
> +	}
> +	spin_unlock(&drvdata->spinlock);
> +	return err;
> +}
> +
> +int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
> +		      u32 channel_idx)
> +{
> +	struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +	struct cti_config *config = &drvdata->config;
> +	u32 chan_bitmask;
> +	u32 reg_value;
> +	u32 reg_offset;
> +	int err = 0;
> +
> +	if (channel_idx >= config->nr_ctm_channels)
> +		return -EINVAL;
> +
> +	chan_bitmask = BIT(channel_idx);
> +
> +	spin_lock(&drvdata->spinlock);
> +	reg_value = config->ctiappset;
> +	switch (op) {
> +	case CTI_CHAN_SET:
> +		config->ctiappset |= chan_bitmask;
> +		reg_value  = config->ctiappset;
> +		reg_offset = CTIAPPSET;
> +		break;
> +
> +	case CTI_CHAN_CLR:
> +		config->ctiappset &= ~chan_bitmask;
> +		reg_value = chan_bitmask;
> +		reg_offset = CTIAPPCLEAR;
> +		break;
> +
> +	case CTI_CHAN_PULSE:
> +		config->ctiappset &= ~chan_bitmask;
> +		reg_value = chan_bitmask;
> +		reg_offset = CTIAPPPULSE;
> +		break;
> +
> +	default:
> +		err = -EINVAL;
> +		break;
> +	}
> +
> +	if ((err == 0) && CTI_PWR_ENA(config))
> +		cti_write_single_reg(drvdata, reg_offset, reg_value);
> +	spin_unlock(&drvdata->spinlock);
> +
> +	return err;
> +}
> +
>  /** cti ect operations **/
>  int cti_enable(struct coresight_device *csdev)
>  {
> diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h
> index 73869fa8b313..9a22f6fcad65 100644
> --- a/drivers/hwtracing/coresight/coresight-cti.h
> +++ b/drivers/hwtracing/coresight/coresight-cti.h
> @@ -168,6 +168,30 @@ struct cti_drvdata {
>  	void (*csdev_release)(struct device *dev);
>  };
>  
> +/*
> + * Channel operation types.
> + */
> +enum cti_chan_op {
> +	CTI_CHAN_ATTACH,
> +	CTI_CHAN_DETACH,
> +};
> +
> +enum cti_trig_dir {
> +	CTI_TRIG_IN,
> +	CTI_TRIG_OUT,
> +};
> +
> +enum cti_chan_gate_op {
> +	CTI_GATE_CHAN_ENABLE,
> +	CTI_GATE_CHAN_DISABLE,
> +};
> +
> +enum cti_chan_set_op {
> +	CTI_CHAN_SET,
> +	CTI_CHAN_CLR,
> +	CTI_CHAN_PULSE,
> +};
> +
>  /* private cti driver fns & vars */
>  extern const struct attribute_group *coresight_cti_groups[];
>  int cti_add_default_connection(struct device *dev,
> @@ -180,8 +204,16 @@ struct cti_trig_con *cti_allocate_trig_con(struct device *dev, int in_sigs,
>  					   int out_sigs);
>  int cti_enable(struct coresight_device *csdev);
>  int cti_disable(struct coresight_device *csdev);
> +void cti_write_all_hw_regs(struct cti_drvdata *drvdata);
>  void cti_write_intack(struct device *dev, u32 ackval);
>  void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
> +int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
> +			enum cti_trig_dir direction, u32 channel_idx,
> +			u32 trigger_idx);
> +int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
> +			u32 channel_idx);
> +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);

Reviewed-by: Mathieu Poirier <[email-masked]>

> -- 
> 2.17.1

_______________________________________________
linux-arm-kernel mailing list
[email-masked]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel