Versatile Express System Config (syscfg) interface, usually
part of the System Registers (sysreg) block, can be used
to generate transactions on the platform configuration bus.

The driver registers itself as a bridge in the VE config
core and provides a regmap abstraction for the components
functions.

Signed-off-by: Pawel Moll <[email protected]>
---
 arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts |   5 +-
 arch/arm/mach-vexpress/ct-ca9x4.c          |   8 +-
 arch/arm/mach-vexpress/v2m.c               |  35 ++--
 drivers/misc/Kconfig                       |   9 +
 drivers/misc/Makefile                      |   1 +
 drivers/misc/vexpress-syscfg.c             | 322 +++++++++++++++++++++++++++++
 include/linux/vexpress.h                   |  11 +-
 7 files changed, 356 insertions(+), 35 deletions(-)
 create mode 100644 drivers/misc/vexpress-syscfg.c

diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts 
b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index 15f98cb..a25c262 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -312,6 +312,7 @@
                        arm,vexpress-sysreg,func = <12 0>;
                        label = "A15 Pcore";
                };
+
                power@1 {
                        /* Total power for the three A7 cores */
                        compatible = "arm,vexpress-power";
@@ -322,14 +323,14 @@
                energy@0 {
                        /* Total energy for the two A15 cores */
                        compatible = "arm,vexpress-energy";
-                       arm,vexpress-sysreg,func = <13 0>;
+                       arm,vexpress-sysreg,func = <13 0>, <13 1>;
                        label = "A15 Jcore";
                };
 
                energy@2 {
                        /* Total energy for the three A7 cores */
                        compatible = "arm,vexpress-energy";
-                       arm,vexpress-sysreg,func = <13 2>;
+                       arm,vexpress-sysreg,func = <13 2>, <13 3>;
                        label = "A7 Jcore";
                };
        };
diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c 
b/arch/arm/mach-vexpress/ct-ca9x4.c
index 6f34497..0df4bf6 100644
--- a/arch/arm/mach-vexpress/ct-ca9x4.c
+++ b/arch/arm/mach-vexpress/ct-ca9x4.c
@@ -130,11 +130,7 @@ static struct platform_device pmu_device = {
 
 static struct platform_device osc1_device = {
        .name           = "vexpress-osc",
-       .id             = 1,
-       .num_resources  = 1,
-       .resource       = (struct resource []) {
-               VEXPRESS_RES_FUNC(0xf, 1),
-       },
+       .id             = -1,
 };
 
 static void __init ct_ca9x4_init(void)
@@ -156,6 +152,8 @@ static void __init ct_ca9x4_init(void)
 
        platform_device_register(&pmu_device);
        platform_device_register(&osc1_device);
+       devm_vexpress_syscfg_regmap_init(&osc1_device.dev,
+                       vexpress_config_get_master(), 0, 0, 1, 1);
 
        WARN_ON(clk_register_clkdev(vexpress_osc_setup(&osc1_device.dev),
                        NULL, "ct:clcd"));
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 5437956..325ae06 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -222,38 +222,22 @@ static struct platform_device v2m_sysreg_device = {
 
 static struct platform_device v2m_muxfpga_device = {
        .name           = "vexpress-muxfpga",
-       .id             = 0,
-       .num_resources  = 1,
-       .resource       = (struct resource []) {
-               VEXPRESS_RES_FUNC(0, 7),
-       }
+       .id             = -1,
 };
 
 static struct platform_device v2m_shutdown_device = {
        .name           = "vexpress-shutdown",
-       .id             = 0,
-       .num_resources  = 1,
-       .resource       = (struct resource []) {
-               VEXPRESS_RES_FUNC(0, 8),
-       }
+       .id             = -1,
 };
 
 static struct platform_device v2m_reboot_device = {
        .name           = "vexpress-reboot",
-       .id             = 0,
-       .num_resources  = 1,
-       .resource       = (struct resource []) {
-               VEXPRESS_RES_FUNC(0, 9),
-       }
+       .id             = -1,
 };
 
 static struct platform_device v2m_dvimode_device = {
        .name           = "vexpress-dvimode",
-       .id             = 0,
-       .num_resources  = 1,
-       .resource       = (struct resource []) {
-               VEXPRESS_RES_FUNC(0, 11),
-       }
+       .id             = -1,
 };
 
 static AMBA_APB_DEVICE(aaci,  "mb:aaci",  0, V2M_AACI, IRQ_V2M_AACI, NULL);
@@ -340,12 +324,21 @@ static void __init v2m_init(void)
        regulator_register_fixed(0, v2m_eth_supplies,
                        ARRAY_SIZE(v2m_eth_supplies));
 
+       platform_device_register(&v2m_sysreg_device);
+
        platform_device_register(&v2m_muxfpga_device);
+       devm_vexpress_syscfg_regmap_init(&v2m_muxfpga_device.dev,
+                       VEXPRESS_SITE_MB, 0, 0, 7, 0);
        platform_device_register(&v2m_shutdown_device);
+       devm_vexpress_syscfg_regmap_init(&v2m_shutdown_device.dev,
+                       VEXPRESS_SITE_MB, 0, 0, 8, 0);
        platform_device_register(&v2m_reboot_device);
+       devm_vexpress_syscfg_regmap_init(&v2m_reboot_device.dev,
+                       VEXPRESS_SITE_MB, 0, 0, 9, 0);
        platform_device_register(&v2m_dvimode_device);
+       devm_vexpress_syscfg_regmap_init(&v2m_dvimode_device.dev,
+                       VEXPRESS_SITE_MB, 0, 0, 11, 0);
 
-       platform_device_register(&v2m_sysreg_device);
        platform_device_register(&v2m_pcie_i2c_device);
        platform_device_register(&v2m_ddc_i2c_device);
        platform_device_register(&v2m_flash_device);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a3e291d..7c53770 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,15 @@ config SRAM
          the genalloc API. It is supposed to be used for small on-chip SRAM
          areas found on many SoCs.
 
+config VEXPRESS_SYSCFG
+       bool "Versatile Express System Configuration driver"
+       depends on VEXPRESS_CONFIG
+       default y
+       help
+         ARM Ltd. Versatile Express uses specialised platform configuration
+         bus. System Configuration registers are one of the possible means
+         of generating transactions on this bus.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f45473e..ca3338d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_VMWARE_VMCI)     += vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)      += lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)             += sram.o
 obj-y                          += mic/
+obj-$(CONFIG_VEXPRESS_SYSCFG)  += vexpress-syscfg.o
diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c
new file mode 100644
index 0000000..6c4632d
--- /dev/null
+++ b/drivers/misc/vexpress-syscfg.c
@@ -0,0 +1,322 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <linux/vexpress.h>
+
+
+#define SYS_CFGDATA            0x0
+
+#define SYS_CFGCTRL            0x4
+#define SYS_CFGCTRL_START      (1 << 31)
+#define SYS_CFGCTRL_WRITE      (1 << 30)
+#define SYS_CFGCTRL_DCC(n)     (((n) & 0xf) << 26)
+#define SYS_CFGCTRL_FUNC(n)    (((n) & 0x3f) << 20)
+#define SYS_CFGCTRL_SITE(n)    (((n) & 0x3) << 16)
+#define SYS_CFGCTRL_POSITION(n)        (((n) & 0xf) << 12)
+#define SYS_CFGCTRL_DEVICE(n)  (((n) & 0xfff) << 0)
+
+#define SYS_CFGSTAT            0x8
+#define SYS_CFGSTAT_ERR                (1 << 1)
+#define SYS_CFGSTAT_COMPLETE   (1 << 0)
+
+
+struct vexpress_syscfg {
+       struct device *dev;
+       void __iomem *base;
+};
+
+struct vexpress_syscfg_reg {
+       struct vexpress_syscfg *syscfg;
+       int regs_num;
+       u32 templates[0];
+};
+
+
+static int vexpress_syscfg_delay_schedule(unsigned int us)
+{
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(usecs_to_jiffies(us));
+       if (signal_pending(current))
+               return -EINTR;
+
+       return 0;
+}
+
+static int vexpress_syscfg_delay_loop(unsigned int us)
+{
+       unsigned long long timeout_ns = sched_clock() + us * 1000;
+
+       while (time_before64(sched_clock(), timeout_ns))
+               cpu_relax();
+
+       return 0;
+}
+
+static int (*vexpress_syscfg_delay)(unsigned int us) =
+               vexpress_syscfg_delay_schedule;
+
+static void vexpress_syscfg_shutdown(void)
+{
+       /* Can't relay on the scheduler when the system is going down */
+       vexpress_syscfg_delay = vexpress_syscfg_delay_loop;
+}
+
+static struct syscore_ops vexpress_syscfg_syscore_ops = {
+       .shutdown = vexpress_syscfg_shutdown,
+};
+
+
+static int vexpress_syscfg_exec(struct vexpress_syscfg_reg *reg,
+               int index, bool write, u32 *data)
+{
+       struct vexpress_syscfg *syscfg = reg->syscfg;
+       u32 command, status;
+       int tries;
+       long timeout;
+
+       if (WARN_ON(index > reg->regs_num))
+               return -EINVAL;
+
+       command = readl(syscfg->base + SYS_CFGCTRL);
+       if (WARN_ON(command & SYS_CFGCTRL_START))
+               return -EBUSY;
+
+       command = reg->templates[index];
+       command |= SYS_CFGCTRL_START;
+       command |= write ? SYS_CFGCTRL_WRITE : 0;
+
+       /* Use a canary for reads */
+       if (!write)
+               *data = 0xdeadbeef;
+
+       dev_dbg(syscfg->dev, "command %x, data %x\n",
+                       command, *data);
+       writel(*data, syscfg->base + SYS_CFGDATA);
+       writel(0, syscfg->base + SYS_CFGSTAT);
+       writel(command, syscfg->base + SYS_CFGCTRL);
+       mb();
+
+       /* The operation can take ages... Go to sleep, 100us initially */
+       tries = 100;
+       timeout = 100;
+       do {
+               int err = vexpress_syscfg_delay(timeout);
+               if (err)
+                       return err;
+
+               status = readl(syscfg->base + SYS_CFGSTAT);
+               if (status & SYS_CFGSTAT_ERR)
+                       return -EFAULT;
+
+               if (timeout > 20)
+                       timeout -= 20;
+       } while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
+       if (WARN_ON_ONCE(!tries))
+               return -ETIMEDOUT;
+
+       if (!write) {
+               *data = readl(syscfg->base + SYS_CFGDATA);
+               dev_dbg(syscfg->dev, "read data %x\n", *data);
+       }
+
+       return 0;
+}
+
+static int vexpress_syscfg_reg_read(void *context, unsigned int index,
+               unsigned int *val)
+{
+       struct vexpress_syscfg_reg *reg = context;
+
+       return vexpress_syscfg_exec(reg, index, false, val);
+}
+
+static int vexpress_syscfg_reg_write(void *context, unsigned int index,
+               unsigned int val)
+{
+       struct vexpress_syscfg_reg *reg = context;
+
+       return vexpress_syscfg_exec(reg, index, true, &val);
+}
+
+static struct vexpress_syscfg_reg *vexpress_syscfg_alloc_reg(
+               struct vexpress_syscfg *syscfg,
+               int site, int position, int dcc, int regs_num)
+{
+       struct vexpress_syscfg_reg *reg;
+       int i;
+
+       reg = kzalloc(sizeof(*reg) + sizeof(*reg->templates) * regs_num,
+                       GFP_KERNEL);
+       if (!reg)
+               return NULL;
+
+       reg->syscfg = syscfg;
+       reg->regs_num = regs_num;
+
+       for (i = 0; i < regs_num; i++) {
+               reg->templates[i] = SYS_CFGCTRL_DCC(dcc);
+               reg->templates[i] |= SYS_CFGCTRL_SITE(site);
+               reg->templates[i] |= SYS_CFGCTRL_POSITION(position);
+       }
+
+       return reg;
+}
+
+static void vexpress_syscfg_set_reg(struct vexpress_syscfg_reg *reg,
+               int index, u32 function, u32 device)
+{
+       if (WARN_ON(index >= reg->regs_num))
+               return;
+
+       reg->templates[index] |= SYS_CFGCTRL_FUNC(function);
+       reg->templates[index] |= SYS_CFGCTRL_DEVICE(device);
+}
+
+static void vexpress_syscfg_free_reg(void *reg)
+{
+       kfree(reg);
+}
+
+struct regmap_config vexpress_syscfg_regmap_config = {
+       .lock = vexpress_config_lock,
+       .unlock = vexpress_config_unlock,
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_read = vexpress_syscfg_reg_read,
+       .reg_write = vexpress_syscfg_reg_write,
+       .free_context = vexpress_syscfg_free_reg,
+       .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+       .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+
+/* Non-DT hack, to be gone... */
+static struct vexpress_syscfg *vexpress_syscfg_not_of;
+
+struct regmap *devm_vexpress_syscfg_regmap_init(struct device *dev,
+               u32 site, u32 position, u32 dcc, u32 function, u32 device)
+{
+       struct vexpress_syscfg_reg *reg;
+
+       reg = vexpress_syscfg_alloc_reg(vexpress_syscfg_not_of,
+                       site, position, dcc, 1);
+       if (!reg)
+               return NULL;
+
+       vexpress_syscfg_set_reg(reg, 0, function, device);
+
+       return devm_regmap_init(dev, NULL, reg, &vexpress_syscfg_regmap_config);
+}
+
+
+static int vexpress_syscfg_bridge_regmap_init(struct device *dev, void 
*context)
+{
+       struct vexpress_syscfg *syscfg = context;
+       int err;
+       u32 site, position, dcc;
+       struct property *prop;
+       int regs_num;
+       struct vexpress_syscfg_reg *reg;
+       const __be32 *val;
+       int i;
+       struct regmap *regmap;
+
+       if (!dev->of_node)
+               return -EINVAL;
+
+       err = vexpress_config_get_topo(dev->of_node, &site, &position, &dcc);
+       if (err)
+               return err;
+
+       prop = of_find_property(dev->of_node, "arm,vexpress-sysreg,func", NULL);
+       if (!prop)
+               return -EINVAL;
+
+       /* Each "register" is defined by two numbers - function and device */
+       regs_num = prop->length / sizeof(u32) / 2;
+       reg = vexpress_syscfg_alloc_reg(syscfg, site, position, dcc,
+                       regs_num);
+       if (!reg)
+               return -ENOMEM;
+
+       for (i = 0, val = prop->value; i < regs_num; i++) {
+               u32 function = be32_to_cpup(val++);
+               u32 device = be32_to_cpup(val++);
+
+               vexpress_syscfg_set_reg(reg, i, function, device);
+       }
+       if (err)
+               return err;
+
+       vexpress_syscfg_regmap_config.max_register = regs_num - 1;
+       regmap = devm_regmap_init(dev, NULL, reg,
+                       &vexpress_syscfg_regmap_config);
+       return IS_ERR(regmap) ? PTR_ERR(regmap) : 0;
+}
+
+int vexpress_syscfg_probe(struct platform_device *pdev)
+{
+       struct vexpress_syscfg *syscfg;
+       struct resource *res;
+       struct device *bridge;
+
+       syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL);
+       if (!syscfg)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!devm_request_mem_region(&pdev->dev, res->start,
+                       resource_size(res), pdev->name))
+               return -EBUSY;
+
+       syscfg->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!syscfg->base)
+               return -EFAULT;
+
+       bridge = vexpress_config_bridge_register(pdev->dev.parent,
+                       vexpress_syscfg_bridge_regmap_init, syscfg);
+       if (IS_ERR(bridge))
+               return PTR_ERR(bridge);
+
+       if (!pdev->dev.of_node)
+               vexpress_syscfg_not_of = syscfg;
+
+       register_syscore_ops(&vexpress_syscfg_syscore_ops);
+
+       return 0;
+}
+
+static const struct platform_device_id vexpress_syscfg_id_table[] = {
+       { "vexpress-syscfg", },
+       {},
+};
+
+static struct platform_driver vexpress_syscfg_driver = {
+       .driver.name = "vexpress-syscfg",
+       .id_table = vexpress_syscfg_id_table,
+       .probe = vexpress_syscfg_probe,
+};
+
+static int __init vexpress_syscfg_init(void)
+{
+       return platform_driver_register(&vexpress_syscfg_driver);
+}
+core_initcall(vexpress_syscfg_init);
diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h
index 1b8889f..203f63a 100644
--- a/include/linux/vexpress.h
+++ b/include/linux/vexpress.h
@@ -16,6 +16,7 @@
 
 #include <linux/device.h>
 #include <linux/reboot.h>
+#include <linux/regmap.h>
 
 #define VEXPRESS_SITE_MB               0
 #define VEXPRESS_SITE_DB1              1
@@ -34,13 +35,6 @@
 #define VEXPRESS_GPIO_LED6             9
 #define VEXPRESS_GPIO_LED7             10
 
-#define VEXPRESS_RES_FUNC(_site, _func)        \
-{                                      \
-       .start = (_site),               \
-       .end = (_func),                 \
-       .flags = IORESOURCE_BUS,        \
-}
-
 /* Config infrastructure */
 
 void vexpress_config_set_master(u32 site);
@@ -57,6 +51,9 @@ struct device *vexpress_config_bridge_register(struct device 
*parent,
 
 /* Platform control */
 
+struct regmap *devm_vexpress_syscfg_regmap_init(struct device *dev,
+               u32 site, u32 position, u32 dcc, u32 function, u32 device);
+
 u32 vexpress_get_procid(int site);
 u32 vexpress_get_hbi(int site);
 void *vexpress_get_24mhz_clock_base(void);
-- 
1.8.3.2


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to