diff options
author | Tony Lindgren <tony@atomide.com> | 2012-11-06 19:09:51 -0500 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2012-11-06 19:09:51 -0500 |
commit | 2580390c563d3a4a6bea820588b188fe76303b5d (patch) | |
tree | 18419de768373eeb00fb215f4924d926a67e508f /arch/arm/mach-omap2/timer.c | |
parent | ad3252556f02cba81f644665ff1fd5a8aa3d97d2 (diff) | |
parent | 9883f7c8dd21acb90697582ca331f3f8a66ac054 (diff) |
Merge branch 'dev-dt-timer' of github.com:jonhunter/linux into omap-for-v3.8/dt
Diffstat (limited to 'arch/arm/mach-omap2/timer.c')
-rw-r--r-- | arch/arm/mach-omap2/timer.c | 203 |
1 files changed, 170 insertions, 33 deletions
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index 69e46631a7cd..0758bae3a57a 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c | |||
@@ -37,6 +37,8 @@ | |||
37 | #include <linux/clockchips.h> | 37 | #include <linux/clockchips.h> |
38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
39 | #include <linux/of.h> | 39 | #include <linux/of.h> |
40 | #include <linux/of_address.h> | ||
41 | #include <linux/of_irq.h> | ||
40 | 42 | ||
41 | #include <asm/mach/time.h> | 43 | #include <asm/mach/time.h> |
42 | #include <asm/smp_twd.h> | 44 | #include <asm/smp_twd.h> |
@@ -66,11 +68,13 @@ | |||
66 | #define OMAP3_CLKEV_SOURCE OMAP3_32K_SOURCE | 68 | #define OMAP3_CLKEV_SOURCE OMAP3_32K_SOURCE |
67 | #define OMAP4_CLKEV_SOURCE OMAP4_32K_SOURCE | 69 | #define OMAP4_CLKEV_SOURCE OMAP4_32K_SOURCE |
68 | #define OMAP3_SECURE_TIMER 12 | 70 | #define OMAP3_SECURE_TIMER 12 |
71 | #define TIMER_PROP_SECURE "ti,timer-secure" | ||
69 | #else | 72 | #else |
70 | #define OMAP2_CLKEV_SOURCE OMAP2_MPU_SOURCE | 73 | #define OMAP2_CLKEV_SOURCE OMAP2_MPU_SOURCE |
71 | #define OMAP3_CLKEV_SOURCE OMAP3_MPU_SOURCE | 74 | #define OMAP3_CLKEV_SOURCE OMAP3_MPU_SOURCE |
72 | #define OMAP4_CLKEV_SOURCE OMAP4_MPU_SOURCE | 75 | #define OMAP4_CLKEV_SOURCE OMAP4_MPU_SOURCE |
73 | #define OMAP3_SECURE_TIMER 1 | 76 | #define OMAP3_SECURE_TIMER 1 |
77 | #define TIMER_PROP_SECURE "ti,timer-alwon" | ||
74 | #endif | 78 | #endif |
75 | 79 | ||
76 | #define REALTIME_COUNTER_BASE 0x48243200 | 80 | #define REALTIME_COUNTER_BASE 0x48243200 |
@@ -144,36 +148,141 @@ static struct clock_event_device clockevent_gpt = { | |||
144 | .set_mode = omap2_gp_timer_set_mode, | 148 | .set_mode = omap2_gp_timer_set_mode, |
145 | }; | 149 | }; |
146 | 150 | ||
151 | static struct property device_disabled = { | ||
152 | .name = "status", | ||
153 | .length = sizeof("disabled"), | ||
154 | .value = "disabled", | ||
155 | }; | ||
156 | |||
157 | static struct of_device_id omap_timer_match[] __initdata = { | ||
158 | { .compatible = "ti,omap2-timer", }, | ||
159 | { } | ||
160 | }; | ||
161 | |||
162 | static struct of_device_id omap_counter_match[] __initdata = { | ||
163 | { .compatible = "ti,omap-counter32k", }, | ||
164 | { } | ||
165 | }; | ||
166 | |||
167 | /** | ||
168 | * omap_get_timer_dt - get a timer using device-tree | ||
169 | * @match - device-tree match structure for matching a device type | ||
170 | * @property - optional timer property to match | ||
171 | * | ||
172 | * Helper function to get a timer during early boot using device-tree for use | ||
173 | * as kernel system timer. Optionally, the property argument can be used to | ||
174 | * select a timer with a specific property. Once a timer is found then mark | ||
175 | * the timer node in device-tree as disabled, to prevent the kernel from | ||
176 | * registering this timer as a platform device and so no one else can use it. | ||
177 | */ | ||
178 | static struct device_node * __init omap_get_timer_dt(struct of_device_id *match, | ||
179 | const char *property) | ||
180 | { | ||
181 | struct device_node *np; | ||
182 | |||
183 | for_each_matching_node(np, match) { | ||
184 | if (!of_device_is_available(np)) { | ||
185 | of_node_put(np); | ||
186 | continue; | ||
187 | } | ||
188 | |||
189 | if (property && !of_get_property(np, property, NULL)) { | ||
190 | of_node_put(np); | ||
191 | continue; | ||
192 | } | ||
193 | |||
194 | prom_add_property(np, &device_disabled); | ||
195 | return np; | ||
196 | } | ||
197 | |||
198 | return NULL; | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * omap_dmtimer_init - initialisation function when device tree is used | ||
203 | * | ||
204 | * For secure OMAP3 devices, timers with device type "timer-secure" cannot | ||
205 | * be used by the kernel as they are reserved. Therefore, to prevent the | ||
206 | * kernel registering these devices remove them dynamically from the device | ||
207 | * tree on boot. | ||
208 | */ | ||
209 | void __init omap_dmtimer_init(void) | ||
210 | { | ||
211 | struct device_node *np; | ||
212 | |||
213 | if (!cpu_is_omap34xx()) | ||
214 | return; | ||
215 | |||
216 | /* If we are a secure device, remove any secure timer nodes */ | ||
217 | if ((omap_type() != OMAP2_DEVICE_TYPE_GP)) { | ||
218 | np = omap_get_timer_dt(omap_timer_match, "ti,timer-secure"); | ||
219 | if (np) | ||
220 | of_node_put(np); | ||
221 | } | ||
222 | } | ||
223 | |||
147 | static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | 224 | static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, |
148 | int gptimer_id, | 225 | int gptimer_id, |
149 | const char *fck_source) | 226 | const char *fck_source, |
227 | const char *property) | ||
150 | { | 228 | { |
151 | char name[10]; /* 10 = sizeof("gptXX_Xck0") */ | 229 | char name[10]; /* 10 = sizeof("gptXX_Xck0") */ |
230 | const char *oh_name; | ||
231 | struct device_node *np; | ||
152 | struct omap_hwmod *oh; | 232 | struct omap_hwmod *oh; |
153 | struct resource irq_rsrc, mem_rsrc; | 233 | struct resource irq_rsrc, mem_rsrc; |
154 | size_t size; | 234 | size_t size; |
155 | int res = 0; | 235 | int res = 0; |
156 | int r; | 236 | int r; |
157 | 237 | ||
158 | sprintf(name, "timer%d", gptimer_id); | 238 | if (of_have_populated_dt()) { |
159 | omap_hwmod_setup_one(name); | 239 | np = omap_get_timer_dt(omap_timer_match, NULL); |
160 | oh = omap_hwmod_lookup(name); | 240 | if (!np) |
241 | return -ENODEV; | ||
242 | |||
243 | of_property_read_string_index(np, "ti,hwmods", 0, &oh_name); | ||
244 | if (!oh_name) | ||
245 | return -ENODEV; | ||
246 | |||
247 | timer->irq = irq_of_parse_and_map(np, 0); | ||
248 | if (!timer->irq) | ||
249 | return -ENXIO; | ||
250 | |||
251 | timer->io_base = of_iomap(np, 0); | ||
252 | |||
253 | of_node_put(np); | ||
254 | } else { | ||
255 | if (omap_dm_timer_reserve_systimer(gptimer_id)) | ||
256 | return -ENODEV; | ||
257 | |||
258 | sprintf(name, "timer%d", gptimer_id); | ||
259 | oh_name = name; | ||
260 | } | ||
261 | |||
262 | omap_hwmod_setup_one(oh_name); | ||
263 | oh = omap_hwmod_lookup(oh_name); | ||
264 | |||
161 | if (!oh) | 265 | if (!oh) |
162 | return -ENODEV; | 266 | return -ENODEV; |
163 | 267 | ||
164 | r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL, &irq_rsrc); | 268 | if (!of_have_populated_dt()) { |
165 | if (r) | 269 | r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL, |
166 | return -ENXIO; | 270 | &irq_rsrc); |
167 | timer->irq = irq_rsrc.start; | 271 | if (r) |
168 | 272 | return -ENXIO; | |
169 | r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL, &mem_rsrc); | 273 | timer->irq = irq_rsrc.start; |
170 | if (r) | 274 | |
171 | return -ENXIO; | 275 | r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL, |
172 | timer->phys_base = mem_rsrc.start; | 276 | &mem_rsrc); |
173 | size = mem_rsrc.end - mem_rsrc.start; | 277 | if (r) |
278 | return -ENXIO; | ||
279 | timer->phys_base = mem_rsrc.start; | ||
280 | size = mem_rsrc.end - mem_rsrc.start; | ||
281 | |||
282 | /* Static mapping, never released */ | ||
283 | timer->io_base = ioremap(timer->phys_base, size); | ||
284 | } | ||
174 | 285 | ||
175 | /* Static mapping, never released */ | ||
176 | timer->io_base = ioremap(timer->phys_base, size); | ||
177 | if (!timer->io_base) | 286 | if (!timer->io_base) |
178 | return -ENXIO; | 287 | return -ENXIO; |
179 | 288 | ||
@@ -184,9 +293,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | |||
184 | 293 | ||
185 | omap_hwmod_enable(oh); | 294 | omap_hwmod_enable(oh); |
186 | 295 | ||
187 | if (omap_dm_timer_reserve_systimer(gptimer_id)) | 296 | /* FIXME: Need to remove hard-coded test on timer ID */ |
188 | return -ENODEV; | ||
189 | |||
190 | if (gptimer_id != 12) { | 297 | if (gptimer_id != 12) { |
191 | struct clk *src; | 298 | struct clk *src; |
192 | 299 | ||
@@ -196,8 +303,8 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | |||
196 | } else { | 303 | } else { |
197 | res = __omap_dm_timer_set_source(timer->fclk, src); | 304 | res = __omap_dm_timer_set_source(timer->fclk, src); |
198 | if (IS_ERR_VALUE(res)) | 305 | if (IS_ERR_VALUE(res)) |
199 | pr_warning("%s: timer%i cannot set source\n", | 306 | pr_warn("%s: %s cannot set source\n", |
200 | __func__, gptimer_id); | 307 | __func__, oh->name); |
201 | clk_put(src); | 308 | clk_put(src); |
202 | } | 309 | } |
203 | } | 310 | } |
@@ -213,11 +320,12 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | |||
213 | } | 320 | } |
214 | 321 | ||
215 | static void __init omap2_gp_clockevent_init(int gptimer_id, | 322 | static void __init omap2_gp_clockevent_init(int gptimer_id, |
216 | const char *fck_source) | 323 | const char *fck_source, |
324 | const char *property) | ||
217 | { | 325 | { |
218 | int res; | 326 | int res; |
219 | 327 | ||
220 | res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source); | 328 | res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source, property); |
221 | BUG_ON(res); | 329 | BUG_ON(res); |
222 | 330 | ||
223 | omap2_gp_timer_irq.dev_id = &clkev; | 331 | omap2_gp_timer_irq.dev_id = &clkev; |
@@ -274,11 +382,26 @@ static u32 notrace dmtimer_read_sched_clock(void) | |||
274 | static int __init omap2_sync32k_clocksource_init(void) | 382 | static int __init omap2_sync32k_clocksource_init(void) |
275 | { | 383 | { |
276 | int ret; | 384 | int ret; |
385 | struct device_node *np = NULL; | ||
277 | struct omap_hwmod *oh; | 386 | struct omap_hwmod *oh; |
278 | void __iomem *vbase; | 387 | void __iomem *vbase; |
279 | const char *oh_name = "counter_32k"; | 388 | const char *oh_name = "counter_32k"; |
280 | 389 | ||
281 | /* | 390 | /* |
391 | * If device-tree is present, then search the DT blob | ||
392 | * to see if the 32kHz counter is supported. | ||
393 | */ | ||
394 | if (of_have_populated_dt()) { | ||
395 | np = omap_get_timer_dt(omap_counter_match, NULL); | ||
396 | if (!np) | ||
397 | return -ENODEV; | ||
398 | |||
399 | of_property_read_string_index(np, "ti,hwmods", 0, &oh_name); | ||
400 | if (!oh_name) | ||
401 | return -ENODEV; | ||
402 | } | ||
403 | |||
404 | /* | ||
282 | * First check hwmod data is available for sync32k counter | 405 | * First check hwmod data is available for sync32k counter |
283 | */ | 406 | */ |
284 | oh = omap_hwmod_lookup(oh_name); | 407 | oh = omap_hwmod_lookup(oh_name); |
@@ -287,7 +410,13 @@ static int __init omap2_sync32k_clocksource_init(void) | |||
287 | 410 | ||
288 | omap_hwmod_setup_one(oh_name); | 411 | omap_hwmod_setup_one(oh_name); |
289 | 412 | ||
290 | vbase = omap_hwmod_get_mpu_rt_va(oh); | 413 | if (np) { |
414 | vbase = of_iomap(np, 0); | ||
415 | of_node_put(np); | ||
416 | } else { | ||
417 | vbase = omap_hwmod_get_mpu_rt_va(oh); | ||
418 | } | ||
419 | |||
291 | if (!vbase) { | 420 | if (!vbase) { |
292 | pr_warn("%s: failed to get counter_32k resource\n", __func__); | 421 | pr_warn("%s: failed to get counter_32k resource\n", __func__); |
293 | return -ENXIO; | 422 | return -ENXIO; |
@@ -321,7 +450,7 @@ static void __init omap2_gptimer_clocksource_init(int gptimer_id, | |||
321 | { | 450 | { |
322 | int res; | 451 | int res; |
323 | 452 | ||
324 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); | 453 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source, NULL); |
325 | BUG_ON(res); | 454 | BUG_ON(res); |
326 | 455 | ||
327 | __omap_dm_timer_load_start(&clksrc, | 456 | __omap_dm_timer_load_start(&clksrc, |
@@ -433,11 +562,12 @@ static inline void __init realtime_counter_init(void) | |||
433 | {} | 562 | {} |
434 | #endif | 563 | #endif |
435 | 564 | ||
436 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ | 565 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, clkev_prop, \ |
437 | clksrc_nr, clksrc_src) \ | 566 | clksrc_nr, clksrc_src) \ |
438 | static void __init omap##name##_timer_init(void) \ | 567 | static void __init omap##name##_timer_init(void) \ |
439 | { \ | 568 | { \ |
440 | omap2_gp_clockevent_init((clkev_nr), clkev_src); \ | 569 | omap_dmtimer_init(); \ |
570 | omap2_gp_clockevent_init((clkev_nr), clkev_src, clkev_prop); \ | ||
441 | omap2_clocksource_init((clksrc_nr), clksrc_src); \ | 571 | omap2_clocksource_init((clksrc_nr), clksrc_src); \ |
442 | } | 572 | } |
443 | 573 | ||
@@ -447,20 +577,23 @@ struct sys_timer omap##name##_timer = { \ | |||
447 | }; | 577 | }; |
448 | 578 | ||
449 | #ifdef CONFIG_ARCH_OMAP2 | 579 | #ifdef CONFIG_ARCH_OMAP2 |
450 | OMAP_SYS_TIMER_INIT(2, 1, OMAP2_CLKEV_SOURCE, 2, OMAP2_MPU_SOURCE) | 580 | OMAP_SYS_TIMER_INIT(2, 1, OMAP2_CLKEV_SOURCE, "ti,timer-alwon", |
581 | 2, OMAP2_MPU_SOURCE) | ||
451 | OMAP_SYS_TIMER(2) | 582 | OMAP_SYS_TIMER(2) |
452 | #endif | 583 | #endif |
453 | 584 | ||
454 | #ifdef CONFIG_ARCH_OMAP3 | 585 | #ifdef CONFIG_ARCH_OMAP3 |
455 | OMAP_SYS_TIMER_INIT(3, 1, OMAP3_CLKEV_SOURCE, 2, OMAP3_MPU_SOURCE) | 586 | OMAP_SYS_TIMER_INIT(3, 1, OMAP3_CLKEV_SOURCE, "ti,timer-alwon", |
587 | 2, OMAP3_MPU_SOURCE) | ||
456 | OMAP_SYS_TIMER(3) | 588 | OMAP_SYS_TIMER(3) |
457 | OMAP_SYS_TIMER_INIT(3_secure, OMAP3_SECURE_TIMER, OMAP3_CLKEV_SOURCE, | 589 | OMAP_SYS_TIMER_INIT(3_secure, OMAP3_SECURE_TIMER, OMAP3_CLKEV_SOURCE, |
458 | 2, OMAP3_MPU_SOURCE) | 590 | TIMER_PROP_SECURE, 2, OMAP3_MPU_SOURCE) |
459 | OMAP_SYS_TIMER(3_secure) | 591 | OMAP_SYS_TIMER(3_secure) |
460 | #endif | 592 | #endif |
461 | 593 | ||
462 | #ifdef CONFIG_SOC_AM33XX | 594 | #ifdef CONFIG_SOC_AM33XX |
463 | OMAP_SYS_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, 2, OMAP4_MPU_SOURCE) | 595 | OMAP_SYS_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, "ti,timer-alwon", |
596 | 2, OMAP4_MPU_SOURCE) | ||
464 | OMAP_SYS_TIMER(3_am33xx) | 597 | OMAP_SYS_TIMER(3_am33xx) |
465 | #endif | 598 | #endif |
466 | 599 | ||
@@ -472,7 +605,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, | |||
472 | 605 | ||
473 | static void __init omap4_timer_init(void) | 606 | static void __init omap4_timer_init(void) |
474 | { | 607 | { |
475 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); | 608 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE, "ti,timer-alwon"); |
476 | omap2_clocksource_init(2, OMAP4_MPU_SOURCE); | 609 | omap2_clocksource_init(2, OMAP4_MPU_SOURCE); |
477 | #ifdef CONFIG_LOCAL_TIMERS | 610 | #ifdef CONFIG_LOCAL_TIMERS |
478 | /* Local timers are not supprted on OMAP4430 ES1.0 */ | 611 | /* Local timers are not supprted on OMAP4430 ES1.0 */ |
@@ -498,7 +631,7 @@ static void __init omap5_timer_init(void) | |||
498 | { | 631 | { |
499 | int err; | 632 | int err; |
500 | 633 | ||
501 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); | 634 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE, "ti,timer-alwon"); |
502 | omap2_clocksource_init(2, OMAP4_MPU_SOURCE); | 635 | omap2_clocksource_init(2, OMAP4_MPU_SOURCE); |
503 | realtime_counter_init(); | 636 | realtime_counter_init(); |
504 | 637 | ||
@@ -583,6 +716,10 @@ static int __init omap2_dm_timer_init(void) | |||
583 | { | 716 | { |
584 | int ret; | 717 | int ret; |
585 | 718 | ||
719 | /* If dtb is there, the devices will be created dynamically */ | ||
720 | if (of_have_populated_dt()) | ||
721 | return -ENODEV; | ||
722 | |||
586 | ret = omap_hwmod_for_each_by_class("timer", omap_timer_init, NULL); | 723 | ret = omap_hwmod_for_each_by_class("timer", omap_timer_init, NULL); |
587 | if (unlikely(ret)) { | 724 | if (unlikely(ret)) { |
588 | pr_err("%s: device registration failed.\n", __func__); | 725 | pr_err("%s: device registration failed.\n", __func__); |