summaryrefslogtreecommitdiff
path: root/drivers/mfd/vexpress-sysreg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/vexpress-sysreg.c')
-rw-r--r--drivers/mfd/vexpress-sysreg.c546
1 files changed, 89 insertions, 457 deletions
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 981bef4b7ebc..f49cee91f71c 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -1,33 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * 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/gpio/driver.h>
+#include <linux/gpio/generic.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/io.h>
-#include <linux/leds.h>
-#include <linux/of_address.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
-#include <linux/regulator/driver.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/stat.h>
-#include <linux/timer.h>
-#include <linux/vexpress.h>
#define SYS_ID 0x000
#define SYS_SW 0x004
#define SYS_LED 0x008
#define SYS_100HZ 0x024
-#define SYS_FLAGS 0x030
#define SYS_FLAGSSET 0x030
#define SYS_FLAGSCLR 0x034
#define SYS_NVFLAGS 0x038
@@ -45,472 +37,115 @@
#define SYS_CFGCTRL 0x0a4
#define SYS_CFGSTAT 0x0a8
-#define SYS_HBI_MASK 0xfff
-#define SYS_ID_HBI_SHIFT 16
-#define SYS_PROCIDx_HBI_SHIFT 0
-
-#define SYS_LED_LED(n) (1 << (n))
-
-#define SYS_MCI_CARDIN (1 << 0)
-#define SYS_MCI_WPROT (1 << 1)
-
-#define SYS_FLASH_WPn (1 << 0)
-
-#define SYS_MISC_MASTERSITE (1 << 14)
-
-#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_ERR (1 << 1)
-#define SYS_CFGSTAT_COMPLETE (1 << 0)
-
-
-static void __iomem *vexpress_sysreg_base;
-static struct device *vexpress_sysreg_dev;
-static int vexpress_master_site;
-
-
-void vexpress_flags_set(u32 data)
-{
- writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR);
- writel(data, vexpress_sysreg_base + SYS_FLAGSSET);
-}
-
-u32 vexpress_get_procid(int site)
-{
- if (site == VEXPRESS_SITE_MASTER)
- site = vexpress_master_site;
-
- return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ?
- SYS_PROCID0 : SYS_PROCID1));
-}
-
-u32 vexpress_get_hbi(int site)
-{
- u32 id;
-
- switch (site) {
- case VEXPRESS_SITE_MB:
- id = readl(vexpress_sysreg_base + SYS_ID);
- return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK;
- case VEXPRESS_SITE_MASTER:
- case VEXPRESS_SITE_DB1:
- case VEXPRESS_SITE_DB2:
- id = vexpress_get_procid(site);
- return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
- }
-
- return ~0;
-}
-
-void __iomem *vexpress_get_24mhz_clock_base(void)
-{
- return vexpress_sysreg_base + SYS_24MHZ;
-}
-
-
-static void vexpress_sysreg_find_prop(struct device_node *node,
- const char *name, u32 *val)
-{
- of_node_get(node);
- while (node) {
- if (of_property_read_u32(node, name, val) == 0) {
- of_node_put(node);
- return;
- }
- node = of_get_next_parent(node);
- }
-}
-
-unsigned __vexpress_get_site(struct device *dev, struct device_node *node)
-{
- u32 site = 0;
+/* The sysreg block is just a random collection of various functions... */
- WARN_ON(dev && node && dev->of_node != node);
- if (dev && !node)
- node = dev->of_node;
-
- if (node) {
- vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site);
- } else if (dev && dev->bus == &platform_bus_type) {
- struct platform_device *pdev = to_platform_device(dev);
-
- if (pdev->num_resources == 1 &&
- pdev->resource[0].flags == IORESOURCE_BUS)
- site = pdev->resource[0].start;
- } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) {
- site = VEXPRESS_SITE_MASTER;
- }
-
- if (site == VEXPRESS_SITE_MASTER)
- site = vexpress_master_site;
-
- return site;
-}
-
-
-struct vexpress_sysreg_config_func {
- u32 template;
- u32 device;
+static const struct property_entry vexpress_sysreg_sys_led_props[] = {
+ PROPERTY_ENTRY_STRING("label", "sys_led"),
+ PROPERTY_ENTRY_U32("ngpios", 8),
+ { }
};
-static struct vexpress_config_bridge *vexpress_sysreg_config_bridge;
-static struct timer_list vexpress_sysreg_config_timer;
-static u32 *vexpress_sysreg_config_data;
-static int vexpress_sysreg_config_tries;
-
-static void *vexpress_sysreg_config_func_get(struct device *dev,
- struct device_node *node)
-{
- struct vexpress_sysreg_config_func *config_func;
- u32 site;
- u32 position = 0;
- u32 dcc = 0;
- u32 func_device[2];
- int err = -EFAULT;
-
- if (node) {
- of_node_get(node);
- vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site);
- vexpress_sysreg_find_prop(node, "arm,vexpress,position",
- &position);
- vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc);
- err = of_property_read_u32_array(node,
- "arm,vexpress-sysreg,func", func_device,
- ARRAY_SIZE(func_device));
- of_node_put(node);
- } else if (dev && dev->bus == &platform_bus_type) {
- struct platform_device *pdev = to_platform_device(dev);
-
- if (pdev->num_resources == 1 &&
- pdev->resource[0].flags == IORESOURCE_BUS) {
- site = pdev->resource[0].start;
- func_device[0] = pdev->resource[0].end;
- func_device[1] = pdev->id;
- err = 0;
- }
- }
- if (err)
- return NULL;
-
- config_func = kzalloc(sizeof(*config_func), GFP_KERNEL);
- if (!config_func)
- return NULL;
-
- config_func->template = SYS_CFGCTRL_DCC(dcc);
- config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]);
- config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ?
- vexpress_master_site : site);
- config_func->template |= SYS_CFGCTRL_POSITION(position);
- config_func->device |= func_device[1];
-
- dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func,
- config_func->template, config_func->device);
-
- return config_func;
-}
-
-static void vexpress_sysreg_config_func_put(void *func)
-{
- kfree(func);
-}
-
-static int vexpress_sysreg_config_func_exec(void *func, int offset,
- bool write, u32 *data)
-{
- int status;
- struct vexpress_sysreg_config_func *config_func = func;
- u32 command;
-
- if (WARN_ON(!vexpress_sysreg_base))
- return -ENOENT;
-
- command = readl(vexpress_sysreg_base + SYS_CFGCTRL);
- if (WARN_ON(command & SYS_CFGCTRL_START))
- return -EBUSY;
-
- command = SYS_CFGCTRL_START;
- command |= write ? SYS_CFGCTRL_WRITE : 0;
- command |= config_func->template;
- command |= SYS_CFGCTRL_DEVICE(config_func->device + offset);
-
- /* Use a canary for reads */
- if (!write)
- *data = 0xdeadbeef;
-
- dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n",
- command, *data);
- writel(*data, vexpress_sysreg_base + SYS_CFGDATA);
- writel(0, vexpress_sysreg_base + SYS_CFGSTAT);
- writel(command, vexpress_sysreg_base + SYS_CFGCTRL);
- mb();
-
- if (vexpress_sysreg_dev) {
- /* Schedule completion check */
- if (!write)
- vexpress_sysreg_config_data = data;
- vexpress_sysreg_config_tries = 100;
- mod_timer(&vexpress_sysreg_config_timer,
- jiffies + usecs_to_jiffies(100));
- status = VEXPRESS_CONFIG_STATUS_WAIT;
- } else {
- /* Early execution, no timer available, have to spin */
- u32 cfgstat;
-
- do {
- cpu_relax();
- cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT);
- } while (!cfgstat);
-
- if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE))
- *data = readl(vexpress_sysreg_base + SYS_CFGDATA);
- status = VEXPRESS_CONFIG_STATUS_DONE;
-
- if (cfgstat & SYS_CFGSTAT_ERR)
- status = -EINVAL;
- }
-
- return status;
-}
-
-struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = {
- .name = "vexpress-sysreg",
- .func_get = vexpress_sysreg_config_func_get,
- .func_put = vexpress_sysreg_config_func_put,
- .func_exec = vexpress_sysreg_config_func_exec,
+static const struct software_node vexpress_sysreg_sys_led_swnode = {
+ .properties = vexpress_sysreg_sys_led_props,
};
-static void vexpress_sysreg_config_complete(unsigned long data)
-{
- int status = VEXPRESS_CONFIG_STATUS_DONE;
- u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT);
-
- if (cfgstat & SYS_CFGSTAT_ERR)
- status = -EINVAL;
- if (!vexpress_sysreg_config_tries--)
- status = -ETIMEDOUT;
-
- if (status < 0) {
- dev_err(vexpress_sysreg_dev, "error %d\n", status);
- } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) {
- mod_timer(&vexpress_sysreg_config_timer,
- jiffies + usecs_to_jiffies(50));
- return;
- }
-
- if (vexpress_sysreg_config_data) {
- *vexpress_sysreg_config_data = readl(vexpress_sysreg_base +
- SYS_CFGDATA);
- dev_dbg(vexpress_sysreg_dev, "read data %x\n",
- *vexpress_sysreg_config_data);
- vexpress_sysreg_config_data = NULL;
- }
-
- vexpress_config_complete(vexpress_sysreg_config_bridge, status);
-}
-
-
-void vexpress_sysreg_setup(struct device_node *node)
-{
- if (WARN_ON(!vexpress_sysreg_base))
- return;
-
- if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
- vexpress_master_site = VEXPRESS_SITE_DB2;
- else
- vexpress_master_site = VEXPRESS_SITE_DB1;
-
- vexpress_sysreg_config_bridge = vexpress_config_bridge_register(
- node, &vexpress_sysreg_config_bridge_info);
- WARN_ON(!vexpress_sysreg_config_bridge);
-}
-
-void __init vexpress_sysreg_early_init(void __iomem *base)
-{
- vexpress_sysreg_base = base;
- vexpress_sysreg_setup(NULL);
-}
-
-void __init vexpress_sysreg_of_early_init(void)
-{
- struct device_node *node;
-
- if (vexpress_sysreg_base)
- return;
-
- node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg");
- if (node) {
- vexpress_sysreg_base = of_iomap(node, 0);
- vexpress_sysreg_setup(node);
- }
-}
-
-
-#ifdef CONFIG_GPIOLIB
-
-#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \
- [VEXPRESS_GPIO_##_name] = { \
- .reg = _reg, \
- .value = _reg##_##_value, \
- }
-
-static struct vexpress_sysreg_gpio {
- unsigned long reg;
- u32 value;
-} vexpress_sysreg_gpios[] = {
- VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN),
- VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT),
- VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn),
- VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)),
- VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)),
- VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)),
- VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)),
- VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)),
- VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)),
- VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)),
- VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)),
+static const struct property_entry vexpress_sysreg_sys_mci_props[] = {
+ PROPERTY_ENTRY_STRING("label", "sys_mci"),
+ PROPERTY_ENTRY_U32("ngpios", 2),
+ { }
};
-static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip,
- unsigned offset)
-{
- return 0;
-}
-
-static int vexpress_sysreg_gpio_get(struct gpio_chip *chip,
- unsigned offset)
-{
- struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset];
- u32 reg_value = readl(vexpress_sysreg_base + gpio->reg);
-
- return !!(reg_value & gpio->value);
-}
-
-static void vexpress_sysreg_gpio_set(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset];
- u32 reg_value = readl(vexpress_sysreg_base + gpio->reg);
-
- if (value)
- reg_value |= gpio->value;
- else
- reg_value &= ~gpio->value;
-
- writel(reg_value, vexpress_sysreg_base + gpio->reg);
-}
-
-static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- vexpress_sysreg_gpio_set(chip, offset, value);
-
- return 0;
-}
-
-static struct gpio_chip vexpress_sysreg_gpio_chip = {
- .label = "vexpress-sysreg",
- .direction_input = vexpress_sysreg_gpio_direction_input,
- .direction_output = vexpress_sysreg_gpio_direction_output,
- .get = vexpress_sysreg_gpio_get,
- .set = vexpress_sysreg_gpio_set,
- .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios),
- .base = 0,
+static const struct software_node vexpress_sysreg_sys_mci_swnode = {
+ .properties = vexpress_sysreg_sys_mci_props,
};
-
-#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \
- { \
- .name = "v2m:green:"_name, \
- .default_trigger = _default_trigger, \
- .gpio = VEXPRESS_GPIO_##_gpio, \
- }
-
-struct gpio_led vexpress_sysreg_leds[] = {
- VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0),
- VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1),
- VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2),
- VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3),
- VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4),
- VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5),
- VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6),
- VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7),
+static const struct property_entry vexpress_sysreg_sys_flash_props[] = {
+ PROPERTY_ENTRY_STRING("label", "sys_flash"),
+ PROPERTY_ENTRY_U32("ngpios", 1),
+ { }
};
-struct gpio_led_platform_data vexpress_sysreg_leds_pdata = {
- .num_leds = ARRAY_SIZE(vexpress_sysreg_leds),
- .leds = vexpress_sysreg_leds,
+static const struct software_node vexpress_sysreg_sys_flash_swnode = {
+ .properties = vexpress_sysreg_sys_flash_props,
};
-#endif
-
-
-static ssize_t vexpress_sysreg_sys_id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID));
-}
-
-DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL);
+static struct mfd_cell vexpress_sysreg_cells[] = {
+ {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_led",
+ .num_resources = 1,
+ .resources = &DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"),
+ .swnode = &vexpress_sysreg_sys_led_swnode,
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_mci",
+ .num_resources = 1,
+ .resources = &DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"),
+ .swnode = &vexpress_sysreg_sys_mci_swnode,
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_flash",
+ .num_resources = 1,
+ .resources = &DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"),
+ .swnode = &vexpress_sysreg_sys_flash_swnode,
+ }, {
+ .name = "vexpress-syscfg",
+ .num_resources = 1,
+ .resources = &DEFINE_RES_MEM(SYS_MISC, 0x4c),
+ }
+};
static int vexpress_sysreg_probe(struct platform_device *pdev)
{
- int err;
- struct resource *res = platform_get_resource(pdev,
- IORESOURCE_MEM, 0);
-
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "Failed to request memory region!\n");
- return -EBUSY;
- }
+ struct gpio_generic_chip *mmc_gpio_chip;
+ struct gpio_generic_chip_config config;
+ struct resource *mem;
+ void __iomem *base;
+ int ret;
- if (!vexpress_sysreg_base) {
- vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- vexpress_sysreg_setup(pdev->dev.of_node);
- }
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
- if (!vexpress_sysreg_base) {
- dev_err(&pdev->dev, "Failed to obtain base address!\n");
- return -EFAULT;
- }
+ base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!base)
+ return -ENOMEM;
- setup_timer(&vexpress_sysreg_config_timer,
- vexpress_sysreg_config_complete, 0);
+ /*
+ * 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;
- vexpress_sysreg_dev = &pdev->dev;
+ config = (struct gpio_generic_chip_config) {
+ .dev = &pdev->dev,
+ .sz = 4,
+ .dat = base + SYS_MCI,
+ };
-#ifdef CONFIG_GPIOLIB
- vexpress_sysreg_gpio_chip.dev = &pdev->dev;
- err = gpiochip_add(&vexpress_sysreg_gpio_chip);
- if (err) {
- vexpress_config_bridge_unregister(
- vexpress_sysreg_config_bridge);
- dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n",
- err);
- return err;
- }
+ ret = gpio_generic_chip_init(mmc_gpio_chip, &config);
+ if (ret)
+ return ret;
- platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",
- PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,
- sizeof(vexpress_sysreg_leds_pdata));
-#endif
+ mmc_gpio_chip->gc.ngpio = 2;
- device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
+ ret = devm_gpiochip_add_data(&pdev->dev, &mmc_gpio_chip->gc, NULL);
+ if (ret)
+ return ret;
- return 0;
+ return devm_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", },
{},
};
+MODULE_DEVICE_TABLE(of, vexpress_sysreg_match);
static struct platform_driver vexpress_sysreg_driver = {
.driver = {
@@ -520,9 +155,6 @@ static struct platform_driver vexpress_sysreg_driver = {
.probe = vexpress_sysreg_probe,
};
-static int __init vexpress_sysreg_init(void)
-{
- vexpress_sysreg_of_early_init();
- return platform_driver_register(&vexpress_sysreg_driver);
-}
-core_initcall(vexpress_sysreg_init);
+module_platform_driver(vexpress_sysreg_driver);
+MODULE_DESCRIPTION("Versatile Express system registers driver");
+MODULE_LICENSE("GPL v2");