diff options
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
-rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 164 |
1 files changed, 103 insertions, 61 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 3b0cfeb33d05..626ad8cad7a9 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c | |||
@@ -37,14 +37,16 @@ | |||
37 | 37 | ||
38 | #include <linux/module.h> | 38 | #include <linux/module.h> |
39 | #include <linux/io.h> | 39 | #include <linux/io.h> |
40 | #include <linux/slab.h> | 40 | #include <linux/device.h> |
41 | #include <linux/err.h> | 41 | #include <linux/err.h> |
42 | #include <linux/pm_runtime.h> | 42 | #include <linux/pm_runtime.h> |
43 | 43 | ||
44 | #include <plat/dmtimer.h> | 44 | #include <plat/dmtimer.h> |
45 | #include <plat/omap-pm.h> | ||
45 | 46 | ||
46 | #include <mach/hardware.h> | 47 | #include <mach/hardware.h> |
47 | 48 | ||
49 | static u32 omap_reserved_systimers; | ||
48 | static LIST_HEAD(omap_timer_list); | 50 | static LIST_HEAD(omap_timer_list); |
49 | static DEFINE_SPINLOCK(dm_timer_lock); | 51 | static DEFINE_SPINLOCK(dm_timer_lock); |
50 | 52 | ||
@@ -133,17 +135,22 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer) | |||
133 | 135 | ||
134 | int omap_dm_timer_prepare(struct omap_dm_timer *timer) | 136 | int omap_dm_timer_prepare(struct omap_dm_timer *timer) |
135 | { | 137 | { |
136 | struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data; | ||
137 | int ret; | 138 | int ret; |
138 | 139 | ||
139 | timer->fclk = clk_get(&timer->pdev->dev, "fck"); | 140 | /* |
140 | if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) { | 141 | * FIXME: OMAP1 devices do not use the clock framework for dmtimers so |
141 | timer->fclk = NULL; | 142 | * do not call clk_get() for these devices. |
142 | dev_err(&timer->pdev->dev, ": No fclk handle.\n"); | 143 | */ |
143 | return -EINVAL; | 144 | if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { |
145 | timer->fclk = clk_get(&timer->pdev->dev, "fck"); | ||
146 | if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) { | ||
147 | timer->fclk = NULL; | ||
148 | dev_err(&timer->pdev->dev, ": No fclk handle.\n"); | ||
149 | return -EINVAL; | ||
150 | } | ||
144 | } | 151 | } |
145 | 152 | ||
146 | if (pdata->needs_manual_reset) | 153 | if (timer->capability & OMAP_TIMER_NEEDS_RESET) |
147 | omap_dm_timer_reset(timer); | 154 | omap_dm_timer_reset(timer); |
148 | 155 | ||
149 | ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); | 156 | ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); |
@@ -152,6 +159,21 @@ int omap_dm_timer_prepare(struct omap_dm_timer *timer) | |||
152 | return ret; | 159 | return ret; |
153 | } | 160 | } |
154 | 161 | ||
162 | static inline u32 omap_dm_timer_reserved_systimer(int id) | ||
163 | { | ||
164 | return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0; | ||
165 | } | ||
166 | |||
167 | int omap_dm_timer_reserve_systimer(int id) | ||
168 | { | ||
169 | if (omap_dm_timer_reserved_systimer(id)) | ||
170 | return -ENODEV; | ||
171 | |||
172 | omap_reserved_systimers |= (1 << (id - 1)); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
155 | struct omap_dm_timer *omap_dm_timer_request(void) | 177 | struct omap_dm_timer *omap_dm_timer_request(void) |
156 | { | 178 | { |
157 | struct omap_dm_timer *timer = NULL, *t; | 179 | struct omap_dm_timer *timer = NULL, *t; |
@@ -325,10 +347,9 @@ int omap_dm_timer_start(struct omap_dm_timer *timer) | |||
325 | 347 | ||
326 | omap_dm_timer_enable(timer); | 348 | omap_dm_timer_enable(timer); |
327 | 349 | ||
328 | if (timer->loses_context) { | 350 | if (!(timer->capability & OMAP_TIMER_ALWON)) { |
329 | u32 ctx_loss_cnt_after = | 351 | if (omap_pm_get_dev_context_loss_count(&timer->pdev->dev) != |
330 | timer->get_context_loss_count(&timer->pdev->dev); | 352 | timer->ctx_loss_count) |
331 | if (ctx_loss_cnt_after != timer->ctx_loss_count) | ||
332 | omap_timer_restore_context(timer); | 353 | omap_timer_restore_context(timer); |
333 | } | 354 | } |
334 | 355 | ||
@@ -347,20 +368,18 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_start); | |||
347 | int omap_dm_timer_stop(struct omap_dm_timer *timer) | 368 | int omap_dm_timer_stop(struct omap_dm_timer *timer) |
348 | { | 369 | { |
349 | unsigned long rate = 0; | 370 | unsigned long rate = 0; |
350 | struct dmtimer_platform_data *pdata; | ||
351 | 371 | ||
352 | if (unlikely(!timer)) | 372 | if (unlikely(!timer)) |
353 | return -EINVAL; | 373 | return -EINVAL; |
354 | 374 | ||
355 | pdata = timer->pdev->dev.platform_data; | 375 | if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) |
356 | if (!pdata->needs_manual_reset) | ||
357 | rate = clk_get_rate(timer->fclk); | 376 | rate = clk_get_rate(timer->fclk); |
358 | 377 | ||
359 | __omap_dm_timer_stop(timer, timer->posted, rate); | 378 | __omap_dm_timer_stop(timer, timer->posted, rate); |
360 | 379 | ||
361 | if (timer->loses_context && timer->get_context_loss_count) | 380 | if (!(timer->capability & OMAP_TIMER_ALWON)) |
362 | timer->ctx_loss_count = | 381 | timer->ctx_loss_count = |
363 | timer->get_context_loss_count(&timer->pdev->dev); | 382 | omap_pm_get_dev_context_loss_count(&timer->pdev->dev); |
364 | 383 | ||
365 | /* | 384 | /* |
366 | * Since the register values are computed and written within | 385 | * Since the register values are computed and written within |
@@ -378,6 +397,8 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_stop); | |||
378 | int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) | 397 | int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) |
379 | { | 398 | { |
380 | int ret; | 399 | int ret; |
400 | char *parent_name = NULL; | ||
401 | struct clk *fclk, *parent; | ||
381 | struct dmtimer_platform_data *pdata; | 402 | struct dmtimer_platform_data *pdata; |
382 | 403 | ||
383 | if (unlikely(!timer)) | 404 | if (unlikely(!timer)) |
@@ -388,7 +409,49 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) | |||
388 | if (source < 0 || source >= 3) | 409 | if (source < 0 || source >= 3) |
389 | return -EINVAL; | 410 | return -EINVAL; |
390 | 411 | ||
391 | ret = pdata->set_timer_src(timer->pdev, source); | 412 | /* |
413 | * FIXME: Used for OMAP1 devices only because they do not currently | ||
414 | * use the clock framework to set the parent clock. To be removed | ||
415 | * once OMAP1 migrated to using clock framework for dmtimers | ||
416 | */ | ||
417 | if (pdata->set_timer_src) | ||
418 | return pdata->set_timer_src(timer->pdev, source); | ||
419 | |||
420 | fclk = clk_get(&timer->pdev->dev, "fck"); | ||
421 | if (IS_ERR_OR_NULL(fclk)) { | ||
422 | pr_err("%s: fck not found\n", __func__); | ||
423 | return -EINVAL; | ||
424 | } | ||
425 | |||
426 | switch (source) { | ||
427 | case OMAP_TIMER_SRC_SYS_CLK: | ||
428 | parent_name = "timer_sys_ck"; | ||
429 | break; | ||
430 | |||
431 | case OMAP_TIMER_SRC_32_KHZ: | ||
432 | parent_name = "timer_32k_ck"; | ||
433 | break; | ||
434 | |||
435 | case OMAP_TIMER_SRC_EXT_CLK: | ||
436 | parent_name = "timer_ext_ck"; | ||
437 | break; | ||
438 | } | ||
439 | |||
440 | parent = clk_get(&timer->pdev->dev, parent_name); | ||
441 | if (IS_ERR_OR_NULL(parent)) { | ||
442 | pr_err("%s: %s not found\n", __func__, parent_name); | ||
443 | ret = -EINVAL; | ||
444 | goto out; | ||
445 | } | ||
446 | |||
447 | ret = clk_set_parent(fclk, parent); | ||
448 | if (IS_ERR_VALUE(ret)) | ||
449 | pr_err("%s: failed to set %s as parent\n", __func__, | ||
450 | parent_name); | ||
451 | |||
452 | clk_put(parent); | ||
453 | out: | ||
454 | clk_put(fclk); | ||
392 | 455 | ||
393 | return ret; | 456 | return ret; |
394 | } | 457 | } |
@@ -431,10 +494,9 @@ int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, | |||
431 | 494 | ||
432 | omap_dm_timer_enable(timer); | 495 | omap_dm_timer_enable(timer); |
433 | 496 | ||
434 | if (timer->loses_context) { | 497 | if (!(timer->capability & OMAP_TIMER_ALWON)) { |
435 | u32 ctx_loss_cnt_after = | 498 | if (omap_pm_get_dev_context_loss_count(&timer->pdev->dev) != |
436 | timer->get_context_loss_count(&timer->pdev->dev); | 499 | timer->ctx_loss_count) |
437 | if (ctx_loss_cnt_after != timer->ctx_loss_count) | ||
438 | omap_timer_restore_context(timer); | 500 | omap_timer_restore_context(timer); |
439 | } | 501 | } |
440 | 502 | ||
@@ -627,68 +689,57 @@ EXPORT_SYMBOL_GPL(omap_dm_timers_active); | |||
627 | */ | 689 | */ |
628 | static int __devinit omap_dm_timer_probe(struct platform_device *pdev) | 690 | static int __devinit omap_dm_timer_probe(struct platform_device *pdev) |
629 | { | 691 | { |
630 | int ret; | ||
631 | unsigned long flags; | 692 | unsigned long flags; |
632 | struct omap_dm_timer *timer; | 693 | struct omap_dm_timer *timer; |
633 | struct resource *mem, *irq, *ioarea; | 694 | struct resource *mem, *irq; |
695 | struct device *dev = &pdev->dev; | ||
634 | struct dmtimer_platform_data *pdata = pdev->dev.platform_data; | 696 | struct dmtimer_platform_data *pdata = pdev->dev.platform_data; |
635 | 697 | ||
636 | if (!pdata) { | 698 | if (!pdata) { |
637 | dev_err(&pdev->dev, "%s: no platform data.\n", __func__); | 699 | dev_err(dev, "%s: no platform data.\n", __func__); |
638 | return -ENODEV; | 700 | return -ENODEV; |
639 | } | 701 | } |
640 | 702 | ||
641 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 703 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
642 | if (unlikely(!irq)) { | 704 | if (unlikely(!irq)) { |
643 | dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__); | 705 | dev_err(dev, "%s: no IRQ resource.\n", __func__); |
644 | return -ENODEV; | 706 | return -ENODEV; |
645 | } | 707 | } |
646 | 708 | ||
647 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 709 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
648 | if (unlikely(!mem)) { | 710 | if (unlikely(!mem)) { |
649 | dev_err(&pdev->dev, "%s: no memory resource.\n", __func__); | 711 | dev_err(dev, "%s: no memory resource.\n", __func__); |
650 | return -ENODEV; | 712 | return -ENODEV; |
651 | } | 713 | } |
652 | 714 | ||
653 | ioarea = request_mem_region(mem->start, resource_size(mem), | 715 | timer = devm_kzalloc(dev, sizeof(struct omap_dm_timer), GFP_KERNEL); |
654 | pdev->name); | ||
655 | if (!ioarea) { | ||
656 | dev_err(&pdev->dev, "%s: region already claimed.\n", __func__); | ||
657 | return -EBUSY; | ||
658 | } | ||
659 | |||
660 | timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL); | ||
661 | if (!timer) { | 716 | if (!timer) { |
662 | dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n", | 717 | dev_err(dev, "%s: memory alloc failed!\n", __func__); |
663 | __func__); | 718 | return -ENOMEM; |
664 | ret = -ENOMEM; | ||
665 | goto err_free_ioregion; | ||
666 | } | 719 | } |
667 | 720 | ||
668 | timer->io_base = ioremap(mem->start, resource_size(mem)); | 721 | timer->io_base = devm_request_and_ioremap(dev, mem); |
669 | if (!timer->io_base) { | 722 | if (!timer->io_base) { |
670 | dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__); | 723 | dev_err(dev, "%s: region already claimed.\n", __func__); |
671 | ret = -ENOMEM; | 724 | return -ENOMEM; |
672 | goto err_free_mem; | ||
673 | } | 725 | } |
674 | 726 | ||
675 | timer->id = pdev->id; | 727 | timer->id = pdev->id; |
676 | timer->irq = irq->start; | 728 | timer->irq = irq->start; |
677 | timer->reserved = pdata->reserved; | 729 | timer->reserved = omap_dm_timer_reserved_systimer(timer->id); |
678 | timer->pdev = pdev; | 730 | timer->pdev = pdev; |
679 | timer->loses_context = pdata->loses_context; | 731 | timer->capability = pdata->timer_capability; |
680 | timer->get_context_loss_count = pdata->get_context_loss_count; | ||
681 | 732 | ||
682 | /* Skip pm_runtime_enable for OMAP1 */ | 733 | /* Skip pm_runtime_enable for OMAP1 */ |
683 | if (!pdata->needs_manual_reset) { | 734 | if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { |
684 | pm_runtime_enable(&pdev->dev); | 735 | pm_runtime_enable(dev); |
685 | pm_runtime_irq_safe(&pdev->dev); | 736 | pm_runtime_irq_safe(dev); |
686 | } | 737 | } |
687 | 738 | ||
688 | if (!timer->reserved) { | 739 | if (!timer->reserved) { |
689 | pm_runtime_get_sync(&pdev->dev); | 740 | pm_runtime_get_sync(dev); |
690 | __omap_dm_timer_init_regs(timer); | 741 | __omap_dm_timer_init_regs(timer); |
691 | pm_runtime_put(&pdev->dev); | 742 | pm_runtime_put(dev); |
692 | } | 743 | } |
693 | 744 | ||
694 | /* add the timer element to the list */ | 745 | /* add the timer element to the list */ |
@@ -696,17 +747,9 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev) | |||
696 | list_add_tail(&timer->node, &omap_timer_list); | 747 | list_add_tail(&timer->node, &omap_timer_list); |
697 | spin_unlock_irqrestore(&dm_timer_lock, flags); | 748 | spin_unlock_irqrestore(&dm_timer_lock, flags); |
698 | 749 | ||
699 | dev_dbg(&pdev->dev, "Device Probed.\n"); | 750 | dev_dbg(dev, "Device Probed.\n"); |
700 | 751 | ||
701 | return 0; | 752 | return 0; |
702 | |||
703 | err_free_mem: | ||
704 | kfree(timer); | ||
705 | |||
706 | err_free_ioregion: | ||
707 | release_mem_region(mem->start, resource_size(mem)); | ||
708 | |||
709 | return ret; | ||
710 | } | 753 | } |
711 | 754 | ||
712 | /** | 755 | /** |
@@ -727,7 +770,6 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev) | |||
727 | list_for_each_entry(timer, &omap_timer_list, node) | 770 | list_for_each_entry(timer, &omap_timer_list, node) |
728 | if (timer->pdev->id == pdev->id) { | 771 | if (timer->pdev->id == pdev->id) { |
729 | list_del(&timer->node); | 772 | list_del(&timer->node); |
730 | kfree(timer); | ||
731 | ret = 0; | 773 | ret = 0; |
732 | break; | 774 | break; |
733 | } | 775 | } |