diff options
| -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; |
