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: Pawel Moll <pawel.moll@arm.com> Acked-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
parent
29f9b6cf7b
commit
974cc7b934
12 changed files with 674 additions and 445 deletions
|
@ -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,12 +18,42 @@ 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>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
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
|
||||
|
|
|
@ -74,8 +74,24 @@
|
|||
v2m_sysreg: sysreg@010000 {
|
||||
compatible = "arm,vexpress-sysreg";
|
||||
reg = <0x010000 0x1000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
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 {
|
||||
|
@ -113,8 +129,8 @@
|
|||
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 @@
|
|||
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,8 +73,24 @@
|
|||
v2m_sysreg: sysreg@00000 {
|
||||
compatible = "arm,vexpress-sysreg";
|
||||
reg = <0x00000 0x1000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
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 {
|
||||
|
@ -112,8 +128,8 @@
|
|||
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 @@
|
|||
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
|
||||
|
|
|
@ -11,25 +11,22 @@
|
|||
* Copyright (C) 2012 ARM Limited
|
||||
*/
|
||||
|
||||
#include <linux/basic_mmio_gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/leds.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/regulator/driver.h>
|
||||
#include <linux/sched.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
|
||||
|
@ -51,36 +48,32 @@
|
|||
#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 void __iomem *vexpress_sysreg_base(void)
|
||||
{
|
||||
if (!__vexpress_sysreg_base) {
|
||||
struct device_node *node = of_find_compatible_node(NULL, NULL,
|
||||
"arm,vexpress-sysreg");
|
||||
|
||||
static void __iomem *vexpress_sysreg_base;
|
||||
static struct device *vexpress_sysreg_dev;
|
||||
static LIST_HEAD(vexpress_sysreg_config_funcs);
|
||||
static struct device *vexpress_sysreg_config_bridge;
|
||||
__vexpress_sysreg_base = of_iomap(node, 0);
|
||||
}
|
||||
|
||||
WARN_ON(!__vexpress_sysreg_base);
|
||||
|
||||
return __vexpress_sysreg_base;
|
||||
}
|
||||
|
||||
|
||||
static int vexpress_sysreg_get_master(void)
|
||||
{
|
||||
if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
|
||||
if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE)
|
||||
return VEXPRESS_SITE_DB2;
|
||||
|
||||
return VEXPRESS_SITE_DB1;
|
||||
|
@ -88,8 +81,13 @@ static int vexpress_sysreg_get_master(void)
|
|||
|
||||
void vexpress_flags_set(u32 data)
|
||||
{
|
||||
writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR);
|
||||
writel(data, vexpress_sysreg_base + SYS_FLAGSSET);
|
||||
writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR);
|
||||
writel(data, vexpress_sysreg_base() + SYS_FLAGSSET);
|
||||
}
|
||||
|
||||
unsigned int vexpress_get_mci_cardin(struct device *dev)
|
||||
{
|
||||
return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN;
|
||||
}
|
||||
|
||||
u32 vexpress_get_procid(int site)
|
||||
|
@ -97,7 +95,7 @@ u32 vexpress_get_procid(int site)
|
|||
if (site == VEXPRESS_SITE_MASTER)
|
||||
site = vexpress_sysreg_get_master();
|
||||
|
||||
return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ?
|
||||
return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ?
|
||||
SYS_PROCID0 : SYS_PROCID1));
|
||||
}
|
||||
|
||||
|
@ -107,7 +105,7 @@ u32 vexpress_get_hbi(int site)
|
|||
|
||||
switch (site) {
|
||||
case VEXPRESS_SITE_MB:
|
||||
id = readl(vexpress_sysreg_base + SYS_ID);
|
||||
id = readl(vexpress_sysreg_base() + SYS_ID);
|
||||
return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK;
|
||||
case VEXPRESS_SITE_MASTER:
|
||||
case VEXPRESS_SITE_DB1:
|
||||
|
@ -121,406 +119,143 @@ u32 vexpress_get_hbi(int site)
|
|||
|
||||
void __iomem *vexpress_get_24mhz_clock_base(void)
|
||||
{
|
||||
return vexpress_sysreg_base + SYS_24MHZ;
|
||||
}
|
||||
|
||||
|
||||
struct vexpress_sysreg_config_func {
|
||||
struct list_head list;
|
||||
struct regmap *regmap;
|
||||
int num_templates;
|
||||
u32 template[0]; /* Keep this last */
|
||||
};
|
||||
|
||||
static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func,
|
||||
int index, bool write, u32 *data)
|
||||
{
|
||||
u32 command, status;
|
||||
int tries;
|
||||
long timeout;
|
||||
|
||||
if (WARN_ON(!vexpress_sysreg_base))
|
||||
return -ENOENT;
|
||||
|
||||
if (WARN_ON(index > func->num_templates))
|
||||
return -EINVAL;
|
||||
|
||||
command = readl(vexpress_sysreg_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(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();
|
||||
|
||||
/* The operation can take ages... Go to sleep, 100us initially */
|
||||
tries = 100;
|
||||
timeout = 100;
|
||||
do {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(usecs_to_jiffies(timeout));
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
|
||||
status = readl(vexpress_sysreg_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(vexpress_sysreg_base + SYS_CFGDATA);
|
||||
dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n",
|
||||
func, *data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vexpress_sysreg_config_read(void *context, unsigned int index,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct vexpress_sysreg_config_func *func = context;
|
||||
|
||||
return vexpress_sysreg_config_exec(func, index, false, val);
|
||||
}
|
||||
|
||||
static int vexpress_sysreg_config_write(void *context, unsigned int index,
|
||||
unsigned int val)
|
||||
{
|
||||
struct vexpress_sysreg_config_func *func = context;
|
||||
|
||||
return vexpress_sysreg_config_exec(func, index, true, &val);
|
||||
}
|
||||
|
||||
struct regmap_config vexpress_sysreg_regmap_config = {
|
||||
.lock = vexpress_config_lock,
|
||||
.unlock = vexpress_config_unlock,
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_read = vexpress_sysreg_config_read,
|
||||
.reg_write = vexpress_sysreg_config_write,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev,
|
||||
void *context)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct vexpress_sysreg_config_func *func;
|
||||
struct property *prop;
|
||||
const __be32 *val = NULL;
|
||||
__be32 energy_quirk[4];
|
||||
int num;
|
||||
u32 site, position, dcc;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
if (dev->of_node) {
|
||||
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_sysreg_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->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_sysreg_regmap_config.max_register = num - 1;
|
||||
|
||||
func->regmap = regmap_init(dev, NULL, func,
|
||||
&vexpress_sysreg_regmap_config);
|
||||
|
||||
if (IS_ERR(func->regmap))
|
||||
kfree(func);
|
||||
else
|
||||
list_add(&func->list, &vexpress_sysreg_config_funcs);
|
||||
|
||||
return func->regmap;
|
||||
}
|
||||
|
||||
static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap,
|
||||
void *context)
|
||||
{
|
||||
struct vexpress_sysreg_config_func *func, *tmp;
|
||||
|
||||
regmap_exit(regmap);
|
||||
|
||||
list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs,
|
||||
list) {
|
||||
if (func->regmap == regmap) {
|
||||
list_del(&vexpress_sysreg_config_funcs);
|
||||
kfree(func);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = {
|
||||
.regmap_init = vexpress_sysreg_config_regmap_init,
|
||||
.regmap_exit = vexpress_sysreg_config_regmap_exit,
|
||||
};
|
||||
|
||||
int vexpress_sysreg_config_device_register(struct platform_device *pdev)
|
||||
{
|
||||
pdev->dev.parent = vexpress_sysreg_config_bridge;
|
||||
|
||||
return platform_device_register(pdev);
|
||||
return vexpress_sysreg_base() + SYS_24MHZ;
|
||||
}
|
||||
|
||||
|
||||
void __init vexpress_sysreg_early_init(void __iomem *base)
|
||||
{
|
||||
vexpress_sysreg_base = base;
|
||||
vexpress_config_set_master(vexpress_sysreg_get_master());
|
||||
}
|
||||
|
||||
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 (WARN_ON(!node))
|
||||
return;
|
||||
|
||||
vexpress_sysreg_base = of_iomap(node, 0);
|
||||
if (WARN_ON(!vexpress_sysreg_base))
|
||||
return;
|
||||
__vexpress_sysreg_base = base;
|
||||
|
||||
vexpress_config_set_master(vexpress_sysreg_get_master());
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
/* The sysreg block is just a random collection of various functions... */
|
||||
|
||||
#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \
|
||||
[VEXPRESS_GPIO_##_name] = { \
|
||||
.reg = _reg, \
|
||||
.value = _reg##_##_value, \
|
||||
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 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 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,
|
||||
};
|
||||
|
||||
|
||||
#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),
|
||||
};
|
||||
|
||||
struct gpio_led_platform_data vexpress_sysreg_leds_pdata = {
|
||||
.num_leds = ARRAY_SIZE(vexpress_sysreg_leds),
|
||||
.leds = vexpress_sysreg_leds,
|
||||
};
|
||||
|
||||
#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 int vexpress_sysreg_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
struct resource *res = platform_get_resource(pdev,
|
||||
IORESOURCE_MEM, 0);
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
struct bgpio_chip *mmc_gpio_chip;
|
||||
|
||||
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;
|
||||
}
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem)
|
||||
return -EINVAL;
|
||||
|
||||
if (!vexpress_sysreg_base)
|
||||
vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
|
||||
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;
|
||||
|
||||
vexpress_config_set_master(vexpress_sysreg_get_master());
|
||||
vexpress_sysreg_dev = &pdev->dev;
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
vexpress_sysreg_gpio_chip.dev = &pdev->dev;
|
||||
err = gpiochip_add(&vexpress_sysreg_gpio_chip);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
|
||||
platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",
|
||||
PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,
|
||||
sizeof(vexpress_sysreg_leds_pdata));
|
||||
#endif
|
||||
|
||||
vexpress_sysreg_config_bridge = vexpress_config_bridge_register(
|
||||
&pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL);
|
||||
WARN_ON(!vexpress_sysreg_config_bridge);
|
||||
|
||||
device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
|
||||
|
||||
return 0;
|
||||
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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
324
drivers/misc/vexpress-syscfg.c
Normal file
324
drivers/misc/vexpress-syscfg.c
Normal file
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
|
|
Loading…
Reference in a new issue