diff options
Diffstat (limited to 'kernel/drivers/mfd/vexpress-sysreg.c')
-rw-r--r-- | kernel/drivers/mfd/vexpress-sysreg.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/kernel/drivers/mfd/vexpress-sysreg.c b/kernel/drivers/mfd/vexpress-sysreg.c new file mode 100644 index 000000000..3e628df92 --- /dev/null +++ b/kernel/drivers/mfd/vexpress-sysreg.c @@ -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); |