coresight: cti: Add sysfs access to program function registers
authorMike Leach <mike.leach@linaro.org>
Fri, 20 Mar 2020 16:52:54 +0000 (10:52 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 21 Mar 2020 10:32:18 +0000 (11:32 +0100)
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>
drivers/hwtracing/coresight/Kconfig
drivers/hwtracing/coresight/coresight-cti-sysfs.c
drivers/hwtracing/coresight/coresight-cti.c
drivers/hwtracing/coresight/coresight-cti.h

index 45d3822..83e841b 100644 (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
index 378b435..40d31d7 100644 (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,
 };
index c71b72d..e0748cc 100644 (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.
index d0ac90f..35eb77b 100644 (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 */