Hi Mathieu,
On Thu, 9 May 2019 at 20:36, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, May 01, 2019 at 09:49:34AM +0100, Mike Leach wrote:
Adds in required sysfs access for:-
- CoreSight management registers.
- CTI device specific registers.
- Channel Cross triggering API.
This provides the basic programming interface for the device.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../hwtracing/coresight/coresight-cti-sysfs.c | 831 +++++++++++++++++- drivers/hwtracing/coresight/coresight-cti.c | 188 +++- drivers/hwtracing/coresight/coresight-cti.h | 42 + drivers/hwtracing/coresight/coresight-priv.h | 2 + 4 files changed, 1053 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 9b6749621dcb..ff8dad02a779 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -34,8 +34,8 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) {
int enable_req;bool enabled, powered, cpuid;
int enable_req, cpuid;bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); ssize_t size = 0;@@ -46,12 +46,18 @@ static ssize_t enable_show(struct device *dev, cpuid = drvdata->ctidev.cpu; spin_unlock(&drvdata->spinlock);
if (powered) {size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",enabled ? "enabled" : "disabled", cpuid);} else if (cpuid >= 0) {size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",enable_req ? "enable req" : "disable req", cpuid);
if (cpuid >= 0) {if (powered) {size = scnprintf(buf, PAGE_SIZE,"cti %s; cpu%d powered;\n",enabled ? "enabled" : "disabled",cpuid);} else {size = scnprintf(buf, PAGE_SIZE,"cti %s; cpu%d unpowered;\n",enable_req ? "enable req" : "disable req",cpuid);} } else { size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", enabled ? "enabled" : "disabled");@@ -81,17 +87,826 @@ static ssize_t enable_store(struct device *dev, } static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return scnprintf(buf, PAGE_SIZE, "0x%x\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_ctmid.attr, NULL,};
+/* register based attributes */ +#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)+/*
- Show a simple 32 bit value. If pval is NULL then live read,
- otherwise read from supplied pointer only.
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)+{
unsigned long val = 0;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;spin_lock(&drvdata->spinlock);if (pval) {val = (unsigned long)*pval;} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {CS_UNLOCK(drvdata->base);val = readl_relaxed(drvdata->base + reg_offset);CS_LOCK(drvdata->base);}spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);+}
+/*
- Store a simple 32 bit value.
- If pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,int reg_offset)+{
unsigned long val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);/* local store */if (pval)*pval = (u32)val;/* write through of offset and enabled */if ((reg_offset >= 0) && CTI_PWR_ENA(config))cti_write_single_reg(drvdata, reg_offset, val);spin_unlock(&drvdata->spinlock);return size;+}
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,&dev_attr_devaff1.attr,&dev_attr_authstatus.attr,&dev_attr_devarch.attr,&dev_attr_devid.attr,&dev_attr_devtype.attr,&dev_attr_pidr0.attr,&dev_attr_pidr1.attr,&dev_attr_pidr2.attr,&dev_attr_pidr3.attr,&dev_attr_pidr4.attr,NULL,+};
+/* CTI low level programming registers */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
u32 val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);val = (u32)drvdata->config.ctiinout_sel;return scnprintf(buf, PAGE_SIZE, "%#x\n", val);+}
+static ssize_t inout_sel_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, 16, &val))return -EINVAL;if (val > (CTIINOUTEN_MAX-1))return -EINVAL;spin_lock(&drvdata->spinlock);drvdata->config.ctiinout_sel = val;spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);spin_lock(&drvdata->spinlock);index = drvdata->config.ctiinout_sel;val = drvdata->config.ctiinen[index];spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);index = config->ctiinout_sel;config->ctiinen[index] = val;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIINEN(index), val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);spin_lock(&drvdata->spinlock);index = drvdata->config.ctiinout_sel;val = drvdata->config.ctiouten[index];spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);index = config->ctiinout_sel;config->ctiouten[index] = val;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIOUTEN(index), val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_store(dev, buf, size,&drvdata->config.ctigate, CTIGATE);+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_store(dev, buf, size,&drvdata->config.asicctl, ASICCTL);+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;if (kstrtoul(buf, 16, &val))return -EINVAL;cti_write_intack(dev, val);return size;+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_store(dev, buf, size,&drvdata->config.ctiappset, CTIAPPSET);+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val, mask;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);/* a 1'b1 in appclr clears down the same bit in appset*/mask = ~val;config->ctiappset &= mask;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIAPPCLEAR, val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val, mask;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);/** a 1'b1 in apppulse sets then clears the bit,* effectively clears down the same bit in appset*/mask = ~val;config->ctiappset &= mask;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIAPPPULSE, val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,&dev_attr_inen.attr,&dev_attr_outen.attr,&dev_attr_gate.attr,&dev_attr_asicctl.attr,&dev_attr_intack.attr,&dev_attr_appset.attr,&dev_attr_appclear.attr,&dev_attr_apppulse.attr,&dev_attr_triginstatus.attr,&dev_attr_trigoutstatus.attr,&dev_attr_chinstatus.attr,&dev_attr_choutstatus.attr,&dev_attr_itctrl.attr,&dev_attr_ittrigin.attr,&dev_attr_itchin.attr,&dev_attr_ittrigout.attr,&dev_attr_itchout.attr,&dev_attr_itchoutack.attr,&dev_attr_ittrigoutack.attr,&dev_attr_ittriginack.attr,&dev_attr_itchinack.attr,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 = size;/* extract chan idx and trigger idx */items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);if (items) {err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);if (!err)err = size;} elseerr = -EINVAL;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 gate_enable_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = 0, channel = 0;if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);} else {if (kstrtoint(buf, 0, &channel))return -EINVAL;err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, channel);}return err ? err : size;+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = 0, channel = 0;if ((size >= 3) && (strncmp(buf, "all", 3) == 0)) {err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);} else {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(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)+{
unsigned long 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, "%ld (%s)\n", val,val ? "enabled" : "disabled");+}
+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, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);drvdata->config.trig_filter_enable = val ? 1 : 0;spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_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 = (0x1 << config->nr_ctm_channels) - 1;config->asicctl = 0;config->ctiappset = 0;config->ctiinout_sel = 0;confit->xtrig_rchan_sel = 0;
Agreed
/* 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(reset_xtrigs);
+static ssize_t show_chan_sel_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
u32 val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);val = (u32)drvdata->config.xtrig_rchan_sel;return scnprintf(buf, PAGE_SIZE, "%d\n", val);+}
+/* select a channel for the show_chan_xtrig function */ +static ssize_t show_chan_sel_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, 16, &val))I think this should be base 10.
yes.
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 DEVICE_ATTR_RW(show_chan_sel);
+/* for the selected channel - show the signals attached. */ +static ssize_t show_chan_xtrigs_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 = 0x1 << cfg->xtrig_rchan_sel;used += scnprintf(buf, buf_sz, "IN: ");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_RO(show_chan_xtrigs);
+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 used = 0, i;u32 inuse_bits = 0, chan_mask, chan_bit_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 = ((u32)(0x1 << config->nr_ctm_channels)) - 1;if (inuse_bits & chan_mask) {for (i = 0; i < config->nr_ctm_channels; i++) {chan_bit_mask = 0x1 << i;if (chan_bit_mask & inuse_bits) {used += scnprintf(buf+used, PAGE_SIZE-used,"%d ", i);}}used += scnprintf(buf+used, PAGE_SIZE-used, "\n");} else {used = scnprintf(buf, PAGE_SIZE, "none\n");}return used;+}
+static ssize_t list_inuse_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return print_chan_list(dev, buf, true);+} +static DEVICE_ATTR_RO(list_inuse);
+static ssize_t list_free_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return print_chan_list(dev, buf, false);+} +static DEVICE_ATTR_RO(list_free);
+static ssize_t list_gate_ena_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 chan_mask;int chan, used = 0;if (cfg->ctigate == 0) {used += scnprintf(buf, b_sz, "none\n");} else {for (chan = 0; chan < cfg->nr_ctm_channels; chan++) {chan_mask = 0x1 << chan;if (chan_mask & cfg->ctigate) {used += scnprintf(buf+used, b_sz-used, "%d ",chan);}}used += scnprintf(buf+used, b_sz-used, "\n");}return used;+} +static DEVICE_ATTR_RO(list_gate_ena);
+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_gate_enable.attr,&dev_attr_gate_disable.attr,&dev_attr_chan_set.attr,&dev_attr_chan_clear.attr,&dev_attr_chan_pulse.attr,&dev_attr_trig_filter_enable.attr,&dev_attr_reset_xtrigs.attr,&dev_attr_show_chan_sel.attr,&dev_attr_show_chan_xtrigs.attr,&dev_attr_list_inuse.attr,&dev_attr_list_free.attr,&dev_attr_list_gate_ena.attr,NULL,+};
+/* attribute and group sysfs tables. */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, };
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,.name = "mgmt",+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,.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 0a0fb6d48237..06b1f84795a4 100644 --- a/drivers/hwtracing/coresight/coresight-cti.c +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -69,7 +69,7 @@ static int cti_cpu_count; dev_get_drvdata(csdev->dev.parent)
/* write set of regs to hardware - call with spinlock claimed */ -static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +void cti_write_all_hw_regs(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; int i; @@ -173,6 +173,13 @@ static int cti_disable_hw(void *info) return 0; }
+void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);writel_relaxed(value, drvdata->base+offset);CS_LOCK(drvdata->base);+}
static void cti_set_default_config(struct cti_config *config) { /* Most regs default to 0 as zalloc'ed except...*/ @@ -285,6 +292,178 @@ int cti_add_default_connection(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;dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",op, direction, channel_idx, trigger_idx);/* ensure indexes in range */if ((channel_idx >= config->nr_ctm_channels) ||(trigger_idx >= config->nr_trig_max))return -EINVAL;trig_bitmask = 0x1 << 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) &&Do we need ->trig_filter_enable?
(config->trig_out_filter & trig_bitmask))Wouldn't it be sufficient to check ->trig_out_filter like you're doing here?
config->trig_out_filter is set from parameters in the .dts file - this defines signals that would be rather bad to set - e.g. EDBGREQ, so is read-only for the user. ->trig_filter_enable controls if this filtering takes place - it is on by default, but if someone really wants to halt a core in debug mode, they can switch it off and we then assume they know what they are doing.
return -EINVAL;}/* update the local register values */chan_bitmask = 0x1 << 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]);reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :reg_value & (~chan_bitmask);/* write local copy */if (direction == CTI_TRIG_IN)config->ctiinen[trigger_idx] = reg_value;elseconfig->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 = 0x1 << 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;case CTI_GATE_CHAN_ENABLE_ALL:reg_value = (0x1 << config->nr_ctm_channels) - 1;break;case CTI_GATE_CHAN_DISABLE_ALL:reg_value = 0x0;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 = 0x1 << 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;Should this be "|= chan_bitmask" ?
No - the effect of pulse is to raise the channel for one clock cycle then lower it. So if we pulse a channel, the net effect is the bit is cleared.
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;+}
+void cti_write_intack(struct device *dev, u32 ackval) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;spin_lock(&drvdata->spinlock);/* write if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIINTACK, ackval);spin_unlock(&drvdata->spinlock);+}
/** cti ect operations **/ int cti_enable(struct coresight_device *csdev, void *__unused) { @@ -432,7 +611,12 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
/* all done - dec pm refcount */ pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
if (drvdata->ctidev.cpu >= 0) {dev_info(dev, "%s: cti-CPU%d initialized\n", pdata->name,drvdata->ctidev.cpu);} else {dev_info(dev, "%s: cti-system initialized\n", pdata->name);} cti_count++; return 0;diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index c1af6cf9c2fe..a5bf59155f62 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -153,6 +153,7 @@ struct cti_device {
- @trig_out_filter: bitfield of out triggers that are blocked if filter
- enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- @xtrig_rchan_sel: channel ID when reading set xtrig info
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
@@ -174,6 +175,7 @@ struct cti_config { u32 trig_out_use; u32 trig_out_filter; int trig_filter_enable;
u8 xtrig_rchan_sel; /* cti cross trig programmable regs */ u32 ctiappset; u8 ctiinout_sel;@@ -202,11 +204,51 @@ struct cti_drvdata { struct cti_config config; };
+/*
- 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,CTI_GATE_CHAN_ENABLE_ALL,CTI_GATE_CHAN_DISABLE_ALL,+};
+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 cti_drvdata *drvdata); int cti_enable(struct coresight_device *csdev, void *__unused); int cti_disable(struct coresight_device *csdev, void *__unused); +void cti_write_all_hw_regs(struct cti_drvdata *drvdata); +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);+void cti_write_intack(struct device *dev, u32 ackval);
+/* cti powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
#ifdef CONFIG_OF extern int of_cti_get_hw_data(struct device *dev, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 8e9e5b6ad866..9b497e2f8725 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -23,10 +23,12 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
/*
- Coresight device CLAIM protocol.
- See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore.
-- 2.20.1
Thanks
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK