diff options
author | Mike Leach <mike.leach@linaro.org> | 2020-03-20 10:52:54 -0600 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-21 11:32:18 +0100 |
commit | b5213376c240d3d1614b60c1140d643b1602400c (patch) | |
tree | 2b6378df9b9c9d02ac392aa99b8edd8675a10750 /drivers/hwtracing/coresight/coresight-cti-sysfs.c | |
parent | 1a556ca6cc24ea598ac6a844a7fe0cc5b2701578 (diff) |
coresight: cti: Add sysfs access to program function registers
Adds in sysfs programming support for the CTI function register sets.
Allows direct manipulation of channel / trigger association registers.
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-4-mathieu.poirier@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-cti-sysfs.c')
-rw-r--r-- | drivers/hwtracing/coresight/coresight-cti-sysfs.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 378b435d9a8f..40d31d73b27c 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -120,6 +120,361 @@ static struct attribute *coresight_cti_mgmt_attrs[] = { NULL, }; +/* CTI low level programming registers */ + +/* + * Show a simple 32 bit value if enabled and powered. + * If inaccessible & pcached_val not NULL then show cached value. + */ +static ssize_t cti_reg32_show(struct device *dev, char *buf, + u32 *pcached_val, int reg_offset) +{ + u32 val = 0; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + if ((reg_offset >= 0) && cti_active(config)) { + CS_UNLOCK(drvdata->base); + val = readl_relaxed(drvdata->base + reg_offset); + if (pcached_val) + *pcached_val = val; + CS_LOCK(drvdata->base); + } else if (pcached_val) { + val = *pcached_val; + } + spin_unlock(&drvdata->spinlock); + return sprintf(buf, "%#x\n", val); +} + +/* + * Store a simple 32 bit value. + * If pcached_val 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 *pcached_val, int reg_offset) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* local store */ + if (pcached_val) + *pcached_val = (u32)val; + + /* write through if offset and enabled */ + if ((reg_offset >= 0) && cti_active(config)) + cti_write_single_reg(drvdata, reg_offset, val); + spin_unlock(&drvdata->spinlock); + return size; +} + +/* Standard macro for simple rw cti config registers */ +#define cti_config_reg32_rw(name, cfgname, offset) \ +static ssize_t name##_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.cfgname, offset); \ +} \ + \ +static ssize_t name##_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.cfgname, offset); \ +} \ +static DEVICE_ATTR_RW(name) + +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 sprintf(buf, "%d\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, 0, &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 sprintf(buf, "%#lx\n", 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, 0, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + index = config->ctiinout_sel; + config->ctiinen[index] = val; + + /* write through if enabled */ + if (cti_active(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 sprintf(buf, "%#lx\n", 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, 0, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + index = config->ctiinout_sel; + config->ctiouten[index] = val; + + /* write through if enabled */ + if (cti_active(config)) + cti_write_single_reg(drvdata, CTIOUTEN(index), val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(outen); + +static ssize_t intack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + cti_write_intack(dev, val); + return size; +} +static DEVICE_ATTR_WO(intack); + +cti_config_reg32_rw(gate, ctigate, CTIGATE); +cti_config_reg32_rw(asicctl, asicctl, ASICCTL); +cti_config_reg32_rw(appset, ctiappset, CTIAPPSET); + +static ssize_t appclear_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); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* a 1'b1 in appclr clears down the same bit in appset*/ + config->ctiappset &= ~val; + + /* write through if enabled */ + if (cti_active(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; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* write through if enabled */ + if (cti_active(config)) + cti_write_single_reg(drvdata, CTIAPPPULSE, val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(apppulse); + +coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); + +/* + * Define CONFIG_CORESIGHT_CTI_INTEGRATION_REGS to enable the access to the + * integration control registers. Normally only used to investigate connection + * data. + */ +#ifdef CONFIG_CORESIGHT_CTI_INTEGRATION_REGS + +/* macro to access RW registers with power check only (no enable check). */ +#define coresight_cti_reg_rw(name, offset) \ +static ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ + u32 val = 0; \ + pm_runtime_get_sync(dev->parent); \ + spin_lock(&drvdata->spinlock); \ + if (drvdata->config.hw_powered) \ + val = readl_relaxed(drvdata->base + offset); \ + spin_unlock(&drvdata->spinlock); \ + pm_runtime_put_sync(dev->parent); \ + return sprintf(buf, "0x%x\n", val); \ +} \ + \ +static ssize_t name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ + unsigned long val = 0; \ + if (kstrtoul(buf, 0, &val)) \ + return -EINVAL; \ + \ + pm_runtime_get_sync(dev->parent); \ + spin_lock(&drvdata->spinlock); \ + if (drvdata->config.hw_powered) \ + cti_write_single_reg(drvdata, offset, val); \ + spin_unlock(&drvdata->spinlock); \ + pm_runtime_put_sync(dev->parent); \ + return size; \ +} \ +static DEVICE_ATTR_RW(name) + +/* macro to access WO registers with power check only (no enable check). */ +#define coresight_cti_reg_wo(name, offset) \ +static ssize_t name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); \ + unsigned long val = 0; \ + if (kstrtoul(buf, 0, &val)) \ + return -EINVAL; \ + \ + pm_runtime_get_sync(dev->parent); \ + spin_lock(&drvdata->spinlock); \ + if (drvdata->config.hw_powered) \ + cti_write_single_reg(drvdata, offset, val); \ + spin_unlock(&drvdata->spinlock); \ + pm_runtime_put_sync(dev->parent); \ + return size; \ +} \ +static DEVICE_ATTR_WO(name) + +coresight_cti_reg_rw(itchout, ITCHOUT); +coresight_cti_reg_rw(ittrigout, ITTRIGOUT); +coresight_cti_reg_rw(itctrl, CORESIGHT_ITCTRL); +coresight_cti_reg_wo(itchinack, ITCHINACK); +coresight_cti_reg_wo(ittriginack, ITTRIGINACK); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK); + +#endif /* CORESIGHT_CTI_INTEGRATION_REGS */ + +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, +#ifdef CONFIG_CORESIGHT_CTI_INTEGRATION_REGS + &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, +#endif + NULL, +}; + +/* sysfs groups */ static const struct attribute_group coresight_cti_group = { .attrs = coresight_cti_attrs, }; @@ -129,8 +484,14 @@ static const struct attribute_group coresight_cti_mgmt_group = { .name = "mgmt", }; +static const struct attribute_group coresight_cti_regs_group = { + .attrs = coresight_cti_regs_attrs, + .name = "regs", +}; + const struct attribute_group *coresight_cti_groups[] = { &coresight_cti_group, &coresight_cti_mgmt_group, + &coresight_cti_regs_group, NULL, }; |