aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/vexpress-sysreg.c
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/mfd/vexpress-sysreg.c
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/mfd/vexpress-sysreg.c')
-rw-r--r--drivers/mfd/vexpress-sysreg.c395
1 files changed, 208 insertions, 187 deletions
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 @@
16#include <linux/io.h> 16#include <linux/io.h>
17#include <linux/leds.h> 17#include <linux/leds.h>
18#include <linux/of_address.h> 18#include <linux/of_address.h>
19#include <linux/of_platform.h>
19#include <linux/platform_device.h> 20#include <linux/platform_device.h>
20#include <linux/regulator/driver.h> 21#include <linux/regulator/driver.h>
22#include <linux/sched.h>
21#include <linux/slab.h> 23#include <linux/slab.h>
22#include <linux/stat.h> 24#include <linux/stat.h>
23#include <linux/timer.h> 25#include <linux/timer.h>
@@ -72,9 +74,18 @@
72 74
73static void __iomem *vexpress_sysreg_base; 75static void __iomem *vexpress_sysreg_base;
74static struct device *vexpress_sysreg_dev; 76static struct device *vexpress_sysreg_dev;
75static int vexpress_master_site; 77static LIST_HEAD(vexpress_sysreg_config_funcs);
78static struct device *vexpress_sysreg_config_bridge;
76 79
77 80
81static int vexpress_sysreg_get_master(void)
82{
83 if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
84 return VEXPRESS_SITE_DB2;
85
86 return VEXPRESS_SITE_DB1;
87}
88
78void vexpress_flags_set(u32 data) 89void vexpress_flags_set(u32 data)
79{ 90{
80 writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); 91 writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR);
@@ -84,7 +95,7 @@ void vexpress_flags_set(u32 data)
84u32 vexpress_get_procid(int site) 95u32 vexpress_get_procid(int site)
85{ 96{
86 if (site == VEXPRESS_SITE_MASTER) 97 if (site == VEXPRESS_SITE_MASTER)
87 site = vexpress_master_site; 98 site = vexpress_sysreg_get_master();
88 99
89 return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? 100 return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ?
90 SYS_PROCID0 : SYS_PROCID1)); 101 SYS_PROCID0 : SYS_PROCID1));
@@ -114,130 +125,33 @@ void __iomem *vexpress_get_24mhz_clock_base(void)
114} 125}
115 126
116 127
117static void vexpress_sysreg_find_prop(struct device_node *node,
118 const char *name, u32 *val)
119{
120 of_node_get(node);
121 while (node) {
122 if (of_property_read_u32(node, name, val) == 0) {
123 of_node_put(node);
124 return;
125 }
126 node = of_get_next_parent(node);
127 }
128}
129
130unsigned __vexpress_get_site(struct device *dev, struct device_node *node)
131{
132 u32 site = 0;
133
134 WARN_ON(dev && node && dev->of_node != node);
135 if (dev && !node)
136 node = dev->of_node;
137
138 if (node) {
139 vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site);
140 } else if (dev && dev->bus == &platform_bus_type) {
141 struct platform_device *pdev = to_platform_device(dev);
142
143 if (pdev->num_resources == 1 &&
144 pdev->resource[0].flags == IORESOURCE_BUS)
145 site = pdev->resource[0].start;
146 } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) {
147 site = VEXPRESS_SITE_MASTER;
148 }
149
150 if (site == VEXPRESS_SITE_MASTER)
151 site = vexpress_master_site;
152
153 return site;
154}
155
156
157struct vexpress_sysreg_config_func { 128struct vexpress_sysreg_config_func {
158 u32 template; 129 struct list_head list;
159 u32 device; 130 struct regmap *regmap;
131 int num_templates;
132 u32 template[0]; /* Keep this last */
160}; 133};
161 134
162static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; 135static int vexpress_sysreg_config_exec(struct vexpress_sysreg_config_func *func,
163static struct timer_list vexpress_sysreg_config_timer; 136 int index, bool write, u32 *data)
164static u32 *vexpress_sysreg_config_data;
165static int vexpress_sysreg_config_tries;
166
167static void *vexpress_sysreg_config_func_get(struct device *dev,
168 struct device_node *node)
169{ 137{
170 struct vexpress_sysreg_config_func *config_func; 138 u32 command, status;
171 u32 site = 0; 139 int tries;
172 u32 position = 0; 140 long timeout;
173 u32 dcc = 0;
174 u32 func_device[2];
175 int err = -EFAULT;
176
177 if (node) {
178 of_node_get(node);
179 vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site);
180 vexpress_sysreg_find_prop(node, "arm,vexpress,position",
181 &position);
182 vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc);
183 err = of_property_read_u32_array(node,
184 "arm,vexpress-sysreg,func", func_device,
185 ARRAY_SIZE(func_device));
186 of_node_put(node);
187 } else if (dev && dev->bus == &platform_bus_type) {
188 struct platform_device *pdev = to_platform_device(dev);
189
190 if (pdev->num_resources == 1 &&
191 pdev->resource[0].flags == IORESOURCE_BUS) {
192 site = pdev->resource[0].start;
193 func_device[0] = pdev->resource[0].end;
194 func_device[1] = pdev->id;
195 err = 0;
196 }
197 }
198 if (err)
199 return NULL;
200
201 config_func = kzalloc(sizeof(*config_func), GFP_KERNEL);
202 if (!config_func)
203 return NULL;
204
205 config_func->template = SYS_CFGCTRL_DCC(dcc);
206 config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]);
207 config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ?
208 vexpress_master_site : site);
209 config_func->template |= SYS_CFGCTRL_POSITION(position);
210 config_func->device |= func_device[1];
211
212 dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func,
213 config_func->template, config_func->device);
214
215 return config_func;
216}
217
218static void vexpress_sysreg_config_func_put(void *func)
219{
220 kfree(func);
221}
222
223static int vexpress_sysreg_config_func_exec(void *func, int offset,
224 bool write, u32 *data)
225{
226 int status;
227 struct vexpress_sysreg_config_func *config_func = func;
228 u32 command;
229 141
230 if (WARN_ON(!vexpress_sysreg_base)) 142 if (WARN_ON(!vexpress_sysreg_base))
231 return -ENOENT; 143 return -ENOENT;
232 144
145 if (WARN_ON(index > func->num_templates))
146 return -EINVAL;
147
233 command = readl(vexpress_sysreg_base + SYS_CFGCTRL); 148 command = readl(vexpress_sysreg_base + SYS_CFGCTRL);
234 if (WARN_ON(command & SYS_CFGCTRL_START)) 149 if (WARN_ON(command & SYS_CFGCTRL_START))
235 return -EBUSY; 150 return -EBUSY;
236 151
237 command = SYS_CFGCTRL_START; 152 command = func->template[index];
153 command |= SYS_CFGCTRL_START;
238 command |= write ? SYS_CFGCTRL_WRITE : 0; 154 command |= write ? SYS_CFGCTRL_WRITE : 0;
239 command |= config_func->template;
240 command |= SYS_CFGCTRL_DEVICE(config_func->device + offset);
241 155
242 /* Use a canary for reads */ 156 /* Use a canary for reads */
243 if (!write) 157 if (!write)
@@ -250,90 +164,190 @@ static int vexpress_sysreg_config_func_exec(void *func, int offset,
250 writel(command, vexpress_sysreg_base + SYS_CFGCTRL); 164 writel(command, vexpress_sysreg_base + SYS_CFGCTRL);
251 mb(); 165 mb();
252 166
253 if (vexpress_sysreg_dev) { 167 /* The operation can take ages... Go to sleep, 100us initially */
254 /* Schedule completion check */ 168 tries = 100;
255 if (!write) 169 timeout = 100;
256 vexpress_sysreg_config_data = data; 170 do {
257 vexpress_sysreg_config_tries = 100; 171 set_current_state(TASK_INTERRUPTIBLE);
258 mod_timer(&vexpress_sysreg_config_timer, 172 schedule_timeout(usecs_to_jiffies(timeout));
259 jiffies + usecs_to_jiffies(100)); 173 if (signal_pending(current))
260 status = VEXPRESS_CONFIG_STATUS_WAIT; 174 return -EINTR;
261 } else { 175
262 /* Early execution, no timer available, have to spin */ 176 status = readl(vexpress_sysreg_base + SYS_CFGSTAT);
263 u32 cfgstat; 177 if (status & SYS_CFGSTAT_ERR)
178 return -EFAULT;
179
180 if (timeout > 20)
181 timeout -= 20;
182 } while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
183 if (WARN_ON_ONCE(!tries))
184 return -ETIMEDOUT;
185
186 if (!write) {
187 *data = readl(vexpress_sysreg_base + SYS_CFGDATA);
188 dev_dbg(vexpress_sysreg_dev, "func %p, read data %x\n",
189 func, *data);
190 }
264 191
265 do { 192 return 0;
266 cpu_relax(); 193}
267 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT);
268 } while (!cfgstat);
269 194
270 if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) 195static int vexpress_sysreg_config_read(void *context, unsigned int index,
271 *data = readl(vexpress_sysreg_base + SYS_CFGDATA); 196 unsigned int *val)
272 status = VEXPRESS_CONFIG_STATUS_DONE; 197{
198 struct vexpress_sysreg_config_func *func = context;
273 199
274 if (cfgstat & SYS_CFGSTAT_ERR) 200 return vexpress_sysreg_config_exec(func, index, false, val);
275 status = -EINVAL; 201}
276 }
277 202
278 return status; 203static int vexpress_sysreg_config_write(void *context, unsigned int index,
204 unsigned int val)
205{
206 struct vexpress_sysreg_config_func *func = context;
207
208 return vexpress_sysreg_config_exec(func, index, true, &val);
279} 209}
280 210
281struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { 211struct regmap_config vexpress_sysreg_regmap_config = {
282 .name = "vexpress-sysreg", 212 .lock = vexpress_config_lock,
283 .func_get = vexpress_sysreg_config_func_get, 213 .unlock = vexpress_config_unlock,
284 .func_put = vexpress_sysreg_config_func_put, 214 .reg_bits = 32,
285 .func_exec = vexpress_sysreg_config_func_exec, 215 .val_bits = 32,
216 .reg_read = vexpress_sysreg_config_read,
217 .reg_write = vexpress_sysreg_config_write,
218 .reg_format_endian = REGMAP_ENDIAN_LITTLE,
219 .val_format_endian = REGMAP_ENDIAN_LITTLE,
286}; 220};
287 221
288static void vexpress_sysreg_config_complete(unsigned long data) 222static struct regmap *vexpress_sysreg_config_regmap_init(struct device *dev,
223 void *context)
289{ 224{
290 int status = VEXPRESS_CONFIG_STATUS_DONE; 225 struct platform_device *pdev = to_platform_device(dev);
291 u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); 226 struct vexpress_sysreg_config_func *func;
292 227 struct property *prop;
293 if (cfgstat & SYS_CFGSTAT_ERR) 228 const __be32 *val = NULL;
294 status = -EINVAL; 229 __be32 energy_quirk[4];
295 if (!vexpress_sysreg_config_tries--) 230 int num;
296 status = -ETIMEDOUT; 231 u32 site, position, dcc;
297 232 int err;
298 if (status < 0) { 233 int i;
299 dev_err(vexpress_sysreg_dev, "error %d\n", status); 234
300 } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { 235 if (dev->of_node) {
301 mod_timer(&vexpress_sysreg_config_timer, 236 err = vexpress_config_get_topo(dev->of_node, &site, &position,
302 jiffies + usecs_to_jiffies(50)); 237 &dcc);
303 return; 238 if (err)
239 return ERR_PTR(err);
240
241 prop = of_find_property(dev->of_node,
242 "arm,vexpress-sysreg,func", NULL);
243 if (!prop)
244 return ERR_PTR(-EINVAL);
245
246 num = prop->length / sizeof(u32) / 2;
247 val = prop->value;
248 } else {
249 if (pdev->num_resources != 1 ||
250 pdev->resource[0].flags != IORESOURCE_BUS)
251 return ERR_PTR(-EFAULT);
252
253 site = pdev->resource[0].start;
254 if (site == VEXPRESS_SITE_MASTER)
255 site = vexpress_sysreg_get_master();
256 position = 0;
257 dcc = 0;
258 num = 1;
304 } 259 }
305 260
306 if (vexpress_sysreg_config_data) { 261 /*
307 *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + 262 * "arm,vexpress-energy" function used to be described
308 SYS_CFGDATA); 263 * by its first device only, now it requires both
309 dev_dbg(vexpress_sysreg_dev, "read data %x\n", 264 */
310 *vexpress_sysreg_config_data); 265 if (num == 1 && of_device_is_compatible(dev->of_node,
311 vexpress_sysreg_config_data = NULL; 266 "arm,vexpress-energy")) {
267 num = 2;
268 energy_quirk[0] = *val;
269 energy_quirk[2] = *val++;
270 energy_quirk[1] = *val;
271 energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
272 val = energy_quirk;
312 } 273 }
313 274
314 vexpress_config_complete(vexpress_sysreg_config_bridge, status); 275 func = kzalloc(sizeof(*func) + sizeof(*func->template) * num,
315} 276 GFP_KERNEL);
277 if (!func)
278 return NULL;
316 279
280 func->num_templates = num;
317 281
318void vexpress_sysreg_setup(struct device_node *node) 282 for (i = 0; i < num; i++) {
319{ 283 u32 function, device;
320 if (WARN_ON(!vexpress_sysreg_base))
321 return;
322 284
323 if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) 285 if (dev->of_node) {
324 vexpress_master_site = VEXPRESS_SITE_DB2; 286 function = be32_to_cpup(val++);
287 device = be32_to_cpup(val++);
288 } else {
289 function = pdev->resource[0].end;
290 device = pdev->id;
291 }
292
293 dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
294 func, site, position, dcc,
295 function, device);
296
297 func->template[i] = SYS_CFGCTRL_DCC(dcc);
298 func->template[i] |= SYS_CFGCTRL_SITE(site);
299 func->template[i] |= SYS_CFGCTRL_POSITION(position);
300 func->template[i] |= SYS_CFGCTRL_FUNC(function);
301 func->template[i] |= SYS_CFGCTRL_DEVICE(device);
302 }
303
304 vexpress_sysreg_regmap_config.max_register = num - 1;
305
306 func->regmap = regmap_init(dev, NULL, func,
307 &vexpress_sysreg_regmap_config);
308
309 if (IS_ERR(func->regmap))
310 kfree(func);
325 else 311 else
326 vexpress_master_site = VEXPRESS_SITE_DB1; 312 list_add(&func->list, &vexpress_sysreg_config_funcs);
327 313
328 vexpress_sysreg_config_bridge = vexpress_config_bridge_register( 314 return func->regmap;
329 node, &vexpress_sysreg_config_bridge_info); 315}
330 WARN_ON(!vexpress_sysreg_config_bridge); 316
317static void vexpress_sysreg_config_regmap_exit(struct regmap *regmap,
318 void *context)
319{
320 struct vexpress_sysreg_config_func *func, *tmp;
321
322 regmap_exit(regmap);
323
324 list_for_each_entry_safe(func, tmp, &vexpress_sysreg_config_funcs,
325 list) {
326 if (func->regmap == regmap) {
327 list_del(&vexpress_sysreg_config_funcs);
328 kfree(func);
329 break;
330 }
331 }
332}
333
334static struct vexpress_config_bridge_ops vexpress_sysreg_config_bridge_ops = {
335 .regmap_init = vexpress_sysreg_config_regmap_init,
336 .regmap_exit = vexpress_sysreg_config_regmap_exit,
337};
338
339int vexpress_sysreg_config_device_register(struct platform_device *pdev)
340{
341 pdev->dev.parent = vexpress_sysreg_config_bridge;
342
343 return platform_device_register(pdev);
331} 344}
332 345
346
333void __init vexpress_sysreg_early_init(void __iomem *base) 347void __init vexpress_sysreg_early_init(void __iomem *base)
334{ 348{
335 vexpress_sysreg_base = base; 349 vexpress_sysreg_base = base;
336 vexpress_sysreg_setup(NULL); 350 vexpress_config_set_master(vexpress_sysreg_get_master());
337} 351}
338 352
339void __init vexpress_sysreg_of_early_init(void) 353void __init vexpress_sysreg_of_early_init(void)
@@ -344,10 +358,14 @@ void __init vexpress_sysreg_of_early_init(void)
344 return; 358 return;
345 359
346 node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); 360 node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg");
347 if (node) { 361 if (WARN_ON(!node))
348 vexpress_sysreg_base = of_iomap(node, 0); 362 return;
349 vexpress_sysreg_setup(node); 363
350 } 364 vexpress_sysreg_base = of_iomap(node, 0);
365 if (WARN_ON(!vexpress_sysreg_base))
366 return;
367
368 vexpress_config_set_master(vexpress_sysreg_get_master());
351} 369}
352 370
353 371
@@ -470,28 +488,22 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
470 return -EBUSY; 488 return -EBUSY;
471 } 489 }
472 490
473 if (!vexpress_sysreg_base) { 491 if (!vexpress_sysreg_base)
474 vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, 492 vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start,
475 resource_size(res)); 493 resource_size(res));
476 vexpress_sysreg_setup(pdev->dev.of_node);
477 }
478 494
479 if (!vexpress_sysreg_base) { 495 if (!vexpress_sysreg_base) {
480 dev_err(&pdev->dev, "Failed to obtain base address!\n"); 496 dev_err(&pdev->dev, "Failed to obtain base address!\n");
481 return -EFAULT; 497 return -EFAULT;
482 } 498 }
483 499
484 setup_timer(&vexpress_sysreg_config_timer, 500 vexpress_config_set_master(vexpress_sysreg_get_master());
485 vexpress_sysreg_config_complete, 0);
486
487 vexpress_sysreg_dev = &pdev->dev; 501 vexpress_sysreg_dev = &pdev->dev;
488 502
489#ifdef CONFIG_GPIOLIB 503#ifdef CONFIG_GPIOLIB
490 vexpress_sysreg_gpio_chip.dev = &pdev->dev; 504 vexpress_sysreg_gpio_chip.dev = &pdev->dev;
491 err = gpiochip_add(&vexpress_sysreg_gpio_chip); 505 err = gpiochip_add(&vexpress_sysreg_gpio_chip);
492 if (err) { 506 if (err) {
493 vexpress_config_bridge_unregister(
494 vexpress_sysreg_config_bridge);
495 dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", 507 dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n",
496 err); 508 err);
497 return err; 509 return err;
@@ -502,6 +514,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
502 sizeof(vexpress_sysreg_leds_pdata)); 514 sizeof(vexpress_sysreg_leds_pdata));
503#endif 515#endif
504 516
517 vexpress_sysreg_config_bridge = vexpress_config_bridge_register(
518 &pdev->dev, &vexpress_sysreg_config_bridge_ops, NULL);
519 WARN_ON(!vexpress_sysreg_config_bridge);
520
505 device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); 521 device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
506 522
507 return 0; 523 return 0;
@@ -522,7 +538,12 @@ static struct platform_driver vexpress_sysreg_driver = {
522 538
523static int __init vexpress_sysreg_init(void) 539static int __init vexpress_sysreg_init(void)
524{ 540{
525 vexpress_sysreg_of_early_init(); 541 struct device_node *node;
542
543 /* Need the sysreg early, before any other device... */
544 for_each_matching_node(node, vexpress_sysreg_match)
545 of_platform_device_create(node, NULL, NULL);
546
526 return platform_driver_register(&vexpress_sysreg_driver); 547 return platform_driver_register(&vexpress_sysreg_driver);
527} 548}
528core_initcall(vexpress_sysreg_init); 549core_initcall(vexpress_sysreg_init);