diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-08-05 19:48:57 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-09-03 19:36:04 -0400 |
commit | bad813831e291cf34a007e6f03c37cf95037c868 (patch) | |
tree | 518e2066116ae87d0026f8d65c23f0d6e5afa373 /drivers/clocksource | |
parent | 61a53bfaa11644b8e9850ac79024b06465a43518 (diff) |
sh: CMT: Basic runtime PM support
Modify the SH CMT clock source/clock event device driver to support
runtime PM at a basic level (i.e. device clocks can be disabled and
enabled, but domain power must be on, because the devices have to
be marked as "irq safe").
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Magnus Damm <damm@opensource.se>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/sh_cmt.c | 44 |
1 files changed, 36 insertions, 8 deletions
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index a515605bf8f5..a5f7829f2799 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <linux/pm_domain.h> | 35 | #include <linux/pm_domain.h> |
36 | #include <linux/pm_runtime.h> | ||
36 | 37 | ||
37 | struct sh_cmt_priv { | 38 | struct sh_cmt_priv { |
38 | void __iomem *mapbase; | 39 | void __iomem *mapbase; |
@@ -52,6 +53,7 @@ struct sh_cmt_priv { | |||
52 | struct clock_event_device ced; | 53 | struct clock_event_device ced; |
53 | struct clocksource cs; | 54 | struct clocksource cs; |
54 | unsigned long total_cycles; | 55 | unsigned long total_cycles; |
56 | bool cs_enabled; | ||
55 | }; | 57 | }; |
56 | 58 | ||
57 | static DEFINE_RAW_SPINLOCK(sh_cmt_lock); | 59 | static DEFINE_RAW_SPINLOCK(sh_cmt_lock); |
@@ -155,6 +157,9 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) | |||
155 | { | 157 | { |
156 | int k, ret; | 158 | int k, ret; |
157 | 159 | ||
160 | pm_runtime_get_sync(&p->pdev->dev); | ||
161 | dev_pm_syscore_device(&p->pdev->dev, true); | ||
162 | |||
158 | /* enable clock */ | 163 | /* enable clock */ |
159 | ret = clk_enable(p->clk); | 164 | ret = clk_enable(p->clk); |
160 | if (ret) { | 165 | if (ret) { |
@@ -221,6 +226,9 @@ static void sh_cmt_disable(struct sh_cmt_priv *p) | |||
221 | 226 | ||
222 | /* stop clock */ | 227 | /* stop clock */ |
223 | clk_disable(p->clk); | 228 | clk_disable(p->clk); |
229 | |||
230 | dev_pm_syscore_device(&p->pdev->dev, false); | ||
231 | pm_runtime_put(&p->pdev->dev); | ||
224 | } | 232 | } |
225 | 233 | ||
226 | /* private flags */ | 234 | /* private flags */ |
@@ -451,17 +459,26 @@ static int sh_cmt_clocksource_enable(struct clocksource *cs) | |||
451 | int ret; | 459 | int ret; |
452 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); | 460 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); |
453 | 461 | ||
462 | WARN_ON(p->cs_enabled); | ||
463 | |||
454 | p->total_cycles = 0; | 464 | p->total_cycles = 0; |
455 | 465 | ||
456 | ret = sh_cmt_start(p, FLAG_CLOCKSOURCE); | 466 | ret = sh_cmt_start(p, FLAG_CLOCKSOURCE); |
457 | if (!ret) | 467 | if (!ret) { |
458 | __clocksource_updatefreq_hz(cs, p->rate); | 468 | __clocksource_updatefreq_hz(cs, p->rate); |
469 | p->cs_enabled = true; | ||
470 | } | ||
459 | return ret; | 471 | return ret; |
460 | } | 472 | } |
461 | 473 | ||
462 | static void sh_cmt_clocksource_disable(struct clocksource *cs) | 474 | static void sh_cmt_clocksource_disable(struct clocksource *cs) |
463 | { | 475 | { |
464 | sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); | 476 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); |
477 | |||
478 | WARN_ON(!p->cs_enabled); | ||
479 | |||
480 | sh_cmt_stop(p, FLAG_CLOCKSOURCE); | ||
481 | p->cs_enabled = false; | ||
465 | } | 482 | } |
466 | 483 | ||
467 | static void sh_cmt_clocksource_suspend(struct clocksource *cs) | 484 | static void sh_cmt_clocksource_suspend(struct clocksource *cs) |
@@ -693,6 +710,7 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
693 | dev_err(&p->pdev->dev, "registration failed\n"); | 710 | dev_err(&p->pdev->dev, "registration failed\n"); |
694 | goto err1; | 711 | goto err1; |
695 | } | 712 | } |
713 | p->cs_enabled = false; | ||
696 | 714 | ||
697 | ret = setup_irq(irq, &p->irqaction); | 715 | ret = setup_irq(irq, &p->irqaction); |
698 | if (ret) { | 716 | if (ret) { |
@@ -711,18 +729,17 @@ err0: | |||
711 | static int __devinit sh_cmt_probe(struct platform_device *pdev) | 729 | static int __devinit sh_cmt_probe(struct platform_device *pdev) |
712 | { | 730 | { |
713 | struct sh_cmt_priv *p = platform_get_drvdata(pdev); | 731 | struct sh_cmt_priv *p = platform_get_drvdata(pdev); |
732 | struct sh_timer_config *cfg = pdev->dev.platform_data; | ||
714 | int ret; | 733 | int ret; |
715 | 734 | ||
716 | if (!is_early_platform_device(pdev)) { | 735 | if (!is_early_platform_device(pdev)) { |
717 | struct sh_timer_config *cfg = pdev->dev.platform_data; | 736 | pm_runtime_set_active(&pdev->dev); |
718 | 737 | pm_runtime_enable(&pdev->dev); | |
719 | if (cfg->clocksource_rating || cfg->clockevent_rating) | ||
720 | dev_pm_syscore_device(&pdev->dev, true); | ||
721 | } | 738 | } |
722 | 739 | ||
723 | if (p) { | 740 | if (p) { |
724 | dev_info(&pdev->dev, "kept as earlytimer\n"); | 741 | dev_info(&pdev->dev, "kept as earlytimer\n"); |
725 | return 0; | 742 | goto out; |
726 | } | 743 | } |
727 | 744 | ||
728 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 745 | p = kmalloc(sizeof(*p), GFP_KERNEL); |
@@ -735,8 +752,19 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev) | |||
735 | if (ret) { | 752 | if (ret) { |
736 | kfree(p); | 753 | kfree(p); |
737 | platform_set_drvdata(pdev, NULL); | 754 | platform_set_drvdata(pdev, NULL); |
755 | pm_runtime_idle(&pdev->dev); | ||
756 | return ret; | ||
738 | } | 757 | } |
739 | return ret; | 758 | if (is_early_platform_device(pdev)) |
759 | return 0; | ||
760 | |||
761 | out: | ||
762 | if (cfg->clockevent_rating || cfg->clocksource_rating) | ||
763 | pm_runtime_irq_safe(&pdev->dev); | ||
764 | else | ||
765 | pm_runtime_idle(&pdev->dev); | ||
766 | |||
767 | return 0; | ||
740 | } | 768 | } |
741 | 769 | ||
742 | static int __devexit sh_cmt_remove(struct platform_device *pdev) | 770 | static int __devexit sh_cmt_remove(struct platform_device *pdev) |