aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/sh_tmu.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-08-05 19:48:17 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2012-09-03 19:36:04 -0400
commit61a53bfaa11644b8e9850ac79024b06465a43518 (patch)
treeac150507375f3d384924c58dbce536f8ed55d166 /drivers/clocksource/sh_tmu.c
parente2e3e4e51ebdcd757079bd7ec5dcc9dfb2ebce24 (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/clocksource/sh_tmu.c')
-rw-r--r--drivers/clocksource/sh_tmu.c80
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
50static DEFINE_RAW_SPINLOCK(sh_tmu_lock); 51static 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
112static int sh_tmu_enable(struct sh_tmu_priv *p) 113static 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
140static void sh_tmu_disable(struct sh_tmu_priv *p) 141static 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
152static 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
164static 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
152static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta, 178static 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
236static void sh_tmu_clocksource_resume(struct clocksource *cs) 270static 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
245static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, 283static 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)
447static int __devinit sh_tmu_probe(struct platform_device *pdev) 487static 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
478static int __devexit sh_tmu_remove(struct platform_device *pdev) 528static int __devexit sh_tmu_remove(struct platform_device *pdev)