diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2012-08-05 19:48:17 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2012-09-03 19:36:04 -0400 |
commit | 61a53bfaa11644b8e9850ac79024b06465a43518 (patch) | |
tree | ac150507375f3d384924c58dbce536f8ed55d166 /drivers | |
parent | e2e3e4e51ebdcd757079bd7ec5dcc9dfb2ebce24 (diff) |
sh: TMU: Basic runtime PM support
Modify the SH TMU 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')
-rw-r--r-- | drivers/clocksource/sh_tmu.c | 80 |
1 files changed, 65 insertions, 15 deletions
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 81b0239718ee..0cc4add88279 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c | |||
@@ -45,6 +45,7 @@ struct sh_tmu_priv { | |||
45 | struct clock_event_device ced; | 45 | struct clock_event_device ced; |
46 | struct clocksource cs; | 46 | struct clocksource cs; |
47 | bool cs_enabled; | 47 | bool cs_enabled; |
48 | unsigned int enable_count; | ||
48 | }; | 49 | }; |
49 | 50 | ||
50 | static DEFINE_RAW_SPINLOCK(sh_tmu_lock); | 51 | static DEFINE_RAW_SPINLOCK(sh_tmu_lock); |
@@ -109,7 +110,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start) | |||
109 | raw_spin_unlock_irqrestore(&sh_tmu_lock, flags); | 110 | raw_spin_unlock_irqrestore(&sh_tmu_lock, flags); |
110 | } | 111 | } |
111 | 112 | ||
112 | static int sh_tmu_enable(struct sh_tmu_priv *p) | 113 | static int __sh_tmu_enable(struct sh_tmu_priv *p) |
113 | { | 114 | { |
114 | int ret; | 115 | int ret; |
115 | 116 | ||
@@ -137,7 +138,18 @@ static int sh_tmu_enable(struct sh_tmu_priv *p) | |||
137 | return 0; | 138 | return 0; |
138 | } | 139 | } |
139 | 140 | ||
140 | static void sh_tmu_disable(struct sh_tmu_priv *p) | 141 | static int sh_tmu_enable(struct sh_tmu_priv *p) |
142 | { | ||
143 | if (p->enable_count++ > 0) | ||
144 | return 0; | ||
145 | |||
146 | pm_runtime_get_sync(&p->pdev->dev); | ||
147 | dev_pm_syscore_device(&p->pdev->dev, true); | ||
148 | |||
149 | return __sh_tmu_enable(p); | ||
150 | } | ||
151 | |||
152 | static void __sh_tmu_disable(struct sh_tmu_priv *p) | ||
141 | { | 153 | { |
142 | /* disable channel */ | 154 | /* disable channel */ |
143 | sh_tmu_start_stop_ch(p, 0); | 155 | sh_tmu_start_stop_ch(p, 0); |
@@ -149,6 +161,20 @@ static void sh_tmu_disable(struct sh_tmu_priv *p) | |||
149 | clk_disable(p->clk); | 161 | clk_disable(p->clk); |
150 | } | 162 | } |
151 | 163 | ||
164 | static void sh_tmu_disable(struct sh_tmu_priv *p) | ||
165 | { | ||
166 | if (WARN_ON(p->enable_count == 0)) | ||
167 | return; | ||
168 | |||
169 | if (--p->enable_count > 0) | ||
170 | return; | ||
171 | |||
172 | __sh_tmu_disable(p); | ||
173 | |||
174 | dev_pm_syscore_device(&p->pdev->dev, false); | ||
175 | pm_runtime_put(&p->pdev->dev); | ||
176 | } | ||
177 | |||
152 | static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta, | 178 | static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta, |
153 | int periodic) | 179 | int periodic) |
154 | { | 180 | { |
@@ -205,11 +231,15 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs) | |||
205 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); | 231 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); |
206 | int ret; | 232 | int ret; |
207 | 233 | ||
234 | if (WARN_ON(p->cs_enabled)) | ||
235 | return 0; | ||
236 | |||
208 | ret = sh_tmu_enable(p); | 237 | ret = sh_tmu_enable(p); |
209 | if (!ret) { | 238 | if (!ret) { |
210 | __clocksource_updatefreq_hz(cs, p->rate); | 239 | __clocksource_updatefreq_hz(cs, p->rate); |
211 | p->cs_enabled = true; | 240 | p->cs_enabled = true; |
212 | } | 241 | } |
242 | |||
213 | return ret; | 243 | return ret; |
214 | } | 244 | } |
215 | 245 | ||
@@ -217,7 +247,8 @@ static void sh_tmu_clocksource_disable(struct clocksource *cs) | |||
217 | { | 247 | { |
218 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); | 248 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); |
219 | 249 | ||
220 | WARN_ON(!p->cs_enabled); | 250 | if (WARN_ON(!p->cs_enabled)) |
251 | return; | ||
221 | 252 | ||
222 | sh_tmu_disable(p); | 253 | sh_tmu_disable(p); |
223 | p->cs_enabled = false; | 254 | p->cs_enabled = false; |
@@ -227,19 +258,26 @@ static void sh_tmu_clocksource_suspend(struct clocksource *cs) | |||
227 | { | 258 | { |
228 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); | 259 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); |
229 | 260 | ||
230 | if (p->cs_enabled) | 261 | if (!p->cs_enabled) |
231 | sh_tmu_disable(p); | 262 | return; |
232 | 263 | ||
233 | pm_genpd_syscore_poweroff(&p->pdev->dev); | 264 | if (--p->enable_count == 0) { |
265 | __sh_tmu_disable(p); | ||
266 | pm_genpd_syscore_poweroff(&p->pdev->dev); | ||
267 | } | ||
234 | } | 268 | } |
235 | 269 | ||
236 | static void sh_tmu_clocksource_resume(struct clocksource *cs) | 270 | static void sh_tmu_clocksource_resume(struct clocksource *cs) |
237 | { | 271 | { |
238 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); | 272 | struct sh_tmu_priv *p = cs_to_sh_tmu(cs); |
239 | 273 | ||
240 | pm_genpd_syscore_poweron(&p->pdev->dev); | 274 | if (!p->cs_enabled) |
241 | if (p->cs_enabled) | 275 | return; |
242 | sh_tmu_enable(p); | 276 | |
277 | if (p->enable_count++ == 0) { | ||
278 | pm_genpd_syscore_poweron(&p->pdev->dev); | ||
279 | __sh_tmu_enable(p); | ||
280 | } | ||
243 | } | 281 | } |
244 | 282 | ||
245 | static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, | 283 | static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, |
@@ -434,6 +472,8 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) | |||
434 | ret = PTR_ERR(p->clk); | 472 | ret = PTR_ERR(p->clk); |
435 | goto err1; | 473 | goto err1; |
436 | } | 474 | } |
475 | p->cs_enabled = false; | ||
476 | p->enable_count = 0; | ||
437 | 477 | ||
438 | return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), | 478 | return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev), |
439 | cfg->clockevent_rating, | 479 | cfg->clockevent_rating, |
@@ -447,18 +487,17 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev) | |||
447 | static int __devinit sh_tmu_probe(struct platform_device *pdev) | 487 | static int __devinit sh_tmu_probe(struct platform_device *pdev) |
448 | { | 488 | { |
449 | struct sh_tmu_priv *p = platform_get_drvdata(pdev); | 489 | struct sh_tmu_priv *p = platform_get_drvdata(pdev); |
490 | struct sh_timer_config *cfg = pdev->dev.platform_data; | ||
450 | int ret; | 491 | int ret; |
451 | 492 | ||
452 | if (!is_early_platform_device(pdev)) { | 493 | if (!is_early_platform_device(pdev)) { |
453 | struct sh_timer_config *cfg = pdev->dev.platform_data; | 494 | pm_runtime_set_active(&pdev->dev); |
454 | 495 | pm_runtime_enable(&pdev->dev); | |
455 | if (cfg->clocksource_rating || cfg->clockevent_rating) | ||
456 | dev_pm_syscore_device(&pdev->dev, true); | ||
457 | } | 496 | } |
458 | 497 | ||
459 | if (p) { | 498 | if (p) { |
460 | dev_info(&pdev->dev, "kept as earlytimer\n"); | 499 | dev_info(&pdev->dev, "kept as earlytimer\n"); |
461 | return 0; | 500 | goto out; |
462 | } | 501 | } |
463 | 502 | ||
464 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 503 | p = kmalloc(sizeof(*p), GFP_KERNEL); |
@@ -471,8 +510,19 @@ static int __devinit sh_tmu_probe(struct platform_device *pdev) | |||
471 | if (ret) { | 510 | if (ret) { |
472 | kfree(p); | 511 | kfree(p); |
473 | platform_set_drvdata(pdev, NULL); | 512 | platform_set_drvdata(pdev, NULL); |
513 | pm_runtime_idle(&pdev->dev); | ||
514 | return ret; | ||
474 | } | 515 | } |
475 | return ret; | 516 | if (is_early_platform_device(pdev)) |
517 | return 0; | ||
518 | |||
519 | out: | ||
520 | if (cfg->clockevent_rating || cfg->clocksource_rating) | ||
521 | pm_runtime_irq_safe(&pdev->dev); | ||
522 | else | ||
523 | pm_runtime_idle(&pdev->dev); | ||
524 | |||
525 | return 0; | ||
476 | } | 526 | } |
477 | 527 | ||
478 | static int __devexit sh_tmu_remove(struct platform_device *pdev) | 528 | static int __devexit sh_tmu_remove(struct platform_device *pdev) |