diff options
author | Ben Dooks <ben@simtec.co.uk> | 2009-10-29 20:30:25 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2009-12-04 08:20:28 -0500 |
commit | e02f838eedef1533f7a1bcf21fe724c3c93093f9 (patch) | |
tree | d1c032ac403a6ebf90fbc8e3294030b3eb83ab2d /drivers/watchdog | |
parent | 22763c5cf3690a681551162c15d34d935308c8d7 (diff) |
[WATCHDOG] CPUFREQ: S3C24XX Watchdog frequency scaling support.
Add support for CPU frequency scaling to the S3C24XX Watchdog
driver.
Signed-off-by: Simtec Linux Team <linux@simtec.co.uk>
Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/s3c2410_wdt.c | 89 |
1 files changed, 86 insertions, 3 deletions
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index b57ac6b49147..85b93e15d011 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/clk.h> | 36 | #include <linux/clk.h> |
37 | #include <linux/uaccess.h> | 37 | #include <linux/uaccess.h> |
38 | #include <linux/io.h> | 38 | #include <linux/io.h> |
39 | #include <linux/cpufreq.h> | ||
39 | 40 | ||
40 | #include <mach/map.h> | 41 | #include <mach/map.h> |
41 | 42 | ||
@@ -142,9 +143,14 @@ static void s3c2410wdt_start(void) | |||
142 | spin_unlock(&wdt_lock); | 143 | spin_unlock(&wdt_lock); |
143 | } | 144 | } |
144 | 145 | ||
146 | static inline int s3c2410wdt_is_running(void) | ||
147 | { | ||
148 | return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; | ||
149 | } | ||
150 | |||
145 | static int s3c2410wdt_set_heartbeat(int timeout) | 151 | static int s3c2410wdt_set_heartbeat(int timeout) |
146 | { | 152 | { |
147 | unsigned int freq = clk_get_rate(wdt_clock); | 153 | unsigned long freq = clk_get_rate(wdt_clock); |
148 | unsigned int count; | 154 | unsigned int count; |
149 | unsigned int divisor = 1; | 155 | unsigned int divisor = 1; |
150 | unsigned long wtcon; | 156 | unsigned long wtcon; |
@@ -155,7 +161,7 @@ static int s3c2410wdt_set_heartbeat(int timeout) | |||
155 | freq /= 128; | 161 | freq /= 128; |
156 | count = timeout * freq; | 162 | count = timeout * freq; |
157 | 163 | ||
158 | DBG("%s: count=%d, timeout=%d, freq=%d\n", | 164 | DBG("%s: count=%d, timeout=%d, freq=%lu\n", |
159 | __func__, count, timeout, freq); | 165 | __func__, count, timeout, freq); |
160 | 166 | ||
161 | /* if the count is bigger than the watchdog register, | 167 | /* if the count is bigger than the watchdog register, |
@@ -324,6 +330,73 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param) | |||
324 | s3c2410wdt_keepalive(); | 330 | s3c2410wdt_keepalive(); |
325 | return IRQ_HANDLED; | 331 | return IRQ_HANDLED; |
326 | } | 332 | } |
333 | |||
334 | |||
335 | #ifdef CONFIG_CPU_FREQ | ||
336 | |||
337 | static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, | ||
338 | unsigned long val, void *data) | ||
339 | { | ||
340 | int ret; | ||
341 | |||
342 | if (!s3c2410wdt_is_running()) | ||
343 | goto done; | ||
344 | |||
345 | if (val == CPUFREQ_PRECHANGE) { | ||
346 | /* To ensure that over the change we don't cause the | ||
347 | * watchdog to trigger, we perform an keep-alive if | ||
348 | * the watchdog is running. | ||
349 | */ | ||
350 | |||
351 | s3c2410wdt_keepalive(); | ||
352 | } else if (val == CPUFREQ_POSTCHANGE) { | ||
353 | s3c2410wdt_stop(); | ||
354 | |||
355 | ret = s3c2410wdt_set_heartbeat(tmr_margin); | ||
356 | |||
357 | if (ret >= 0) | ||
358 | s3c2410wdt_start(); | ||
359 | else | ||
360 | goto err; | ||
361 | } | ||
362 | |||
363 | done: | ||
364 | return 0; | ||
365 | |||
366 | err: | ||
367 | dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin); | ||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | static struct notifier_block s3c2410wdt_cpufreq_transition_nb = { | ||
372 | .notifier_call = s3c2410wdt_cpufreq_transition, | ||
373 | }; | ||
374 | |||
375 | static inline int s3c2410wdt_cpufreq_register(void) | ||
376 | { | ||
377 | return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb, | ||
378 | CPUFREQ_TRANSITION_NOTIFIER); | ||
379 | } | ||
380 | |||
381 | static inline void s3c2410wdt_cpufreq_deregister(void) | ||
382 | { | ||
383 | cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb, | ||
384 | CPUFREQ_TRANSITION_NOTIFIER); | ||
385 | } | ||
386 | |||
387 | #else | ||
388 | static inline int s3c2410wdt_cpufreq_register(void) | ||
389 | { | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static inline void s3c2410wdt_cpufreq_deregister(void) | ||
394 | { | ||
395 | } | ||
396 | #endif | ||
397 | |||
398 | |||
399 | |||
327 | /* device interface */ | 400 | /* device interface */ |
328 | 401 | ||
329 | static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | 402 | static int __devinit s3c2410wdt_probe(struct platform_device *pdev) |
@@ -387,6 +460,11 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | |||
387 | 460 | ||
388 | clk_enable(wdt_clock); | 461 | clk_enable(wdt_clock); |
389 | 462 | ||
463 | if (s3c2410wdt_cpufreq_register() < 0) { | ||
464 | printk(KERN_ERR PFX "failed to register cpufreq\n"); | ||
465 | goto err_clk; | ||
466 | } | ||
467 | |||
390 | /* see if we can actually set the requested timer margin, and if | 468 | /* see if we can actually set the requested timer margin, and if |
391 | * not, try the default value */ | 469 | * not, try the default value */ |
392 | 470 | ||
@@ -407,7 +485,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | |||
407 | if (ret) { | 485 | if (ret) { |
408 | dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", | 486 | dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", |
409 | WATCHDOG_MINOR, ret); | 487 | WATCHDOG_MINOR, ret); |
410 | goto err_clk; | 488 | goto err_cpufreq; |
411 | } | 489 | } |
412 | 490 | ||
413 | if (tmr_atboot && started == 0) { | 491 | if (tmr_atboot && started == 0) { |
@@ -432,6 +510,9 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | |||
432 | 510 | ||
433 | return 0; | 511 | return 0; |
434 | 512 | ||
513 | err_cpufreq: | ||
514 | s3c2410wdt_cpufreq_deregister(); | ||
515 | |||
435 | err_clk: | 516 | err_clk: |
436 | clk_disable(wdt_clock); | 517 | clk_disable(wdt_clock); |
437 | clk_put(wdt_clock); | 518 | clk_put(wdt_clock); |
@@ -451,6 +532,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) | |||
451 | 532 | ||
452 | static int __devexit s3c2410wdt_remove(struct platform_device *dev) | 533 | static int __devexit s3c2410wdt_remove(struct platform_device *dev) |
453 | { | 534 | { |
535 | s3c2410wdt_cpufreq_deregister(); | ||
536 | |||
454 | release_resource(wdt_mem); | 537 | release_resource(wdt_mem); |
455 | kfree(wdt_mem); | 538 | kfree(wdt_mem); |
456 | wdt_mem = NULL; | 539 | wdt_mem = NULL; |