diff options
| -rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/watchdog/octeon-wdt-main.c | 185 |
2 files changed, 39 insertions, 147 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 6442412377a4..7acc71ec6048 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -1189,6 +1189,7 @@ config OCTEON_WDT | |||
| 1189 | tristate "Cavium OCTEON SOC family Watchdog Timer" | 1189 | tristate "Cavium OCTEON SOC family Watchdog Timer" |
| 1190 | depends on CAVIUM_OCTEON_SOC | 1190 | depends on CAVIUM_OCTEON_SOC |
| 1191 | default y | 1191 | default y |
| 1192 | select WATCHDOG_CORE | ||
| 1192 | select EXPORT_UASM if OCTEON_WDT = m | 1193 | select EXPORT_UASM if OCTEON_WDT = m |
| 1193 | help | 1194 | help |
| 1194 | Hardware driver for OCTEON's on chip watchdog timer. | 1195 | Hardware driver for OCTEON's on chip watchdog timer. |
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c index 8453531545df..9aa5121d7d50 100644 --- a/drivers/watchdog/octeon-wdt-main.c +++ b/drivers/watchdog/octeon-wdt-main.c | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2007, 2008, 2009, 2010 Cavium Networks | 4 | * Copyright (C) 2007, 2008, 2009, 2010 Cavium Networks |
| 5 | * | 5 | * |
| 6 | * Converted to use WATCHDOG_CORE by Aaro Koskinen <aaro.koskinen@iki.fi>. | ||
| 7 | * | ||
| 6 | * Some parts derived from wdt.c | 8 | * Some parts derived from wdt.c |
| 7 | * | 9 | * |
| 8 | * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, | 10 | * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, |
| @@ -103,9 +105,6 @@ MODULE_PARM_DESC(nowayout, | |||
| 103 | "Watchdog cannot be stopped once started (default=" | 105 | "Watchdog cannot be stopped once started (default=" |
| 104 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 106 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
| 105 | 107 | ||
| 106 | static unsigned long octeon_wdt_is_open; | ||
| 107 | static char expect_close; | ||
| 108 | |||
| 109 | static u32 __initdata nmi_stage1_insns[64]; | 108 | static u32 __initdata nmi_stage1_insns[64]; |
| 110 | /* We need one branch and therefore one relocation per target label. */ | 109 | /* We need one branch and therefore one relocation per target label. */ |
| 111 | static struct uasm_label __initdata labels[5]; | 110 | static struct uasm_label __initdata labels[5]; |
| @@ -444,7 +443,7 @@ static int octeon_wdt_cpu_callback(struct notifier_block *nfb, | |||
| 444 | return NOTIFY_OK; | 443 | return NOTIFY_OK; |
| 445 | } | 444 | } |
| 446 | 445 | ||
| 447 | static void octeon_wdt_ping(void) | 446 | static int octeon_wdt_ping(struct watchdog_device __always_unused *wdog) |
| 448 | { | 447 | { |
| 449 | int cpu; | 448 | int cpu; |
| 450 | int coreid; | 449 | int coreid; |
| @@ -461,6 +460,7 @@ static void octeon_wdt_ping(void) | |||
| 461 | cpumask_set_cpu(cpu, &irq_enabled_cpus); | 460 | cpumask_set_cpu(cpu, &irq_enabled_cpus); |
| 462 | } | 461 | } |
| 463 | } | 462 | } |
| 463 | return 0; | ||
| 464 | } | 464 | } |
| 465 | 465 | ||
| 466 | static void octeon_wdt_calc_parameters(int t) | 466 | static void octeon_wdt_calc_parameters(int t) |
| @@ -489,7 +489,8 @@ static void octeon_wdt_calc_parameters(int t) | |||
| 489 | timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * timeout_sec) >> 8; | 489 | timeout_cnt = ((octeon_get_io_clock_rate() >> 8) * timeout_sec) >> 8; |
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | static int octeon_wdt_set_heartbeat(int t) | 492 | static int octeon_wdt_set_timeout(struct watchdog_device *wdog, |
| 493 | unsigned int t) | ||
| 493 | { | 494 | { |
| 494 | int cpu; | 495 | int cpu; |
| 495 | int coreid; | 496 | int coreid; |
| @@ -509,158 +510,45 @@ static int octeon_wdt_set_heartbeat(int t) | |||
| 509 | cvmx_write_csr(CVMX_CIU_WDOGX(coreid), ciu_wdog.u64); | 510 | cvmx_write_csr(CVMX_CIU_WDOGX(coreid), ciu_wdog.u64); |
| 510 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); | 511 | cvmx_write_csr(CVMX_CIU_PP_POKEX(coreid), 1); |
| 511 | } | 512 | } |
| 512 | octeon_wdt_ping(); /* Get the irqs back on. */ | 513 | octeon_wdt_ping(wdog); /* Get the irqs back on. */ |
| 513 | return 0; | 514 | return 0; |
| 514 | } | 515 | } |
| 515 | 516 | ||
| 516 | /** | 517 | static int octeon_wdt_start(struct watchdog_device *wdog) |
| 517 | * octeon_wdt_write: | ||
| 518 | * @file: file handle to the watchdog | ||
| 519 | * @buf: buffer to write (unused as data does not matter here | ||
| 520 | * @count: count of bytes | ||
| 521 | * @ppos: pointer to the position to write. No seeks allowed | ||
| 522 | * | ||
| 523 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
| 524 | * write of data will do, as we we don't define content meaning. | ||
| 525 | */ | ||
| 526 | |||
| 527 | static ssize_t octeon_wdt_write(struct file *file, const char __user *buf, | ||
| 528 | size_t count, loff_t *ppos) | ||
| 529 | { | ||
| 530 | if (count) { | ||
| 531 | if (!nowayout) { | ||
| 532 | size_t i; | ||
| 533 | |||
| 534 | /* In case it was set long ago */ | ||
| 535 | expect_close = 0; | ||
| 536 | |||
| 537 | for (i = 0; i != count; i++) { | ||
| 538 | char c; | ||
| 539 | if (get_user(c, buf + i)) | ||
| 540 | return -EFAULT; | ||
| 541 | if (c == 'V') | ||
| 542 | expect_close = 1; | ||
| 543 | } | ||
| 544 | } | ||
| 545 | octeon_wdt_ping(); | ||
| 546 | } | ||
| 547 | return count; | ||
| 548 | } | ||
| 549 | |||
| 550 | /** | ||
| 551 | * octeon_wdt_ioctl: | ||
| 552 | * @file: file handle to the device | ||
| 553 | * @cmd: watchdog command | ||
| 554 | * @arg: argument pointer | ||
| 555 | * | ||
| 556 | * The watchdog API defines a common set of functions for all | ||
| 557 | * watchdogs according to their available features. We only | ||
| 558 | * actually usefully support querying capabilities and setting | ||
| 559 | * the timeout. | ||
| 560 | */ | ||
| 561 | |||
| 562 | static long octeon_wdt_ioctl(struct file *file, unsigned int cmd, | ||
| 563 | unsigned long arg) | ||
| 564 | { | ||
| 565 | void __user *argp = (void __user *)arg; | ||
| 566 | int __user *p = argp; | ||
| 567 | int new_heartbeat; | ||
| 568 | |||
| 569 | static struct watchdog_info ident = { | ||
| 570 | .options = WDIOF_SETTIMEOUT| | ||
| 571 | WDIOF_MAGICCLOSE| | ||
| 572 | WDIOF_KEEPALIVEPING, | ||
| 573 | .firmware_version = 1, | ||
| 574 | .identity = "OCTEON", | ||
| 575 | }; | ||
| 576 | |||
| 577 | switch (cmd) { | ||
| 578 | case WDIOC_GETSUPPORT: | ||
| 579 | return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
| 580 | case WDIOC_GETSTATUS: | ||
| 581 | case WDIOC_GETBOOTSTATUS: | ||
| 582 | return put_user(0, p); | ||
| 583 | case WDIOC_KEEPALIVE: | ||
| 584 | octeon_wdt_ping(); | ||
| 585 | return 0; | ||
| 586 | case WDIOC_SETTIMEOUT: | ||
| 587 | if (get_user(new_heartbeat, p)) | ||
| 588 | return -EFAULT; | ||
| 589 | if (octeon_wdt_set_heartbeat(new_heartbeat)) | ||
| 590 | return -EINVAL; | ||
| 591 | /* Fall through. */ | ||
| 592 | case WDIOC_GETTIMEOUT: | ||
| 593 | return put_user(heartbeat, p); | ||
| 594 | default: | ||
| 595 | return -ENOTTY; | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 599 | /** | ||
| 600 | * octeon_wdt_open: | ||
| 601 | * @inode: inode of device | ||
| 602 | * @file: file handle to device | ||
| 603 | * | ||
| 604 | * The watchdog device has been opened. The watchdog device is single | ||
| 605 | * open and on opening we do a ping to reset the counters. | ||
| 606 | */ | ||
| 607 | |||
| 608 | static int octeon_wdt_open(struct inode *inode, struct file *file) | ||
| 609 | { | 518 | { |
| 610 | if (test_and_set_bit(0, &octeon_wdt_is_open)) | 519 | octeon_wdt_ping(wdog); |
| 611 | return -EBUSY; | ||
| 612 | /* | ||
| 613 | * Activate | ||
| 614 | */ | ||
| 615 | octeon_wdt_ping(); | ||
| 616 | do_coundown = 1; | 520 | do_coundown = 1; |
| 617 | return nonseekable_open(inode, file); | 521 | return 0; |
| 618 | } | 522 | } |
| 619 | 523 | ||
| 620 | /** | 524 | static int octeon_wdt_stop(struct watchdog_device *wdog) |
| 621 | * octeon_wdt_release: | ||
| 622 | * @inode: inode to board | ||
| 623 | * @file: file handle to board | ||
| 624 | * | ||
| 625 | * The watchdog has a configurable API. There is a religious dispute | ||
| 626 | * between people who want their watchdog to be able to shut down and | ||
| 627 | * those who want to be sure if the watchdog manager dies the machine | ||
| 628 | * reboots. In the former case we disable the counters, in the latter | ||
| 629 | * case you have to open it again very soon. | ||
| 630 | */ | ||
| 631 | |||
| 632 | static int octeon_wdt_release(struct inode *inode, struct file *file) | ||
| 633 | { | 525 | { |
| 634 | if (expect_close) { | 526 | do_coundown = 0; |
| 635 | do_coundown = 0; | 527 | octeon_wdt_ping(wdog); |
| 636 | octeon_wdt_ping(); | ||
| 637 | } else { | ||
| 638 | pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); | ||
| 639 | } | ||
| 640 | clear_bit(0, &octeon_wdt_is_open); | ||
| 641 | expect_close = 0; | ||
| 642 | return 0; | 528 | return 0; |
| 643 | } | 529 | } |
| 644 | 530 | ||
| 645 | static const struct file_operations octeon_wdt_fops = { | 531 | static struct notifier_block octeon_wdt_cpu_notifier = { |
| 646 | .owner = THIS_MODULE, | 532 | .notifier_call = octeon_wdt_cpu_callback, |
| 647 | .llseek = no_llseek, | ||
| 648 | .write = octeon_wdt_write, | ||
| 649 | .unlocked_ioctl = octeon_wdt_ioctl, | ||
| 650 | .open = octeon_wdt_open, | ||
| 651 | .release = octeon_wdt_release, | ||
| 652 | }; | 533 | }; |
| 653 | 534 | ||
| 654 | static struct miscdevice octeon_wdt_miscdev = { | 535 | static const struct watchdog_info octeon_wdt_info = { |
| 655 | .minor = WATCHDOG_MINOR, | 536 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, |
| 656 | .name = "watchdog", | 537 | .identity = "OCTEON", |
| 657 | .fops = &octeon_wdt_fops, | ||
| 658 | }; | 538 | }; |
| 659 | 539 | ||
| 660 | static struct notifier_block octeon_wdt_cpu_notifier = { | 540 | static const struct watchdog_ops octeon_wdt_ops = { |
| 661 | .notifier_call = octeon_wdt_cpu_callback, | 541 | .owner = THIS_MODULE, |
| 542 | .start = octeon_wdt_start, | ||
| 543 | .stop = octeon_wdt_stop, | ||
| 544 | .ping = octeon_wdt_ping, | ||
| 545 | .set_timeout = octeon_wdt_set_timeout, | ||
| 662 | }; | 546 | }; |
| 663 | 547 | ||
| 548 | static struct watchdog_device octeon_wdt = { | ||
| 549 | .info = &octeon_wdt_info, | ||
| 550 | .ops = &octeon_wdt_ops, | ||
| 551 | }; | ||
| 664 | 552 | ||
| 665 | /** | 553 | /** |
| 666 | * Module/ driver initialization. | 554 | * Module/ driver initialization. |
| @@ -694,11 +582,15 @@ static int __init octeon_wdt_init(void) | |||
| 694 | 582 | ||
| 695 | pr_info("Initial granularity %d Sec\n", timeout_sec); | 583 | pr_info("Initial granularity %d Sec\n", timeout_sec); |
| 696 | 584 | ||
| 697 | ret = misc_register(&octeon_wdt_miscdev); | 585 | octeon_wdt.timeout = timeout_sec; |
| 586 | octeon_wdt.max_timeout = UINT_MAX; | ||
| 587 | |||
| 588 | watchdog_set_nowayout(&octeon_wdt, nowayout); | ||
| 589 | |||
| 590 | ret = watchdog_register_device(&octeon_wdt); | ||
| 698 | if (ret) { | 591 | if (ret) { |
| 699 | pr_err("cannot register miscdev on minor=%d (err=%d)\n", | 592 | pr_err("watchdog_register_device() failed: %d\n", ret); |
| 700 | WATCHDOG_MINOR, ret); | 593 | return ret; |
| 701 | goto out; | ||
| 702 | } | 594 | } |
| 703 | 595 | ||
| 704 | /* Build the NMI handler ... */ | 596 | /* Build the NMI handler ... */ |
| @@ -721,8 +613,7 @@ static int __init octeon_wdt_init(void) | |||
| 721 | __register_hotcpu_notifier(&octeon_wdt_cpu_notifier); | 613 | __register_hotcpu_notifier(&octeon_wdt_cpu_notifier); |
| 722 | cpu_notifier_register_done(); | 614 | cpu_notifier_register_done(); |
| 723 | 615 | ||
| 724 | out: | 616 | return 0; |
| 725 | return ret; | ||
| 726 | } | 617 | } |
| 727 | 618 | ||
| 728 | /** | 619 | /** |
| @@ -732,7 +623,7 @@ static void __exit octeon_wdt_cleanup(void) | |||
| 732 | { | 623 | { |
| 733 | int cpu; | 624 | int cpu; |
| 734 | 625 | ||
| 735 | misc_deregister(&octeon_wdt_miscdev); | 626 | watchdog_unregister_device(&octeon_wdt); |
| 736 | 627 | ||
| 737 | cpu_notifier_register_begin(); | 628 | cpu_notifier_register_begin(); |
| 738 | __unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier); | 629 | __unregister_hotcpu_notifier(&octeon_wdt_cpu_notifier); |
