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:
parent
1a556ca6cc
commit
b5213376c2
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue