aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/bus
diff options
context:
space:
mode:
authorPawel Moll <pawel.moll@arm.com>2014-04-30 11:46:29 -0400
committerPawel Moll <pawel.moll@arm.com>2014-05-15 12:02:18 -0400
commit3b9334ac835bb431e2186645230c9f1eb94b5d49 (patch)
tree631e00349363927a6e49c6810332d92295103446 /drivers/bus
parentc6e126de43e7d4abfd6cf796b40589db3a046167 (diff)
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/bus')
-rw-r--r--drivers/bus/Kconfig9
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/vexpress-config.c202
3 files changed, 213 insertions, 0 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 552373c4e362..f24e79dd51bf 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -41,4 +41,13 @@ config ARM_CCI
41 help 41 help
42 Driver supporting the CCI cache coherent interconnect for ARM 42 Driver supporting the CCI cache coherent interconnect for ARM
43 platforms. 43 platforms.
44
45config VEXPRESS_CONFIG
46 bool "Versatile Express configuration bus"
47 default y if ARCH_VEXPRESS
48 depends on ARM || ARM64
49 select REGMAP
50 help
51 Platform configuration infrastructure for the ARM Ltd.
52 Versatile Express.
44endmenu 53endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 8947bdd0de8b..f095aa771de9 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
10obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o 10obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
11# CCI cache coherent interconnect for ARM platforms 11# CCI cache coherent interconnect for ARM platforms
12obj-$(CONFIG_ARM_CCI) += arm-cci.o 12obj-$(CONFIG_ARM_CCI) += arm-cci.o
13
14obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c
new file mode 100644
index 000000000000..27a07dfcd626
--- /dev/null
+++ b/drivers/bus/vexpress-config.c
@@ -0,0 +1,202 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2014 ARM Limited
12 */
13
14#include <linux/err.h>
15#include <linux/init.h>
16#include <linux/of.h>
17#include <linux/of_device.h>
18#include <linux/vexpress.h>
19
20
21struct vexpress_config_bridge {
22 struct vexpress_config_bridge_ops *ops;
23 void *context;
24};
25
26
27static DEFINE_MUTEX(vexpress_config_mutex);
28static struct class *vexpress_config_class;
29static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
30
31
32void vexpress_config_set_master(u32 site)
33{
34 vexpress_config_site_master = site;
35}
36
37u32 vexpress_config_get_master(void)
38{
39 return vexpress_config_site_master;
40}
41
42void vexpress_config_lock(void *arg)
43{
44 mutex_lock(&vexpress_config_mutex);
45}
46
47void vexpress_config_unlock(void *arg)
48{
49 mutex_unlock(&vexpress_config_mutex);
50}
51
52
53static void vexpress_config_find_prop(struct device_node *node,
54 const char *name, u32 *val)
55{
56 /* Default value */
57 *val = 0;
58
59 of_node_get(node);
60 while (node) {
61 if (of_property_read_u32(node, name, val) == 0) {
62 of_node_put(node);
63 return;
64 }
65 node = of_get_next_parent(node);
66 }
67}
68
69int vexpress_config_get_topo(struct device_node *node, u32 *site,
70 u32 *position, u32 *dcc)
71{
72 vexpress_config_find_prop(node, "arm,vexpress,site", site);
73 if (*site == VEXPRESS_SITE_MASTER)
74 *site = vexpress_config_site_master;
75 if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER))
76 return -EINVAL;
77 vexpress_config_find_prop(node, "arm,vexpress,position", position);
78 vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc);
79
80 return 0;
81}
82
83
84static void vexpress_config_devres_release(struct device *dev, void *res)
85{
86 struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent);
87 struct regmap *regmap = res;
88
89 bridge->ops->regmap_exit(regmap, bridge->context);
90}
91
92struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
93{
94 struct vexpress_config_bridge *bridge;
95 struct regmap *regmap;
96 struct regmap **res;
97
98 if (WARN_ON(dev->parent->class != vexpress_config_class))
99 return ERR_PTR(-ENODEV);
100
101 bridge = dev_get_drvdata(dev->parent);
102 if (WARN_ON(!bridge))
103 return ERR_PTR(-EINVAL);
104
105 res = devres_alloc(vexpress_config_devres_release, sizeof(*res),
106 GFP_KERNEL);
107 if (!res)
108 return ERR_PTR(-ENOMEM);
109
110 regmap = bridge->ops->regmap_init(dev, bridge->context);
111 if (IS_ERR(regmap)) {
112 devres_free(res);
113 return regmap;
114 }
115
116 *res = regmap;
117 devres_add(dev, res);
118
119 return regmap;
120}
121
122
123struct device *vexpress_config_bridge_register(struct device *parent,
124 struct vexpress_config_bridge_ops *ops, void *context)
125{
126 struct device *dev;
127 struct vexpress_config_bridge *bridge;
128
129 if (!vexpress_config_class) {
130 vexpress_config_class = class_create(THIS_MODULE,
131 "vexpress-config");
132 if (IS_ERR(vexpress_config_class))
133 return (void *)vexpress_config_class;
134 }
135
136 dev = device_create(vexpress_config_class, parent, 0,
137 NULL, "%s.bridge", dev_name(parent));
138
139 if (IS_ERR(dev))
140 return dev;
141
142 bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
143 if (!bridge) {
144 put_device(dev);
145 device_unregister(dev);
146 return ERR_PTR(-ENOMEM);
147 }
148 bridge->ops = ops;
149 bridge->context = context;
150
151 dev_set_drvdata(dev, bridge);
152
153 dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
154 dev_name(dev), parent->of_node);
155
156 return dev;
157}
158
159
160static int vexpress_config_node_match(struct device *dev, const void *data)
161{
162 const struct device_node *node = data;
163
164 dev_dbg(dev, "Parent node %p, looking for %p\n",
165 dev->parent->of_node, node);
166
167 return dev->parent->of_node == node;
168}
169
170static int vexpress_config_populate(struct device_node *node)
171{
172 struct device_node *bridge;
173 struct device *parent;
174
175 bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
176 if (!bridge)
177 return -EINVAL;
178
179 parent = class_find_device(vexpress_config_class, NULL, bridge,
180 vexpress_config_node_match);
181 if (WARN_ON(!parent))
182 return -ENODEV;
183
184 return of_platform_populate(node, NULL, NULL, parent);
185}
186
187static int __init vexpress_config_init(void)
188{
189 int err = 0;
190 struct device_node *node;
191
192 /* Need the config devices early, before the "normal" devices... */
193 for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
194 err = vexpress_config_populate(node);
195 if (err)
196 break;
197 }
198
199 return err;
200}
201postcore_initcall(vexpress_config_init);
202