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>
This commit is contained in:
Mike Leach 2020-03-20 10:52:54 -06:00 committed by Greg Kroah-Hartman
parent 1a556ca6cc
commit b5213376c2
4 changed files with 397 additions and 0 deletions

View File

@ -122,4 +122,13 @@ config CORESIGHT_CTI
halt compared to disabling sources and sinks normally in driver
software.
config CORESIGHT_CTI_INTEGRATION_REGS
bool "Access CTI CoreSight Integration Registers"
depends on CORESIGHT_CTI
help
This option adds support for the CoreSight integration registers on
this device. The integration registers allow the exploration of the
CTI trigger connections between this and other devices.These
registers are not used in normal operation and can leave devices in
an inconsistent state.
endif

View File

@ -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,
};

View File

@ -149,6 +149,25 @@ cti_not_disabled:
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);
}
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_active(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
spin_unlock(&drvdata->spinlock);
}
/*
* Look at the HW DEVID register for some of the HW settings.
* DEVID[15:8] - max number of in / out triggers.

View File

@ -180,7 +180,15 @@ 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_intack(struct device *dev, u32 ackval);
void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value);
struct coresight_platform_data *
coresight_cti_get_platform_data(struct device *dev);
/* cti powered and enabled */
static inline bool cti_active(struct cti_config *cfg)
{
return cfg->hw_powered && cfg->hw_enabled;
}
#endif /* _CORESIGHT_CORESIGHT_CTI_H */