Commit 974cc7b9 authored by Pawel Moll's avatar Pawel Moll

mfd: vexpress: Define the device as MFD cells

This patch - finally, after over 6 months! :-( - addresses
Samuel's request to split the vexpress-sysreg driver into
smaller portions and define the device in a form of MFD
cells:

* LEDs code has been completely removed and replaced with
  "gpio-leds" nodes in the tree (referencing dedicated
  GPIO subnodes in sysreg - bindings documentation updated);
  this also better fits the reality as some variants of the
  motherboard don't have all the LEDs populated

* syscfg bridge code has been extracted into a separate
  driver (placed in drivers/misc for no better place)

* all the ID & MISC registers are defined as sysconf
  making them available for other drivers should they need
  to use them (and also to the user via /sys/kernel/debug/regmap
  which can be helpful in platform debugging)
Signed-off-by: default avatarPawel Moll <pawel.moll@arm.com>
Acked-by: default avatarLee Jones <lee.jones@linaro.org>
parent 29f9b6cf
......@@ -8,6 +8,8 @@ interrupt generation, MMC and NOR Flash control etc.
Required node properties:
- compatible value : = "arm,vexpress,sysreg";
- reg : physical base address and the size of the registers window
Deprecated properties, replaced by GPIO subnodes (see below):
- gpio-controller : specifies that the node is a GPIO controller
- #gpio-cells : size of the GPIO specifier, should be 2:
- first cell is the pseudo-GPIO line number:
......@@ -16,14 +18,44 @@ Required node properties:
2 - NOR FLASH WPn
- second cell can take standard GPIO flags (currently ignored).
Control registers providing pseudo-GPIO lines must be represented
by subnodes, each of them requiring the following properties:
- compatible value : one of
"arm,vexpress-sysreg,sys_led"
"arm,vexpress-sysreg,sys_mci"
"arm,vexpress-sysreg,sys_flash"
- gpio-controller : makes the node a GPIO controller
- #gpio-cells : size of the GPIO specifier, must be 2:
- first cell is the function number:
- for sys_led : 0..7 = LED 0..7
- for sys_mci : 0 = MMC CARDIN, 1 = MMC WPROT
- for sys_flash : 0 = NOR FLASH WPn
- second cell can take standard GPIO flags (currently ignored).
Example:
v2m_sysreg: sysreg@10000000 {
compatible = "arm,vexpress-sysreg";
reg = <0x10000000 0x1000>;
v2m_led_gpios: sys_led@08 {
compatible = "arm,vexpress-sysreg,sys_led";
gpio-controller;
#gpio-cells = <2>;
};
v2m_mmc_gpios: sys_mci@48 {
compatible = "arm,vexpress-sysreg,sys_mci";
gpio-controller;
#gpio-cells = <2>;
};
v2m_flash_gpios: sys_flash@4c {
compatible = "arm,vexpress-sysreg,sys_flash";
gpio-controller;
#gpio-cells = <2>;
};
};
This block also can also act a bridge to the platform's configuration
bus via "system control" interface, addressing devices with site number,
position in the board stack, config controller, function and device
......
......@@ -74,9 +74,25 @@ iofpga@3,00000000 {
v2m_sysreg: sysreg@010000 {
compatible = "arm,vexpress-sysreg";
reg = <0x010000 0x1000>;
v2m_led_gpios: sys_led@08 {
compatible = "arm,vexpress-sysreg,sys_led";
gpio-controller;
#gpio-cells = <2>;
};
v2m_mmc_gpios: sys_mci@48 {
compatible = "arm,vexpress-sysreg,sys_mci";
gpio-controller;
#gpio-cells = <2>;
};
v2m_flash_gpios: sys_flash@4c {
compatible = "arm,vexpress-sysreg,sys_flash";
gpio-controller;
#gpio-cells = <2>;
};
};
v2m_sysctl: sysctl@020000 {
compatible = "arm,sp810", "arm,primecell";
......@@ -113,8 +129,8 @@ mmci@050000 {
compatible = "arm,pl180", "arm,primecell";
reg = <0x050000 0x1000>;
interrupts = <9 10>;
cd-gpios = <&v2m_sysreg 0 0>;
wp-gpios = <&v2m_sysreg 1 0>;
cd-gpios = <&v2m_mmc_gpios 0 0>;
wp-gpios = <&v2m_mmc_gpios 1 0>;
max-frequency = <12000000>;
vmmc-supply = <&v2m_fixed_3v3>;
clocks = <&v2m_clk24mhz>, <&smbclk>;
......@@ -265,6 +281,58 @@ v2m_refclk32khz: refclk32khz {
clock-output-names = "v2m:refclk32khz";
};
leds {
compatible = "gpio-leds";
user@1 {
label = "v2m:green:user1";
gpios = <&v2m_led_gpios 0 0>;
linux,default-trigger = "heartbeat";
};
user@2 {
label = "v2m:green:user2";
gpios = <&v2m_led_gpios 1 0>;
linux,default-trigger = "mmc0";
};
user@3 {
label = "v2m:green:user3";
gpios = <&v2m_led_gpios 2 0>;
linux,default-trigger = "cpu0";
};
user@4 {
label = "v2m:green:user4";
gpios = <&v2m_led_gpios 3 0>;
linux,default-trigger = "cpu1";
};
user@5 {
label = "v2m:green:user5";
gpios = <&v2m_led_gpios 4 0>;
linux,default-trigger = "cpu2";
};
user@6 {
label = "v2m:green:user6";
gpios = <&v2m_led_gpios 5 0>;
linux,default-trigger = "cpu3";
};
user@7 {
label = "v2m:green:user7";
gpios = <&v2m_led_gpios 6 0>;
linux,default-trigger = "cpu4";
};
user@8 {
label = "v2m:green:user8";
gpios = <&v2m_led_gpios 7 0>;
linux,default-trigger = "cpu5";
};
};
mcc {
compatible = "arm,vexpress,config-bus";
arm,vexpress,config-bridge = <&v2m_sysreg>;
......
......@@ -73,9 +73,25 @@ iofpga@7,00000000 {
v2m_sysreg: sysreg@00000 {
compatible = "arm,vexpress-sysreg";
reg = <0x00000 0x1000>;
v2m_led_gpios: sys_led@08 {
compatible = "arm,vexpress-sysreg,sys_led";
gpio-controller;
#gpio-cells = <2>;
};
v2m_mmc_gpios: sys_mci@48 {
compatible = "arm,vexpress-sysreg,sys_mci";
gpio-controller;
#gpio-cells = <2>;
};
v2m_flash_gpios: sys_flash@4c {
compatible = "arm,vexpress-sysreg,sys_flash";
gpio-controller;
#gpio-cells = <2>;
};
};
v2m_sysctl: sysctl@01000 {
compatible = "arm,sp810", "arm,primecell";
......@@ -112,8 +128,8 @@ mmci@05000 {
compatible = "arm,pl180", "arm,primecell";
reg = <0x05000 0x1000>;
interrupts = <9 10>;
cd-gpios = <&v2m_sysreg 0 0>;
wp-gpios = <&v2m_sysreg 1 0>;
cd-gpios = <&v2m_mmc_gpios 0 0>;
wp-gpios = <&v2m_mmc_gpios 1 0>;
max-frequency = <12000000>;
vmmc-supply = <&v2m_fixed_3v3>;
clocks = <&v2m_clk24mhz>, <&smbclk>;
......@@ -264,6 +280,58 @@ v2m_refclk32khz: refclk32khz {
clock-output-names = "v2m:refclk32khz";
};
leds {
compatible = "gpio-leds";
user@1 {
label = "v2m:green:user1";
gpios = <&v2m_led_gpios 0 0>;
linux,default-trigger = "heartbeat";
};
user@2 {
label = "v2m:green:user2";
gpios = <&v2m_led_gpios 1 0>;
linux,default-trigger = "mmc0";
};
user@3 {
label = "v2m:green:user3";
gpios = <&v2m_led_gpios 2 0>;
linux,default-trigger = "cpu0";
};
user@4 {
label = "v2m:green:user4";
gpios = <&v2m_led_gpios 3 0>;
linux,default-trigger = "cpu1";
};
user@5 {
label = "v2m:green:user5";
gpios = <&v2m_led_gpios 4 0>;
linux,default-trigger = "cpu2";
};
user@6 {
label = "v2m:green:user6";
gpios = <&v2m_led_gpios 5 0>;
linux,default-trigger = "cpu3";
};
user@7 {
label = "v2m:green:user7";
gpios = <&v2m_led_gpios 6 0>;
linux,default-trigger = "cpu4";
};
user@8 {
label = "v2m:green:user8";
gpios = <&v2m_led_gpios 7 0>;
linux,default-trigger = "cpu5";
};
};
mcc {
compatible = "arm,vexpress,config-bus";
arm,vexpress,config-bridge = <&v2m_sysreg>;
......
......@@ -160,7 +160,7 @@ static void __init ct_ca9x4_init(void)
amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource);
platform_device_register(&pmu_device);
vexpress_sysreg_config_device_register(&osc1_device);
vexpress_syscfg_device_register(&osc1_device);
}
#ifdef CONFIG_SMP
......
......@@ -201,8 +201,9 @@ static struct platform_device v2m_cf_device = {
static struct mmci_platform_data v2m_mmci_data = {
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
.gpio_wp = VEXPRESS_GPIO_MMC_WPROT,
.gpio_cd = VEXPRESS_GPIO_MMC_CARDIN,
.status = vexpress_get_mci_cardin,
.gpio_cd = -1,
.gpio_wp = -1,
};
static struct resource v2m_sysreg_resources[] = {
......@@ -351,10 +352,10 @@ static void __init v2m_init(void)
for (i = 0; i < ARRAY_SIZE(v2m_amba_devs); i++)
amba_device_register(v2m_amba_devs[i], &iomem_resource);
vexpress_sysreg_config_device_register(&v2m_muxfpga_device);
vexpress_sysreg_config_device_register(&v2m_shutdown_device);
vexpress_sysreg_config_device_register(&v2m_reboot_device);
vexpress_sysreg_config_device_register(&v2m_dvimode_device);
vexpress_syscfg_device_register(&v2m_muxfpga_device);
vexpress_syscfg_device_register(&v2m_shutdown_device);
vexpress_syscfg_device_register(&v2m_reboot_device);
vexpress_syscfg_device_register(&v2m_dvimode_device);
ct_desc->init_tile();
}
......@@ -409,8 +410,6 @@ void __init v2m_dt_init_early(void)
{
u32 dt_hbi;
vexpress_sysreg_of_early_init();
/* Confirm board type against DT property, if available */
if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) {
u32 hbi = vexpress_get_hbi(VEXPRESS_SITE_MASTER);
......
......@@ -1227,12 +1227,17 @@ config MCP_UCB1200_TS
endmenu
config VEXPRESS_CONFIG
bool "ARM Versatile Express platform infrastructure"
depends on ARM || ARM64
config MFD_VEXPRESS_SYSREG
bool "Versatile Express System Registers"
depends on VEXPRESS_CONFIG
default y
select CLKSRC_MMIO
select GPIO_GENERIC_PLATFORM
select MFD_CORE
select MFD_SYSCON
help
Platform configuration infrastructure for the ARM Ltd.
Versatile Express.
System Registers are the platform configuration block
on the ARM Ltd. Versatile Express board.
endmenu
endif
......@@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o
obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
obj-$(CONFIG_MFD_AS3722) += as3722.o
......
This diff is collapsed.
......@@ -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 interface is 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"
......
......@@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
/*
* 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) 2014 ARM Limited
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.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 list_head funcs;
};
struct vexpress_syscfg_func {
struct list_head list;
struct vexpress_syscfg *syscfg;
struct regmap *regmap;
int num_templates;
u32 template[0]; /* Keep it last! */
};
static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
int index, bool write, u32 *data)
{
struct vexpress_syscfg *syscfg = func->syscfg;
u32 command, status;
int tries;
long timeout;
if (WARN_ON(index > func->num_templates))
return -EINVAL;
command = readl(syscfg->base + SYS_CFGCTRL);
if (WARN_ON(command & SYS_CFGCTRL_START))
return -EBUSY;
command = func->template[index];
command |= SYS_CFGCTRL_START;
command |= write ? SYS_CFGCTRL_WRITE : 0;
/* Use a canary for reads */
if (!write)
*data = 0xdeadbeef;
dev_dbg(syscfg->dev, "func %p, command %x, data %x\n",
func, 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 {
if (!irqs_disabled()) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(usecs_to_jiffies(timeout));
if (signal_pending(current))
return -EINTR;
} else {
udelay(timeout);
}
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, "func %p, read data %x\n", func, *data);
}
return 0;
}
static int vexpress_syscfg_read(void *context, unsigned int index,
unsigned int *val)
{
struct vexpress_syscfg_func *func = context;
return vexpress_syscfg_exec(func, index, false, val);
}
static int vexpress_syscfg_write(void *context, unsigned int index,
unsigned int val)
{
struct vexpress_syscfg_func *func = context;
return vexpress_syscfg_exec(func, index, true, &val);
}
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_read,
.reg_write = vexpress_syscfg_write,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
static struct regmap *vexpress_syscfg_regmap_init(struct device *dev,
void *context)
{
struct platform_device *pdev = to_platform_device(dev);
struct vexpress_syscfg *syscfg = context;
struct vexpress_syscfg_func *func;
struct property *prop;
const __be32 *val = NULL;
__be32 energy_quirk[4];
int num;
u32 site, position, dcc;
int i;
if (dev->of_node) {
int err = vexpress_config_get_topo(dev->of_node, &site,
&position, &dcc);
if (err)
return ERR_PTR(err);
prop = of_find_property(dev->of_node,
"arm,vexpress-sysreg,func", NULL);
if (!prop)
return ERR_PTR(-EINVAL);
num = prop->length / sizeof(u32) / 2;
val = prop->value;
} else {
if (pdev->num_resources != 1 ||
pdev->resource[0].flags != IORESOURCE_BUS)
return ERR_PTR(-EFAULT);
site = pdev->resource[0].start;
if (site == VEXPRESS_SITE_MASTER)
site = vexpress_config_get_master();
position = 0;
dcc = 0;
num = 1;
}
/*
* "arm,vexpress-energy" function used to be described
* by its first device only, now it requires both
*/
if (num == 1 && of_device_is_compatible(dev->of_node,
"arm,vexpress-energy")) {
num = 2;
energy_quirk[0] = *val;
energy_quirk[2] = *val++;
energy_quirk[1] = *val;
energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
val = energy_quirk;
}
func = kzalloc(sizeof(*func) + sizeof(*func->template) * num,
GFP_KERNEL);
if (!func)
return NULL;
func->syscfg = syscfg;
func->num_templates = num;
for (i = 0; i < num; i++) {
u32 function, device;
if (dev->of_node) {
function = be32_to_cpup(val++);
device = be32_to_cpup(val++);
} else {
function = pdev->resource[0].end;
device = pdev->id;
}
dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
func, site, position, dcc,
function, device);
func->template[i] = SYS_CFGCTRL_DCC(dcc);
func->template[i] |= SYS_CFGCTRL_SITE(site);
func->template[i] |= SYS_CFGCTRL_POSITION(position);
func->template[i] |= SYS_CFGCTRL_FUNC(function);
func->template[i] |= SYS_CFGCTRL_DEVICE(device);
}
vexpress_syscfg_regmap_config.max_register = num - 1;
func->regmap = regmap_init(dev, NULL, func,
&vexpress_syscfg_regmap_config);
if (IS_ERR(func->regmap))
kfree(func);
else
list_add(&func->list, &syscfg->funcs);
return func->regmap;
}
static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context)
{
struct vexpress_syscfg *syscfg = context;
struct vexpress_syscfg_func *func, *tmp;
regmap_exit(regmap);
list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) {
if (func->regmap == regmap) {
list_del(&syscfg->funcs);
kfree(func);
break;
}
}
}
static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
.regmap_init = vexpress_syscfg_regmap_init,
.regmap_exit = vexpress_syscfg_regmap_exit,
};
/* Non-DT hack, to be gone... */
static struct device *vexpress_syscfg_bridge;
int vexpress_syscfg_device_register(struct platform_device *pdev)
{
pdev->dev.parent = vexpress_syscfg_bridge;
return platform_device_register(pdev);
}
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;
syscfg->dev = &pdev->dev;
INIT_LIST_HEAD(&syscfg->funcs);
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;
/* Must use dev.parent (MFD), as that's where DT phandle points at... */
bridge = vexpress_config_bridge_register(pdev->dev.parent,
&vexpress_syscfg_bridge_ops, syscfg);
if (IS_ERR(bridge))
return PTR_ERR(bridge);
/* Non-DT case */
if (!pdev->dev.of_node)
vexpress_syscfg_bridge = bridge;
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);
......@@ -24,18 +24,6 @@
#define VEXPRESS_SITE_DB2 2
#define VEXPRESS_SITE_MASTER 0xf
#define VEXPRESS_GPIO_MMC_CARDIN 0
#define VEXPRESS_GPIO_MMC_WPROT 1
#define VEXPRESS_GPIO_FLASH_WPn 2
#define VEXPRESS_GPIO_LED0 3
#define VEXPRESS_GPIO_LED1 4
#define VEXPRESS_GPIO_LED2 5
#define VEXPRESS_GPIO_LED3 6
#define VEXPRESS_GPIO_LED4 7
#define VEXPRESS_GPIO_LED5 8
#define VEXPRESS_GPIO_LED6 9
#define VEXPRESS_GPIO_LED7 10
#define VEXPRESS_RES_FUNC(_site, _func) \
{ \
.start = (_site), \
......@@ -70,14 +58,14 @@ struct regmap *devm_regmap_init_vexpress_config(struct device *dev);
/* Platform control */
unsigned int vexpress_get_mci_cardin(struct device *dev);
u32 vexpress_get_procid(int site);
u32 vexpress_get_hbi(int site);
void *vexpress_get_24mhz_clock_base(void);
void vexpress_flags_set(u32 data);
void vexpress_sysreg_early_init(void __iomem *base);
void vexpress_sysreg_of_early_init(void);
int vexpress_sysreg_config_device_register(struct platform_device *pdev);
int vexpress_syscfg_device_register(struct platform_device *pdev);
/* Clocks */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment