summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorPawel Moll <pawel.moll@arm.com>2014-04-30 19:46:29 +0400
committerPawel Moll <pawel.moll@arm.com>2014-05-15 20:02:18 +0400
commit3b9334ac835bb431e2186645230c9f1eb94b5d49 (patch)
tree631e00349363927a6e49c6810332d92295103446 /drivers/mfd
parentc6e126de43e7d4abfd6cf796b40589db3a046167 (diff)
downloadlinux-3b9334ac835bb431e2186645230c9f1eb94b5d49.tar.xz
mfd: vexpress: Convert custom func API to regmap
Components of the Versatile Express platform (configuration microcontrollers on motherboard and daughterboards in particular) talk to each other over a custom configuration bus. They provide miscellaneous functions (from clock generator control to energy sensors) which are represented as platform devices (and Device Tree nodes). The transactions on the bus can be generated by different "bridges" in the system, some of which are universal for the whole platform (for the price of high transfer latencies), others restricted to a subsystem (but much faster). Until now drivers for such functions were using custom "func" API, which is being replaced in this patch by regmap calls. This required: * a rework (and move to drivers/bus directory, as suggested by Samuel and Arnd) of the config bus core, which is much simpler now and uses device model infrastructure (class) to keep track of the bridges; non-DT case (soon to be retired anyway) is simply covered by a special device registration function * the new config-bus driver also takes over device population, so there is no need for special matching table for of_platform_populate nor "simple-bus" hack in the arm64 model dtsi file (relevant bindings documentation has been updated); this allows all the vexpress devices fit into normal device model, making it possible to remove plenty of early inits and other hacks in the near future * adaptation of the syscfg bridge implementation in the sysreg driver, again making it much simpler; there is a special case of the "energy" function spanning two registers, where they should be both defined in the tree now, but backward compatibility is maintained in the code * modification of the relevant drivers: * hwmon - just a straight-forward API change * power/reset driver - API change * regulator - API change plus error handling simplification * osc clock driver - this one required larger rework in order to turn in into a standard platform driver Signed-off-by: Pawel Moll <pawel.moll@arm.com> Acked-by: Mark Brown <broonie@linaro.org> Acked-by: Lee Jones <lee.jones@linaro.org> Acked-by: Guenter Roeck <linux@roeck-us.net> Acked-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/vexpress-config.c287
-rw-r--r--drivers/mfd/vexpress-sysreg.c395
3 files changed, 209 insertions, 475 deletions
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2851275e2656..9ba838eb5131 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -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-config.o vexpress-sysreg.o
+obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
obj-$(CONFIG_MFD_AS3722) += as3722.o
diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c
deleted file mode 100644
index d0db89d13e01..000000000000
--- a/drivers/mfd/vexpress-config.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * 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
- */
-
-#define pr_fmt(fmt) "vexpress-config: " fmt
-
-#include <linux/bitops.h>
-#include <linux/completion.h>
-#include <linux/export.h>
-#include <linux/list.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/vexpress.h>
-
-
-#define VEXPRESS_CONFIG_MAX_BRIDGES 2
-
-static struct vexpress_config_bridge {
- struct device_node *node;
- struct vexpress_config_bridge_info *info;
- struct list_head transactions;
- spinlock_t transactions_lock;
-} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES];
-
-static DECLARE_BITMAP(vexpress_config_bridges_map,
- ARRAY_SIZE(vexpress_config_bridges));
-static DEFINE_MUTEX(vexpress_config_bridges_mutex);
-
-struct vexpress_config_bridge *vexpress_config_bridge_register(
- struct device_node *node,
- struct vexpress_config_bridge_info *info)
-{
- struct vexpress_config_bridge *bridge;
- int i;
-
- pr_debug("Registering bridge '%s'\n", info->name);
-
- mutex_lock(&vexpress_config_bridges_mutex);
- i = find_first_zero_bit(vexpress_config_bridges_map,
- ARRAY_SIZE(vexpress_config_bridges));
- if (i >= ARRAY_SIZE(vexpress_config_bridges)) {
- pr_err("Can't register more bridges!\n");
- mutex_unlock(&vexpress_config_bridges_mutex);
- return NULL;
- }
- __set_bit(i, vexpress_config_bridges_map);
- bridge = &vexpress_config_bridges[i];
-
- bridge->node = node;
- bridge->info = info;
- INIT_LIST_HEAD(&bridge->transactions);
- spin_lock_init(&bridge->transactions_lock);
-
- mutex_unlock(&vexpress_config_bridges_mutex);
-
- return bridge;
-}
-EXPORT_SYMBOL(vexpress_config_bridge_register);
-
-void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge)
-{
- struct vexpress_config_bridge __bridge = *bridge;
- int i;
-
- mutex_lock(&vexpress_config_bridges_mutex);
- for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++)
- if (&vexpress_config_bridges[i] == bridge)
- __clear_bit(i, vexpress_config_bridges_map);
- mutex_unlock(&vexpress_config_bridges_mutex);
-
- WARN_ON(!list_empty(&__bridge.transactions));
- while (!list_empty(&__bridge.transactions))
- cpu_relax();
-}
-EXPORT_SYMBOL(vexpress_config_bridge_unregister);
-
-
-struct vexpress_config_func {
- struct vexpress_config_bridge *bridge;
- void *func;
-};
-
-struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
- struct device_node *node)
-{
- struct device_node *bridge_node;
- struct vexpress_config_func *func;
- int i;
-
- if (WARN_ON(dev && node && dev->of_node != node))
- return NULL;
- if (dev && !node)
- node = dev->of_node;
-
- func = kzalloc(sizeof(*func), GFP_KERNEL);
- if (!func)
- return NULL;
-
- bridge_node = of_node_get(node);
- while (bridge_node) {
- const __be32 *prop = of_get_property(bridge_node,
- "arm,vexpress,config-bridge", NULL);
-
- if (prop) {
- bridge_node = of_find_node_by_phandle(
- be32_to_cpup(prop));
- break;
- }
-
- bridge_node = of_get_next_parent(bridge_node);
- }
-
- mutex_lock(&vexpress_config_bridges_mutex);
- for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) {
- struct vexpress_config_bridge *bridge =
- &vexpress_config_bridges[i];
-
- if (test_bit(i, vexpress_config_bridges_map) &&
- bridge->node == bridge_node) {
- func->bridge = bridge;
- func->func = bridge->info->func_get(dev, node);
- break;
- }
- }
- mutex_unlock(&vexpress_config_bridges_mutex);
-
- if (!func->func) {
- of_node_put(node);
- kfree(func);
- return NULL;
- }
-
- return func;
-}
-EXPORT_SYMBOL(__vexpress_config_func_get);
-
-void vexpress_config_func_put(struct vexpress_config_func *func)
-{
- func->bridge->info->func_put(func->func);
- of_node_put(func->bridge->node);
- kfree(func);
-}
-EXPORT_SYMBOL(vexpress_config_func_put);
-
-struct vexpress_config_trans {
- struct vexpress_config_func *func;
- int offset;
- bool write;
- u32 *data;
- int status;
- struct completion completion;
- struct list_head list;
-};
-
-static void vexpress_config_dump_trans(const char *what,
- struct vexpress_config_trans *trans)
-{
- pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n",
- what, trans->write ? "write" : "read", trans,
- trans->func->func, trans->offset,
- trans->data ? *trans->data : 0, trans->status);
-}
-
-static int vexpress_config_schedule(struct vexpress_config_trans *trans)
-{
- int status;
- struct vexpress_config_bridge *bridge = trans->func->bridge;
- unsigned long flags;
-
- init_completion(&trans->completion);
- trans->status = -EFAULT;
-
- spin_lock_irqsave(&bridge->transactions_lock, flags);
-
- if (list_empty(&bridge->transactions)) {
- vexpress_config_dump_trans("Executing", trans);
- status = bridge->info->func_exec(trans->func->func,
- trans->offset, trans->write, trans->data);
- } else {
- vexpress_config_dump_trans("Queuing", trans);
- status = VEXPRESS_CONFIG_STATUS_WAIT;
- }
-
- switch (status) {
- case VEXPRESS_CONFIG_STATUS_DONE:
- vexpress_config_dump_trans("Finished", trans);
- trans->status = status;
- break;
- case VEXPRESS_CONFIG_STATUS_WAIT:
- list_add_tail(&trans->list, &bridge->transactions);
- break;
- }
-
- spin_unlock_irqrestore(&bridge->transactions_lock, flags);
-
- return status;
-}
-
-void vexpress_config_complete(struct vexpress_config_bridge *bridge,
- int status)
-{
- struct vexpress_config_trans *trans;
- unsigned long flags;
- const char *message = "Completed";
-
- spin_lock_irqsave(&bridge->transactions_lock, flags);
-
- trans = list_first_entry(&bridge->transactions,
- struct vexpress_config_trans, list);
- trans->status = status;
-
- do {
- vexpress_config_dump_trans(message, trans);
- list_del(&trans->list);
- complete(&trans->completion);
-
- if (list_empty(&bridge->transactions))
- break;
-
- trans = list_first_entry(&bridge->transactions,
- struct vexpress_config_trans, list);
- vexpress_config_dump_trans("Executing pending", trans);
- trans->status = bridge->info->func_exec(trans->func->func,
- trans->offset, trans->write, trans->data);
- message = "Finished pending";
- } while (trans->status == VEXPRESS_CONFIG_STATUS_DONE);
-
- spin_unlock_irqrestore(&bridge->transactions_lock, flags);
-}
-EXPORT_SYMBOL(vexpress_config_complete);
-
-int vexpress_config_wait(struct vexpress_config_trans *trans)
-{
- wait_for_completion(&trans->completion);
-
- return trans->status;
-}
-EXPORT_SYMBOL(vexpress_config_wait);
-
-int vexpress_config_read(struct vexpress_config_func *func, int offset,
- u32 *data)
-{
- struct vexpress_config_trans trans = {
- .func = func,
- .offset = offset,
- .write = false,
- .data = data,
- .status = 0,
- };
- int status = vexpress_config_schedule(&trans);
-
- if (status == VEXPRESS_CONFIG_STATUS_WAIT)
- status = vexpress_config_wait(&trans);
-
- return status;
-}
-EXPORT_SYMBOL(vexpress_config_read);
-
-int vexpress_config_write(struct vexpress_config_func *func, int offset,
- u32 data)
-{
- struct vexpress_config_trans trans = {
- .func = func,
- .offset = offset,
- .write = true,
- .data = &data,
- .status = 0,
- };
- int status = vexpress_config_schedule(&trans);
-
- if (status == VEXPRESS_CONFIG_STATUS_WAIT)
- status = vexpress_config_wait(&trans);
-
- return status;
-}
-EXPORT_SYMBOL(vexpress_config_write);
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 35281e804e7e..b4138a7168db 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -16,8 +16,10 @@
#include <linux/io.h>
#include <linux/leds.h>
#include <linux/of_address.h>
+#include <linux/of_platform.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>
@@ -72,9 +74,18 @@
static void __iomem *vexpress_sysreg_base;
static struct device *vexpress_sysreg_dev;
-static int vexpress_master_site;
+static LIST_HEAD(vexpress_sysreg_config_funcs);
+static struct device *vexpress_sysreg_config_bridge;
+static int vexpress_sysreg_get_master(void)
+{
+ if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
+ return VEXPRESS_SITE_DB2;
+
+ return VEXPRESS_SITE_DB1;
+}
+
void vexpress_flags_set(u32 data)
{
writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR);
@@ -84,7 +95,7 @@ void vexpress_flags_set(u32 data)
u32 vexpress_get_procid(int site)
{
if (site == VEXPRESS_SITE_MASTER)
- site = vexpress_master_site;
+ site = vexpress_sysreg_get_master();
return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ?
SYS_PROCID0 : SYS_PROCID1));
@@ -114,130 +125,33 @@ void __iomem *vexpress_get_24mhz_clock_base(void)
}
-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;
-
- 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;
+ struct list_head list;
+ struct regmap *regmap;
+ int num_templates;
+ u32 template[0]; /* Keep this last */
};
-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)
+static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func,
+ int index, bool write, u32 *data)
{
- struct vexpress_sysreg_config_func *config_func;
- u32 site = 0;
- 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;
+ 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 = SYS_CFGCTRL_START;
+ command = func->template[index];
+ 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)
@@ -250,90 +164,190 @@ static int vexpress_sysreg_config_func_exec(void *func, int offset,
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;
+ /* 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);
+ }
- do {
- cpu_relax();
- cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT);
- } while (!cfgstat);
+ return 0;
+}
- if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE))
- *data = readl(vexpress_sysreg_base + SYS_CFGDATA);
- status = VEXPRESS_CONFIG_STATUS_DONE;
+static int vexpress_sysreg_config_read(void *context, unsigned int index,
+ unsigned int *val)
+{
+ struct vexpress_sysreg_config_func *func = context;
- if (cfgstat & SYS_CFGSTAT_ERR)
- status = -EINVAL;
- }
+ return vexpress_sysreg_config_exec(func, index, false, val);
+}
- return status;
+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 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,
+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 void vexpress_sysreg_config_complete(unsigned long data)
+static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev,
+ void *context)
{
- 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;
+ 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;
}
- 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;
+ /*
+ * "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;
}
- vexpress_config_complete(vexpress_sysreg_config_bridge, status);
-}
+ func = kzalloc(sizeof(*func) + sizeof(*func->template) * num,
+ GFP_KERNEL);
+ if (!func)
+ return NULL;
+ func->num_templates = num;
-void vexpress_sysreg_setup(struct device_node *node)
-{
- if (WARN_ON(!vexpress_sysreg_base))
- return;
+ for (i = 0; i < num; i++) {
+ u32 function, device;
- if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
- vexpress_master_site = VEXPRESS_SITE_DB2;
+ 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
- vexpress_master_site = VEXPRESS_SITE_DB1;
+ list_add(&func->list, &vexpress_sysreg_config_funcs);
- vexpress_sysreg_config_bridge = vexpress_config_bridge_register(
- node, &vexpress_sysreg_config_bridge_info);
- WARN_ON(!vexpress_sysreg_config_bridge);
+ 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);
}
+
void __init vexpress_sysreg_early_init(void __iomem *base)
{
vexpress_sysreg_base = base;
- vexpress_sysreg_setup(NULL);
+ vexpress_config_set_master(vexpress_sysreg_get_master());
}
void __init vexpress_sysreg_of_early_init(void)
@@ -344,10 +358,14 @@ void __init vexpress_sysreg_of_early_init(void)
return;
node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg");
- if (node) {
- vexpress_sysreg_base = of_iomap(node, 0);
- vexpress_sysreg_setup(node);
- }
+ if (WARN_ON(!node))
+ return;
+
+ vexpress_sysreg_base = of_iomap(node, 0);
+ if (WARN_ON(!vexpress_sysreg_base))
+ return;
+
+ vexpress_config_set_master(vexpress_sysreg_get_master());
}
@@ -470,28 +488,22 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
return -EBUSY;
}
- if (!vexpress_sysreg_base) {
+ if (!vexpress_sysreg_base)
vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
- vexpress_sysreg_setup(pdev->dev.of_node);
- }
if (!vexpress_sysreg_base) {
dev_err(&pdev->dev, "Failed to obtain base address!\n");
return -EFAULT;
}
- setup_timer(&vexpress_sysreg_config_timer,
- vexpress_sysreg_config_complete, 0);
-
+ 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) {
- vexpress_config_bridge_unregister(
- vexpress_sysreg_config_bridge);
dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n",
err);
return err;
@@ -502,6 +514,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
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;
@@ -522,7 +538,12 @@ static struct platform_driver vexpress_sysreg_driver = {
static int __init vexpress_sysreg_init(void)
{
- vexpress_sysreg_of_early_init();
+ 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);