Root/
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) 2012 ARM Limited |
12 | */ |
13 | |
14 | #include <linux/err.h> |
15 | #include <linux/gpio.h> |
16 | #include <linux/io.h> |
17 | #include <linux/leds.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/regulator/driver.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/stat.h> |
23 | #include <linux/timer.h> |
24 | #include <linux/vexpress.h> |
25 | |
26 | #define SYS_ID 0x000 |
27 | #define SYS_SW 0x004 |
28 | #define SYS_LED 0x008 |
29 | #define SYS_100HZ 0x024 |
30 | #define SYS_FLAGS 0x030 |
31 | #define SYS_FLAGSSET 0x030 |
32 | #define SYS_FLAGSCLR 0x034 |
33 | #define SYS_NVFLAGS 0x038 |
34 | #define SYS_NVFLAGSSET 0x038 |
35 | #define SYS_NVFLAGSCLR 0x03c |
36 | #define SYS_MCI 0x048 |
37 | #define SYS_FLASH 0x04c |
38 | #define SYS_CFGSW 0x058 |
39 | #define SYS_24MHZ 0x05c |
40 | #define SYS_MISC 0x060 |
41 | #define SYS_DMA 0x064 |
42 | #define SYS_PROCID0 0x084 |
43 | #define SYS_PROCID1 0x088 |
44 | #define SYS_CFGDATA 0x0a0 |
45 | #define SYS_CFGCTRL 0x0a4 |
46 | #define SYS_CFGSTAT 0x0a8 |
47 | |
48 | #define SYS_HBI_MASK 0xfff |
49 | #define SYS_ID_HBI_SHIFT 16 |
50 | #define SYS_PROCIDx_HBI_SHIFT 0 |
51 | |
52 | #define SYS_LED_LED(n) (1 << (n)) |
53 | |
54 | #define SYS_MCI_CARDIN (1 << 0) |
55 | #define SYS_MCI_WPROT (1 << 1) |
56 | |
57 | #define SYS_FLASH_WPn (1 << 0) |
58 | |
59 | #define SYS_MISC_MASTERSITE (1 << 14) |
60 | |
61 | #define SYS_CFGCTRL_START (1 << 31) |
62 | #define SYS_CFGCTRL_WRITE (1 << 30) |
63 | #define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) |
64 | #define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) |
65 | #define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) |
66 | #define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) |
67 | #define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) |
68 | |
69 | #define SYS_CFGSTAT_ERR (1 << 1) |
70 | #define SYS_CFGSTAT_COMPLETE (1 << 0) |
71 | |
72 | |
73 | static void __iomem *vexpress_sysreg_base; |
74 | static struct device *vexpress_sysreg_dev; |
75 | static int vexpress_master_site; |
76 | |
77 | |
78 | void vexpress_flags_set(u32 data) |
79 | { |
80 | writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); |
81 | writel(data, vexpress_sysreg_base + SYS_FLAGSSET); |
82 | } |
83 | |
84 | u32 vexpress_get_procid(int site) |
85 | { |
86 | if (site == VEXPRESS_SITE_MASTER) |
87 | site = vexpress_master_site; |
88 | |
89 | return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? |
90 | SYS_PROCID0 : SYS_PROCID1)); |
91 | } |
92 | |
93 | u32 vexpress_get_hbi(int site) |
94 | { |
95 | u32 id; |
96 | |
97 | switch (site) { |
98 | case VEXPRESS_SITE_MB: |
99 | id = readl(vexpress_sysreg_base + SYS_ID); |
100 | return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; |
101 | case VEXPRESS_SITE_MASTER: |
102 | case VEXPRESS_SITE_DB1: |
103 | case VEXPRESS_SITE_DB2: |
104 | id = vexpress_get_procid(site); |
105 | return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; |
106 | } |
107 | |
108 | return ~0; |
109 | } |
110 | |
111 | void __iomem *vexpress_get_24mhz_clock_base(void) |
112 | { |
113 | return vexpress_sysreg_base + SYS_24MHZ; |
114 | } |
115 | |
116 | |
117 | static 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 | |
130 | unsigned __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 | |
157 | struct vexpress_sysreg_config_func { |
158 | u32 template; |
159 | u32 device; |
160 | }; |
161 | |
162 | static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; |
163 | static struct timer_list vexpress_sysreg_config_timer; |
164 | static u32 *vexpress_sysreg_config_data; |
165 | static int vexpress_sysreg_config_tries; |
166 | |
167 | static void *vexpress_sysreg_config_func_get(struct device *dev, |
168 | struct device_node *node) |
169 | { |
170 | struct vexpress_sysreg_config_func *config_func; |
171 | u32 site; |
172 | u32 position = 0; |
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 | |
218 | static void vexpress_sysreg_config_func_put(void *func) |
219 | { |
220 | kfree(func); |
221 | } |
222 | |
223 | static 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 | |
230 | if (WARN_ON(!vexpress_sysreg_base)) |
231 | return -ENOENT; |
232 | |
233 | command = readl(vexpress_sysreg_base + SYS_CFGCTRL); |
234 | if (WARN_ON(command & SYS_CFGCTRL_START)) |
235 | return -EBUSY; |
236 | |
237 | command = SYS_CFGCTRL_START; |
238 | command |= write ? SYS_CFGCTRL_WRITE : 0; |
239 | command |= config_func->template; |
240 | command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); |
241 | |
242 | /* Use a canary for reads */ |
243 | if (!write) |
244 | *data = 0xdeadbeef; |
245 | |
246 | dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", |
247 | command, *data); |
248 | writel(*data, vexpress_sysreg_base + SYS_CFGDATA); |
249 | writel(0, vexpress_sysreg_base + SYS_CFGSTAT); |
250 | writel(command, vexpress_sysreg_base + SYS_CFGCTRL); |
251 | mb(); |
252 | |
253 | if (vexpress_sysreg_dev) { |
254 | /* Schedule completion check */ |
255 | if (!write) |
256 | vexpress_sysreg_config_data = data; |
257 | vexpress_sysreg_config_tries = 100; |
258 | mod_timer(&vexpress_sysreg_config_timer, |
259 | jiffies + usecs_to_jiffies(100)); |
260 | status = VEXPRESS_CONFIG_STATUS_WAIT; |
261 | } else { |
262 | /* Early execution, no timer available, have to spin */ |
263 | u32 cfgstat; |
264 | |
265 | do { |
266 | cpu_relax(); |
267 | cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); |
268 | } while (!cfgstat); |
269 | |
270 | if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) |
271 | *data = readl(vexpress_sysreg_base + SYS_CFGDATA); |
272 | status = VEXPRESS_CONFIG_STATUS_DONE; |
273 | |
274 | if (cfgstat & SYS_CFGSTAT_ERR) |
275 | status = -EINVAL; |
276 | } |
277 | |
278 | return status; |
279 | } |
280 | |
281 | struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { |
282 | .name = "vexpress-sysreg", |
283 | .func_get = vexpress_sysreg_config_func_get, |
284 | .func_put = vexpress_sysreg_config_func_put, |
285 | .func_exec = vexpress_sysreg_config_func_exec, |
286 | }; |
287 | |
288 | static void vexpress_sysreg_config_complete(unsigned long data) |
289 | { |
290 | int status = VEXPRESS_CONFIG_STATUS_DONE; |
291 | u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); |
292 | |
293 | if (cfgstat & SYS_CFGSTAT_ERR) |
294 | status = -EINVAL; |
295 | if (!vexpress_sysreg_config_tries--) |
296 | status = -ETIMEDOUT; |
297 | |
298 | if (status < 0) { |
299 | dev_err(vexpress_sysreg_dev, "error %d\n", status); |
300 | } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { |
301 | mod_timer(&vexpress_sysreg_config_timer, |
302 | jiffies + usecs_to_jiffies(50)); |
303 | return; |
304 | } |
305 | |
306 | if (vexpress_sysreg_config_data) { |
307 | *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + |
308 | SYS_CFGDATA); |
309 | dev_dbg(vexpress_sysreg_dev, "read data %x\n", |
310 | *vexpress_sysreg_config_data); |
311 | vexpress_sysreg_config_data = NULL; |
312 | } |
313 | |
314 | vexpress_config_complete(vexpress_sysreg_config_bridge, status); |
315 | } |
316 | |
317 | |
318 | void vexpress_sysreg_setup(struct device_node *node) |
319 | { |
320 | if (WARN_ON(!vexpress_sysreg_base)) |
321 | return; |
322 | |
323 | if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) |
324 | vexpress_master_site = VEXPRESS_SITE_DB2; |
325 | else |
326 | vexpress_master_site = VEXPRESS_SITE_DB1; |
327 | |
328 | vexpress_sysreg_config_bridge = vexpress_config_bridge_register( |
329 | node, &vexpress_sysreg_config_bridge_info); |
330 | WARN_ON(!vexpress_sysreg_config_bridge); |
331 | } |
332 | |
333 | void __init vexpress_sysreg_early_init(void __iomem *base) |
334 | { |
335 | vexpress_sysreg_base = base; |
336 | vexpress_sysreg_setup(NULL); |
337 | } |
338 | |
339 | void __init vexpress_sysreg_of_early_init(void) |
340 | { |
341 | struct device_node *node; |
342 | |
343 | if (vexpress_sysreg_base) |
344 | return; |
345 | |
346 | node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); |
347 | if (node) { |
348 | vexpress_sysreg_base = of_iomap(node, 0); |
349 | vexpress_sysreg_setup(node); |
350 | } |
351 | } |
352 | |
353 | |
354 | #define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ |
355 | [VEXPRESS_GPIO_##_name] = { \ |
356 | .reg = _reg, \ |
357 | .value = _reg##_##_value, \ |
358 | } |
359 | |
360 | static struct vexpress_sysreg_gpio { |
361 | unsigned long reg; |
362 | u32 value; |
363 | } vexpress_sysreg_gpios[] = { |
364 | VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), |
365 | VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), |
366 | VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), |
367 | VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), |
368 | VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), |
369 | VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), |
370 | VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), |
371 | VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), |
372 | VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), |
373 | VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), |
374 | VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), |
375 | }; |
376 | |
377 | static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, |
378 | unsigned offset) |
379 | { |
380 | return 0; |
381 | } |
382 | |
383 | static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, |
384 | unsigned offset) |
385 | { |
386 | struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; |
387 | u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); |
388 | |
389 | return !!(reg_value & gpio->value); |
390 | } |
391 | |
392 | static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, |
393 | unsigned offset, int value) |
394 | { |
395 | struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; |
396 | u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); |
397 | |
398 | if (value) |
399 | reg_value |= gpio->value; |
400 | else |
401 | reg_value &= ~gpio->value; |
402 | |
403 | writel(reg_value, vexpress_sysreg_base + gpio->reg); |
404 | } |
405 | |
406 | static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, |
407 | unsigned offset, int value) |
408 | { |
409 | vexpress_sysreg_gpio_set(chip, offset, value); |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | static struct gpio_chip vexpress_sysreg_gpio_chip = { |
415 | .label = "vexpress-sysreg", |
416 | .direction_input = vexpress_sysreg_gpio_direction_input, |
417 | .direction_output = vexpress_sysreg_gpio_direction_output, |
418 | .get = vexpress_sysreg_gpio_get, |
419 | .set = vexpress_sysreg_gpio_set, |
420 | .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), |
421 | .base = 0, |
422 | }; |
423 | |
424 | |
425 | #define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ |
426 | { \ |
427 | .name = "v2m:green:"_name, \ |
428 | .default_trigger = _default_trigger, \ |
429 | .gpio = VEXPRESS_GPIO_##_gpio, \ |
430 | } |
431 | |
432 | struct gpio_led vexpress_sysreg_leds[] = { |
433 | VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), |
434 | VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), |
435 | VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), |
436 | VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), |
437 | VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), |
438 | VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), |
439 | VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), |
440 | VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), |
441 | }; |
442 | |
443 | struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { |
444 | .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), |
445 | .leds = vexpress_sysreg_leds, |
446 | }; |
447 | |
448 | |
449 | static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, |
450 | struct device_attribute *attr, char *buf) |
451 | { |
452 | return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); |
453 | } |
454 | |
455 | DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); |
456 | |
457 | static int vexpress_sysreg_probe(struct platform_device *pdev) |
458 | { |
459 | int err; |
460 | struct resource *res = platform_get_resource(pdev, |
461 | IORESOURCE_MEM, 0); |
462 | |
463 | if (!devm_request_mem_region(&pdev->dev, res->start, |
464 | resource_size(res), pdev->name)) { |
465 | dev_err(&pdev->dev, "Failed to request memory region!\n"); |
466 | return -EBUSY; |
467 | } |
468 | |
469 | if (!vexpress_sysreg_base) { |
470 | vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, |
471 | resource_size(res)); |
472 | vexpress_sysreg_setup(pdev->dev.of_node); |
473 | } |
474 | |
475 | if (!vexpress_sysreg_base) { |
476 | dev_err(&pdev->dev, "Failed to obtain base address!\n"); |
477 | return -EFAULT; |
478 | } |
479 | |
480 | setup_timer(&vexpress_sysreg_config_timer, |
481 | vexpress_sysreg_config_complete, 0); |
482 | |
483 | vexpress_sysreg_gpio_chip.dev = &pdev->dev; |
484 | err = gpiochip_add(&vexpress_sysreg_gpio_chip); |
485 | if (err) { |
486 | vexpress_config_bridge_unregister( |
487 | vexpress_sysreg_config_bridge); |
488 | dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", |
489 | err); |
490 | return err; |
491 | } |
492 | |
493 | platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", |
494 | PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, |
495 | sizeof(vexpress_sysreg_leds_pdata)); |
496 | |
497 | vexpress_sysreg_dev = &pdev->dev; |
498 | |
499 | device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); |
500 | |
501 | return 0; |
502 | } |
503 | |
504 | static const struct of_device_id vexpress_sysreg_match[] = { |
505 | { .compatible = "arm,vexpress-sysreg", }, |
506 | {}, |
507 | }; |
508 | |
509 | static struct platform_driver vexpress_sysreg_driver = { |
510 | .driver = { |
511 | .name = "vexpress-sysreg", |
512 | .of_match_table = vexpress_sysreg_match, |
513 | }, |
514 | .probe = vexpress_sysreg_probe, |
515 | }; |
516 | |
517 | static int __init vexpress_sysreg_init(void) |
518 | { |
519 | vexpress_sysreg_of_early_init(); |
520 | return platform_driver_register(&vexpress_sysreg_driver); |
521 | } |
522 | core_initcall(vexpress_sysreg_init); |
523 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9