Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / mfd / vexpress-sysreg.c
diff --git a/kernel/drivers/mfd/vexpress-sysreg.c b/kernel/drivers/mfd/vexpress-sysreg.c
new file mode 100644 (file)
index 0000000..3e628df
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * 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) 2012 ARM Limited
+ */
+
+#include <linux/basic_mmio_gpio.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/vexpress.h>
+
+#define SYS_ID                 0x000
+#define SYS_SW                 0x004
+#define SYS_LED                        0x008
+#define SYS_100HZ              0x024
+#define SYS_FLAGSSET           0x030
+#define SYS_FLAGSCLR           0x034
+#define SYS_NVFLAGS            0x038
+#define SYS_NVFLAGSSET         0x038
+#define SYS_NVFLAGSCLR         0x03c
+#define SYS_MCI                        0x048
+#define SYS_FLASH              0x04c
+#define SYS_CFGSW              0x058
+#define SYS_24MHZ              0x05c
+#define SYS_MISC               0x060
+#define SYS_DMA                        0x064
+#define SYS_PROCID0            0x084
+#define SYS_PROCID1            0x088
+#define SYS_CFGDATA            0x0a0
+#define SYS_CFGCTRL            0x0a4
+#define SYS_CFGSTAT            0x0a8
+
+#define SYS_HBI_MASK           0xfff
+#define SYS_PROCIDx_HBI_SHIFT  0
+
+#define SYS_MISC_MASTERSITE    (1 << 14)
+
+void vexpress_flags_set(u32 data)
+{
+       static void __iomem *base;
+
+       if (!base) {
+               struct device_node *node = of_find_compatible_node(NULL, NULL,
+                               "arm,vexpress-sysreg");
+
+               base = of_iomap(node, 0);
+       }
+
+       if (WARN_ON(!base))
+               return;
+
+       writel(~0, base + SYS_FLAGSCLR);
+       writel(data, base + SYS_FLAGSSET);
+}
+
+/* The sysreg block is just a random collection of various functions... */
+
+static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = {
+       .label = "sys_id",
+};
+
+static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = {
+       .label = "sys_led",
+       .base = -1,
+       .ngpio = 8,
+};
+
+static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = {
+       .label = "sys_mci",
+       .base = -1,
+       .ngpio = 2,
+};
+
+static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = {
+       .label = "sys_flash",
+       .base = -1,
+       .ngpio = 1,
+};
+
+static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = {
+       .label = "sys_misc",
+};
+
+static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = {
+       .label = "sys_procid",
+};
+
+static struct mfd_cell vexpress_sysreg_cells[] = {
+       {
+               .name = "syscon",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM(SYS_ID, 0x4),
+               },
+               .platform_data = &vexpress_sysreg_sys_id_pdata,
+               .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata),
+       }, {
+               .name = "basic-mmio-gpio",
+               .of_compatible = "arm,vexpress-sysreg,sys_led",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"),
+               },
+               .platform_data = &vexpress_sysreg_sys_led_pdata,
+               .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata),
+       }, {
+               .name = "basic-mmio-gpio",
+               .of_compatible = "arm,vexpress-sysreg,sys_mci",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"),
+               },
+               .platform_data = &vexpress_sysreg_sys_mci_pdata,
+               .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata),
+       }, {
+               .name = "basic-mmio-gpio",
+               .of_compatible = "arm,vexpress-sysreg,sys_flash",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"),
+               },
+               .platform_data = &vexpress_sysreg_sys_flash_pdata,
+               .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata),
+       }, {
+               .name = "syscon",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM(SYS_MISC, 0x4),
+               },
+               .platform_data = &vexpress_sysreg_sys_misc_pdata,
+               .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata),
+       }, {
+               .name = "syscon",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM(SYS_PROCID0, 0x8),
+               },
+               .platform_data = &vexpress_sysreg_sys_procid_pdata,
+               .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata),
+       }, {
+               .name = "vexpress-syscfg",
+               .num_resources = 1,
+               .resources = (struct resource []) {
+                       DEFINE_RES_MEM(SYS_CFGDATA, 0xc),
+               },
+       }
+};
+
+static int vexpress_sysreg_probe(struct platform_device *pdev)
+{
+       struct resource *mem;
+       void __iomem *base;
+       struct bgpio_chip *mmc_gpio_chip;
+       int master;
+       u32 dt_hbi;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem)
+               return -EINVAL;
+
+       base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+       if (!base)
+               return -ENOMEM;
+
+       master = readl(base + SYS_MISC) & SYS_MISC_MASTERSITE ?
+                       VEXPRESS_SITE_DB2 : VEXPRESS_SITE_DB1;
+       vexpress_config_set_master(master);
+
+       /* Confirm board type against DT property, if available */
+       if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) {
+               u32 id = readl(base + (master == VEXPRESS_SITE_DB1 ?
+                                SYS_PROCID0 : SYS_PROCID1));
+               u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
+
+               if (WARN_ON(dt_hbi != hbi))
+                       dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n",
+                                       dt_hbi, hbi);
+       }
+
+       /*
+        * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with
+        * older trees using sysreg node for MMC control lines.
+        */
+       mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip),
+                       GFP_KERNEL);
+       if (!mmc_gpio_chip)
+               return -ENOMEM;
+       bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,
+                       NULL, NULL, NULL, NULL, 0);
+       mmc_gpio_chip->gc.ngpio = 2;
+       gpiochip_add(&mmc_gpio_chip->gc);
+
+       return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
+                       vexpress_sysreg_cells,
+                       ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL);
+}
+
+static const struct of_device_id vexpress_sysreg_match[] = {
+       { .compatible = "arm,vexpress-sysreg", },
+       {},
+};
+
+static struct platform_driver vexpress_sysreg_driver = {
+       .driver = {
+               .name = "vexpress-sysreg",
+               .of_match_table = vexpress_sysreg_match,
+       },
+       .probe = vexpress_sysreg_probe,
+};
+
+static int __init vexpress_sysreg_init(void)
+{
+       struct device_node *node;
+
+       /* Need the sysreg early, before any other device... */
+       for_each_matching_node(node, vexpress_sysreg_match)
+               of_platform_device_create(node, NULL, NULL);
+
+       return platform_driver_register(&vexpress_sysreg_driver);
+}
+core_initcall(vexpress_sysreg_init);