diff options
| -rw-r--r-- | Documentation/watchdog/hpwdt.txt | 19 | ||||
| -rw-r--r-- | drivers/mfd/twl4030-core.c | 12 | ||||
| -rw-r--r-- | drivers/watchdog/Kconfig | 59 | ||||
| -rw-r--r-- | drivers/watchdog/Makefile | 5 | ||||
| -rw-r--r-- | drivers/watchdog/bcm47xx_wdt.c | 286 | ||||
| -rw-r--r-- | drivers/watchdog/coh901327_wdt.c | 537 | ||||
| -rw-r--r-- | drivers/watchdog/hpwdt.c | 26 | ||||
| -rw-r--r-- | drivers/watchdog/omap_wdt.c | 7 | ||||
| -rw-r--r-- | drivers/watchdog/pnx833x_wdt.c | 282 | ||||
| -rw-r--r-- | drivers/watchdog/stmp3xxx_wdt.c | 296 | ||||
| -rw-r--r-- | drivers/watchdog/twl4030_wdt.c | 272 | ||||
| -rw-r--r-- | drivers/watchdog/wdt_pci.c | 122 |
12 files changed, 1840 insertions, 83 deletions
diff --git a/Documentation/watchdog/hpwdt.txt b/Documentation/watchdog/hpwdt.txt index 127839e53043..9c24d5ffbb06 100644 --- a/Documentation/watchdog/hpwdt.txt +++ b/Documentation/watchdog/hpwdt.txt | |||
| @@ -19,30 +19,41 @@ Last reviewed: 06/02/2009 | |||
| 19 | not be updated in a timely fashion and a hardware system reset (also known as | 19 | not be updated in a timely fashion and a hardware system reset (also known as |
| 20 | an Automatic Server Recovery (ASR)) event will occur. | 20 | an Automatic Server Recovery (ASR)) event will occur. |
| 21 | 21 | ||
| 22 | The hpwdt driver also has three (3) module parameters. They are the following: | 22 | The hpwdt driver also has four (4) module parameters. They are the following: |
| 23 | 23 | ||
| 24 | soft_margin - allows the user to set the watchdog timer value | 24 | soft_margin - allows the user to set the watchdog timer value |
| 25 | allow_kdump - allows the user to save off a kernel dump image after an NMI | 25 | allow_kdump - allows the user to save off a kernel dump image after an NMI |
| 26 | nowayout - basic watchdog parameter that does not allow the timer to | 26 | nowayout - basic watchdog parameter that does not allow the timer to |
| 27 | be restarted or an impending ASR to be escaped. | 27 | be restarted or an impending ASR to be escaped. |
| 28 | priority - determines whether or not the hpwdt driver is first on the | ||
| 29 | die_notify list to handle NMIs or last. The default value | ||
| 30 | for this module parameter is 0 or LAST. If the user wants to | ||
| 31 | enable NMI sourcing then reload the hpwdt driver with | ||
| 32 | priority=1 (and boot with nmi_watchdog=0). | ||
| 28 | 33 | ||
| 29 | NOTE: More information about watchdog drivers in general, including the ioctl | 34 | NOTE: More information about watchdog drivers in general, including the ioctl |
| 30 | interface to /dev/watchdog can be found in | 35 | interface to /dev/watchdog can be found in |
| 31 | Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt. | 36 | Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt. |
| 32 | 37 | ||
| 33 | The NMI sourcing capability is disabled when the driver discovers that the | 38 | The priority parameter was introduced due to other kernel software that relied |
| 34 | nmi_watchdog is turned on (nmi_watchdog = 1). This is due to the inability to | 39 | on handling NMIs (like oprofile). Keeping hpwdt's priority at 0 (or LAST) |
| 40 | enables the users of NMIs for non critical events to be work as expected. | ||
| 41 | |||
| 42 | The NMI sourcing capability is disabled by default due to the inability to | ||
| 35 | distinguish between "NMI Watchdog Ticks" and "HW generated NMI events" in the | 43 | distinguish between "NMI Watchdog Ticks" and "HW generated NMI events" in the |
| 36 | Linux kernel. What this means is that the hpwdt nmi handler code is called | 44 | Linux kernel. What this means is that the hpwdt nmi handler code is called |
| 37 | each time the NMI signal fires off. This could amount to several thousands of | 45 | each time the NMI signal fires off. This could amount to several thousands of |
| 38 | NMIs in a matter of seconds. If a user sees the Linux kernel's "dazed and | 46 | NMIs in a matter of seconds. If a user sees the Linux kernel's "dazed and |
| 39 | confused" message in the logs or if the system gets into a hung state, then | 47 | confused" message in the logs or if the system gets into a hung state, then |
| 40 | the user should reboot with nmi_watchdog=0. | 48 | the hpwdt driver can be reloaded with the "priority" module parameter set |
| 49 | (priority=1). | ||
| 41 | 50 | ||
| 42 | 1. If the kernel has not been booted with nmi_watchdog turned off then | 51 | 1. If the kernel has not been booted with nmi_watchdog turned off then |
| 43 | edit /boot/grub/menu.lst and place the nmi_watchdog=0 at the end of the | 52 | edit /boot/grub/menu.lst and place the nmi_watchdog=0 at the end of the |
| 44 | currently booting kernel line. | 53 | currently booting kernel line. |
| 45 | 2. reboot the sever | 54 | 2. reboot the sever |
| 55 | 3. Once the system comes up perform a rmmod hpwdt | ||
| 56 | 4. insmod /lib/modules/`uname -r`/kernel/drivers/char/watchdog/hpwdt.ko priority=1 | ||
| 46 | 57 | ||
| 47 | Now, the hpwdt can successfully receive and source the NMI and provide a log | 58 | Now, the hpwdt can successfully receive and source the NMI and provide a log |
| 48 | message that details the reason for the NMI (as determined by the HP BIOS). | 59 | message that details the reason for the NMI (as determined by the HP BIOS). |
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index cd1008c19cd7..ca54996ffd0e 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c | |||
| @@ -101,6 +101,12 @@ | |||
| 101 | #define twl_has_usb() false | 101 | #define twl_has_usb() false |
| 102 | #endif | 102 | #endif |
| 103 | 103 | ||
| 104 | #if defined(CONFIG_TWL4030_WATCHDOG) || \ | ||
| 105 | defined(CONFIG_TWL4030_WATCHDOG_MODULE) | ||
| 106 | #define twl_has_watchdog() true | ||
| 107 | #else | ||
| 108 | #define twl_has_watchdog() false | ||
| 109 | #endif | ||
| 104 | 110 | ||
| 105 | /* Triton Core internal information (BEGIN) */ | 111 | /* Triton Core internal information (BEGIN) */ |
| 106 | 112 | ||
| @@ -526,6 +532,12 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) | |||
| 526 | usb_transceiver = child; | 532 | usb_transceiver = child; |
| 527 | } | 533 | } |
| 528 | 534 | ||
| 535 | if (twl_has_watchdog()) { | ||
| 536 | child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); | ||
| 537 | if (IS_ERR(child)) | ||
| 538 | return PTR_ERR(child); | ||
| 539 | } | ||
| 540 | |||
| 529 | if (twl_has_regulator()) { | 541 | if (twl_has_regulator()) { |
| 530 | /* | 542 | /* |
| 531 | child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); | 543 | child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b166f2852a64..b1ccc04f3c9a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -240,6 +240,32 @@ config ORION_WATCHDOG | |||
| 240 | To compile this driver as a module, choose M here: the | 240 | To compile this driver as a module, choose M here: the |
| 241 | module will be called orion_wdt. | 241 | module will be called orion_wdt. |
| 242 | 242 | ||
| 243 | config COH901327_WATCHDOG | ||
| 244 | bool "ST-Ericsson COH 901 327 watchdog" | ||
| 245 | depends on ARCH_U300 | ||
| 246 | default y if MACH_U300 | ||
| 247 | help | ||
| 248 | Say Y here to include Watchdog timer support for the | ||
| 249 | watchdog embedded into the ST-Ericsson U300 series platforms. | ||
| 250 | This watchdog is used to reset the system and thus cannot be | ||
| 251 | compiled as a module. | ||
| 252 | |||
| 253 | config TWL4030_WATCHDOG | ||
| 254 | tristate "TWL4030 Watchdog" | ||
| 255 | depends on TWL4030_CORE | ||
| 256 | help | ||
| 257 | Support for TI TWL4030 watchdog. Say 'Y' here to enable the | ||
| 258 | watchdog timer support for TWL4030 chips. | ||
| 259 | |||
| 260 | config STMP3XXX_WATCHDOG | ||
| 261 | tristate "Freescale STMP3XXX watchdog" | ||
| 262 | depends on ARCH_STMP3XXX | ||
| 263 | help | ||
| 264 | Say Y here if to include support for the watchdog timer | ||
| 265 | for the Sigmatel STMP37XX/378X SoC. | ||
| 266 | To compile this driver as a module, choose M here: the | ||
| 267 | module will be called stmp3xxx_wdt. | ||
| 268 | |||
| 243 | # AVR32 Architecture | 269 | # AVR32 Architecture |
| 244 | 270 | ||
| 245 | config AT32AP700X_WDT | 271 | config AT32AP700X_WDT |
| @@ -703,6 +729,12 @@ config SBC_EPX_C3_WATCHDOG | |||
| 703 | 729 | ||
| 704 | # MIPS Architecture | 730 | # MIPS Architecture |
| 705 | 731 | ||
| 732 | config BCM47XX_WDT | ||
| 733 | tristate "Broadcom BCM47xx Watchdog Timer" | ||
| 734 | depends on BCM47XX | ||
| 735 | help | ||
| 736 | Hardware driver for the Broadcom BCM47xx Watchog Timer. | ||
| 737 | |||
| 706 | config RC32434_WDT | 738 | config RC32434_WDT |
| 707 | tristate "IDT RC32434 SoC Watchdog Timer" | 739 | tristate "IDT RC32434 SoC Watchdog Timer" |
| 708 | depends on MIKROTIK_RB532 | 740 | depends on MIKROTIK_RB532 |
| @@ -729,6 +761,15 @@ config WDT_MTX1 | |||
| 729 | Hardware driver for the MTX-1 boards. This is a watchdog timer that | 761 | Hardware driver for the MTX-1 boards. This is a watchdog timer that |
| 730 | will reboot the machine after a 100 seconds timer expired. | 762 | will reboot the machine after a 100 seconds timer expired. |
| 731 | 763 | ||
| 764 | config PNX833X_WDT | ||
| 765 | tristate "PNX833x Hardware Watchdog" | ||
| 766 | depends on SOC_PNX8335 | ||
| 767 | help | ||
| 768 | Hardware driver for the PNX833x's watchdog. This is a | ||
| 769 | watchdog timer that will reboot the machine after a programable | ||
| 770 | timer has expired and no process has written to /dev/watchdog during | ||
| 771 | that time. | ||
| 772 | |||
| 732 | config WDT_RM9K_GPI | 773 | config WDT_RM9K_GPI |
| 733 | tristate "RM9000/GPI hardware watchdog" | 774 | tristate "RM9000/GPI hardware watchdog" |
| 734 | depends on CPU_RM9000 | 775 | depends on CPU_RM9000 |
| @@ -966,24 +1007,16 @@ config WDTPCI | |||
| 966 | ---help--- | 1007 | ---help--- |
| 967 | If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. | 1008 | If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. |
| 968 | 1009 | ||
| 969 | To compile this driver as a module, choose M here: the | 1010 | If you have a PCI-WDT501 watchdog board then you can enable the |
| 970 | module will be called wdt_pci. | 1011 | temperature sensor by setting the type parameter to 501. |
| 971 | |||
| 972 | config WDT_501_PCI | ||
| 973 | bool "PCI-WDT501 features" | ||
| 974 | depends on WDTPCI | ||
| 975 | help | ||
| 976 | Saying Y here and creating a character special file /dev/temperature | ||
| 977 | with major number 10 and minor number 131 ("man mknod") will give | ||
| 978 | you a thermometer inside your computer: reading from | ||
| 979 | /dev/temperature yields one byte, the temperature in degrees | ||
| 980 | Fahrenheit. This works only if you have a PCI-WDT501 watchdog board | ||
| 981 | installed. | ||
| 982 | 1012 | ||
| 983 | If you want to enable the Fan Tachometer on the PCI-WDT501, then you | 1013 | If you want to enable the Fan Tachometer on the PCI-WDT501, then you |
| 984 | can do this via the tachometer parameter. Only do this if you have a | 1014 | can do this via the tachometer parameter. Only do this if you have a |
| 985 | fan tachometer actually set up. | 1015 | fan tachometer actually set up. |
| 986 | 1016 | ||
| 1017 | To compile this driver as a module, choose M here: the | ||
| 1018 | module will be called wdt_pci. | ||
| 1019 | |||
| 987 | # | 1020 | # |
| 988 | # USB-based Watchdog Cards | 1021 | # USB-based Watchdog Cards |
| 989 | # | 1022 | # |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c3afa14d5be1..3d774294a2b7 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
| @@ -28,6 +28,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
| 28 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 28 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
| 29 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 29 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
| 30 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | 30 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o |
| 31 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o | ||
| 31 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o | 32 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o |
| 32 | obj-$(CONFIG_977_WATCHDOG) += wdt977.o | 33 | obj-$(CONFIG_977_WATCHDOG) += wdt977.o |
| 33 | obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o | 34 | obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o |
| @@ -41,6 +42,8 @@ obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o | |||
| 41 | obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o | 42 | obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o |
| 42 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o | 43 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o |
| 43 | obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o | 44 | obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o |
| 45 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o | ||
| 46 | obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o | ||
| 44 | 47 | ||
| 45 | # AVR32 Architecture | 48 | # AVR32 Architecture |
| 46 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 49 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
| @@ -98,9 +101,11 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o | |||
| 98 | # M68KNOMMU Architecture | 101 | # M68KNOMMU Architecture |
| 99 | 102 | ||
| 100 | # MIPS Architecture | 103 | # MIPS Architecture |
| 104 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o | ||
| 101 | obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o | 105 | obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o |
| 102 | obj-$(CONFIG_INDYDOG) += indydog.o | 106 | obj-$(CONFIG_INDYDOG) += indydog.o |
| 103 | obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o | 107 | obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o |
| 108 | obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o | ||
| 104 | obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o | 109 | obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o |
| 105 | obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o | 110 | obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o |
| 106 | obj-$(CONFIG_AR7_WDT) += ar7_wdt.o | 111 | obj-$(CONFIG_AR7_WDT) += ar7_wdt.o |
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c new file mode 100644 index 000000000000..5c7011cda6a6 --- /dev/null +++ b/drivers/watchdog/bcm47xx_wdt.c | |||
| @@ -0,0 +1,286 @@ | |||
| 1 | /* | ||
| 2 | * Watchdog driver for Broadcom BCM47XX | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs> | ||
| 5 | * Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version | ||
| 10 | * 2 of the License, or (at your option) any later version. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/bitops.h> | ||
| 14 | #include <linux/errno.h> | ||
| 15 | #include <linux/fs.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/miscdevice.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/moduleparam.h> | ||
| 21 | #include <linux/reboot.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/uaccess.h> | ||
| 24 | #include <linux/watchdog.h> | ||
| 25 | #include <linux/timer.h> | ||
| 26 | #include <linux/jiffies.h> | ||
| 27 | #include <linux/ssb/ssb_embedded.h> | ||
| 28 | #include <asm/mach-bcm47xx/bcm47xx.h> | ||
| 29 | |||
| 30 | #define DRV_NAME "bcm47xx_wdt" | ||
| 31 | |||
| 32 | #define WDT_DEFAULT_TIME 30 /* seconds */ | ||
| 33 | #define WDT_MAX_TIME 255 /* seconds */ | ||
| 34 | |||
| 35 | static int wdt_time = WDT_DEFAULT_TIME; | ||
| 36 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 37 | |||
| 38 | module_param(wdt_time, int, 0); | ||
| 39 | MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" | ||
| 40 | __MODULE_STRING(WDT_DEFAULT_TIME) ")"); | ||
| 41 | |||
| 42 | #ifdef CONFIG_WATCHDOG_NOWAYOUT | ||
| 43 | module_param(nowayout, int, 0); | ||
| 44 | MODULE_PARM_DESC(nowayout, | ||
| 45 | "Watchdog cannot be stopped once started (default=" | ||
| 46 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
| 47 | #endif | ||
| 48 | |||
| 49 | static unsigned long bcm47xx_wdt_busy; | ||
| 50 | static char expect_release; | ||
| 51 | static struct timer_list wdt_timer; | ||
| 52 | static atomic_t ticks; | ||
| 53 | |||
| 54 | static inline void bcm47xx_wdt_hw_start(void) | ||
| 55 | { | ||
| 56 | /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ | ||
| 57 | ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff); | ||
| 58 | } | ||
| 59 | |||
| 60 | static inline int bcm47xx_wdt_hw_stop(void) | ||
| 61 | { | ||
| 62 | return ssb_watchdog_timer_set(&ssb_bcm47xx, 0); | ||
| 63 | } | ||
| 64 | |||
| 65 | static void bcm47xx_timer_tick(unsigned long unused) | ||
| 66 | { | ||
| 67 | if (!atomic_dec_and_test(&ticks)) { | ||
| 68 | bcm47xx_wdt_hw_start(); | ||
| 69 | mod_timer(&wdt_timer, jiffies + HZ); | ||
| 70 | } else { | ||
| 71 | printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n"); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | static inline void bcm47xx_wdt_pet(void) | ||
| 76 | { | ||
| 77 | atomic_set(&ticks, wdt_time); | ||
| 78 | } | ||
| 79 | |||
| 80 | static void bcm47xx_wdt_start(void) | ||
| 81 | { | ||
| 82 | bcm47xx_wdt_pet(); | ||
| 83 | bcm47xx_timer_tick(0); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void bcm47xx_wdt_pause(void) | ||
| 87 | { | ||
| 88 | del_timer_sync(&wdt_timer); | ||
| 89 | bcm47xx_wdt_hw_stop(); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void bcm47xx_wdt_stop(void) | ||
| 93 | { | ||
| 94 | bcm47xx_wdt_pause(); | ||
| 95 | } | ||
| 96 | |||
| 97 | static int bcm47xx_wdt_settimeout(int new_time) | ||
| 98 | { | ||
| 99 | if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) | ||
| 100 | return -EINVAL; | ||
| 101 | |||
| 102 | wdt_time = new_time; | ||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | static int bcm47xx_wdt_open(struct inode *inode, struct file *file) | ||
| 107 | { | ||
| 108 | if (test_and_set_bit(0, &bcm47xx_wdt_busy)) | ||
| 109 | return -EBUSY; | ||
| 110 | |||
| 111 | bcm47xx_wdt_start(); | ||
| 112 | return nonseekable_open(inode, file); | ||
| 113 | } | ||
| 114 | |||
| 115 | static int bcm47xx_wdt_release(struct inode *inode, struct file *file) | ||
| 116 | { | ||
| 117 | if (expect_release == 42) { | ||
| 118 | bcm47xx_wdt_stop(); | ||
| 119 | } else { | ||
| 120 | printk(KERN_CRIT DRV_NAME | ||
| 121 | ": Unexpected close, not stopping watchdog!\n"); | ||
| 122 | bcm47xx_wdt_start(); | ||
| 123 | } | ||
| 124 | |||
| 125 | clear_bit(0, &bcm47xx_wdt_busy); | ||
| 126 | expect_release = 0; | ||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data, | ||
| 131 | size_t len, loff_t *ppos) | ||
| 132 | { | ||
| 133 | if (len) { | ||
| 134 | if (!nowayout) { | ||
| 135 | size_t i; | ||
| 136 | |||
| 137 | expect_release = 0; | ||
| 138 | |||
| 139 | for (i = 0; i != len; i++) { | ||
| 140 | char c; | ||
| 141 | if (get_user(c, data + i)) | ||
| 142 | return -EFAULT; | ||
| 143 | if (c == 'V') | ||
| 144 | expect_release = 42; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | bcm47xx_wdt_pet(); | ||
| 148 | } | ||
| 149 | return len; | ||
| 150 | } | ||
| 151 | |||
| 152 | static struct watchdog_info bcm47xx_wdt_info = { | ||
| 153 | .identity = DRV_NAME, | ||
| 154 | .options = WDIOF_SETTIMEOUT | | ||
| 155 | WDIOF_KEEPALIVEPING | | ||
| 156 | WDIOF_MAGICCLOSE, | ||
| 157 | }; | ||
| 158 | |||
| 159 | static long bcm47xx_wdt_ioctl(struct file *file, | ||
| 160 | unsigned int cmd, unsigned long arg) | ||
| 161 | { | ||
| 162 | void __user *argp = (void __user *)arg; | ||
| 163 | int __user *p = argp; | ||
| 164 | int new_value, retval = -EINVAL;; | ||
| 165 | |||
| 166 | switch (cmd) { | ||
| 167 | case WDIOC_GETSUPPORT: | ||
| 168 | return copy_to_user(argp, &bcm47xx_wdt_info, | ||
| 169 | sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0; | ||
| 170 | |||
| 171 | case WDIOC_GETSTATUS: | ||
| 172 | case WDIOC_GETBOOTSTATUS: | ||
| 173 | return put_user(0, p); | ||
| 174 | |||
| 175 | case WDIOC_SETOPTIONS: | ||
| 176 | if (get_user(new_value, p)) | ||
| 177 | return -EFAULT; | ||
| 178 | |||
| 179 | if (new_value & WDIOS_DISABLECARD) { | ||
| 180 | bcm47xx_wdt_stop(); | ||
| 181 | retval = 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | if (new_value & WDIOS_ENABLECARD) { | ||
| 185 | bcm47xx_wdt_start(); | ||
| 186 | retval = 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | return retval; | ||
| 190 | |||
| 191 | case WDIOC_KEEPALIVE: | ||
| 192 | bcm47xx_wdt_pet(); | ||
| 193 | return 0; | ||
| 194 | |||
| 195 | case WDIOC_SETTIMEOUT: | ||
| 196 | if (get_user(new_value, p)) | ||
| 197 | return -EFAULT; | ||
| 198 | |||
| 199 | if (bcm47xx_wdt_settimeout(new_value)) | ||
| 200 | return -EINVAL; | ||
| 201 | |||
| 202 | bcm47xx_wdt_pet(); | ||
| 203 | |||
| 204 | case WDIOC_GETTIMEOUT: | ||
| 205 | return put_user(wdt_time, p); | ||
| 206 | |||
| 207 | default: | ||
| 208 | return -ENOTTY; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | static int bcm47xx_wdt_notify_sys(struct notifier_block *this, | ||
| 213 | unsigned long code, void *unused) | ||
| 214 | { | ||
| 215 | if (code == SYS_DOWN || code == SYS_HALT) | ||
| 216 | bcm47xx_wdt_stop(); | ||
| 217 | return NOTIFY_DONE; | ||
| 218 | } | ||
| 219 | |||
| 220 | static const struct file_operations bcm47xx_wdt_fops = { | ||
| 221 | .owner = THIS_MODULE, | ||
| 222 | .llseek = no_llseek, | ||
| 223 | .unlocked_ioctl = bcm47xx_wdt_ioctl, | ||
| 224 | .open = bcm47xx_wdt_open, | ||
| 225 | .release = bcm47xx_wdt_release, | ||
| 226 | .write = bcm47xx_wdt_write, | ||
| 227 | }; | ||
| 228 | |||
| 229 | static struct miscdevice bcm47xx_wdt_miscdev = { | ||
| 230 | .minor = WATCHDOG_MINOR, | ||
| 231 | .name = "watchdog", | ||
| 232 | .fops = &bcm47xx_wdt_fops, | ||
| 233 | }; | ||
| 234 | |||
| 235 | static struct notifier_block bcm47xx_wdt_notifier = { | ||
| 236 | .notifier_call = bcm47xx_wdt_notify_sys, | ||
| 237 | }; | ||
| 238 | |||
| 239 | static int __init bcm47xx_wdt_init(void) | ||
| 240 | { | ||
| 241 | int ret; | ||
| 242 | |||
| 243 | if (bcm47xx_wdt_hw_stop() < 0) | ||
| 244 | return -ENODEV; | ||
| 245 | |||
| 246 | setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); | ||
| 247 | |||
| 248 | if (bcm47xx_wdt_settimeout(wdt_time)) { | ||
| 249 | bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME); | ||
| 250 | printk(KERN_INFO DRV_NAME ": " | ||
| 251 | "wdt_time value must be 0 < wdt_time < %d, using %d\n", | ||
| 252 | (WDT_MAX_TIME + 1), wdt_time); | ||
| 253 | } | ||
| 254 | |||
| 255 | ret = register_reboot_notifier(&bcm47xx_wdt_notifier); | ||
| 256 | if (ret) | ||
| 257 | return ret; | ||
| 258 | |||
| 259 | ret = misc_register(&bcm47xx_wdt_miscdev); | ||
| 260 | if (ret) { | ||
| 261 | unregister_reboot_notifier(&bcm47xx_wdt_notifier); | ||
| 262 | return ret; | ||
| 263 | } | ||
| 264 | |||
| 265 | printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n", | ||
| 266 | wdt_time, nowayout ? ", nowayout" : ""); | ||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | static void __exit bcm47xx_wdt_exit(void) | ||
| 271 | { | ||
| 272 | if (!nowayout) | ||
| 273 | bcm47xx_wdt_stop(); | ||
| 274 | |||
| 275 | misc_deregister(&bcm47xx_wdt_miscdev); | ||
| 276 | |||
| 277 | unregister_reboot_notifier(&bcm47xx_wdt_notifier); | ||
| 278 | } | ||
| 279 | |||
| 280 | module_init(bcm47xx_wdt_init); | ||
| 281 | module_exit(bcm47xx_wdt_exit); | ||
| 282 | |||
| 283 | MODULE_AUTHOR("Aleksandar Radovanovic"); | ||
| 284 | MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx"); | ||
| 285 | MODULE_LICENSE("GPL"); | ||
| 286 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c new file mode 100644 index 000000000000..fecb307d28e9 --- /dev/null +++ b/drivers/watchdog/coh901327_wdt.c | |||
| @@ -0,0 +1,537 @@ | |||
| 1 | /* | ||
| 2 | * coh901327_wdt.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-2009 ST-Ericsson AB | ||
| 5 | * License terms: GNU General Public License (GPL) version 2 | ||
| 6 | * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core | ||
| 7 | * Author: Linus Walleij <linus.walleij@stericsson.com> | ||
| 8 | */ | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/fs.h> | ||
| 12 | #include <linux/miscdevice.h> | ||
| 13 | #include <linux/watchdog.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/pm.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/io.h> | ||
| 18 | #include <linux/bitops.h> | ||
| 19 | #include <linux/uaccess.h> | ||
| 20 | #include <linux/clk.h> | ||
| 21 | |||
| 22 | #define DRV_NAME "WDOG COH 901 327" | ||
| 23 | |||
| 24 | /* | ||
| 25 | * COH 901 327 register definitions | ||
| 26 | */ | ||
| 27 | |||
| 28 | /* WDOG_FEED Register 32bit (-/W) */ | ||
| 29 | #define U300_WDOG_FR 0x00 | ||
| 30 | #define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU | ||
| 31 | /* WDOG_TIMEOUT Register 32bit (R/W) */ | ||
| 32 | #define U300_WDOG_TR 0x04 | ||
| 33 | #define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU | ||
| 34 | /* WDOG_DISABLE1 Register 32bit (-/W) */ | ||
| 35 | #define U300_WDOG_D1R 0x08 | ||
| 36 | #define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU | ||
| 37 | /* WDOG_DISABLE2 Register 32bit (R/W) */ | ||
| 38 | #define U300_WDOG_D2R 0x0C | ||
| 39 | #define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU | ||
| 40 | #define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU | ||
| 41 | #define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U | ||
| 42 | /* WDOG_STATUS Register 32bit (R/W) */ | ||
| 43 | #define U300_WDOG_SR 0x10 | ||
| 44 | #define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U | ||
| 45 | #define U300_WDOG_SR_STATUS_NORMAL 0x0000U | ||
| 46 | #define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U | ||
| 47 | /* WDOG_COUNT Register 32bit (R/-) */ | ||
| 48 | #define U300_WDOG_CR 0x14 | ||
| 49 | #define U300_WDOG_CR_VALID_IND 0x8000U | ||
| 50 | #define U300_WDOG_CR_VALID_STABLE 0x0000U | ||
| 51 | #define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU | ||
| 52 | /* WDOG_JTAGOVR Register 32bit (R/W) */ | ||
| 53 | #define U300_WDOG_JOR 0x18 | ||
| 54 | #define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U | ||
| 55 | #define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U | ||
| 56 | /* WDOG_RESTART Register 32bit (-/W) */ | ||
| 57 | #define U300_WDOG_RR 0x1C | ||
| 58 | #define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU | ||
| 59 | /* WDOG_IRQ_EVENT Register 32bit (R/W) */ | ||
| 60 | #define U300_WDOG_IER 0x20 | ||
| 61 | #define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U | ||
| 62 | #define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U | ||
| 63 | /* WDOG_IRQ_MASK Register 32bit (R/W) */ | ||
| 64 | #define U300_WDOG_IMR 0x24 | ||
| 65 | #define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U | ||
| 66 | /* WDOG_IRQ_FORCE Register 32bit (R/W) */ | ||
| 67 | #define U300_WDOG_IFR 0x28 | ||
| 68 | #define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U | ||
| 69 | |||
| 70 | /* Default timeout in seconds = 1 minute */ | ||
| 71 | static int margin = 60; | ||
| 72 | static resource_size_t phybase; | ||
| 73 | static resource_size_t physize; | ||
| 74 | static int irq; | ||
| 75 | static void __iomem *virtbase; | ||
| 76 | static unsigned long coh901327_users; | ||
| 77 | static unsigned long boot_status; | ||
| 78 | static u16 wdogenablestore; | ||
| 79 | static u16 irqmaskstore; | ||
| 80 | static struct device *parent; | ||
| 81 | |||
| 82 | /* | ||
| 83 | * The watchdog block is of course always clocked, the | ||
| 84 | * clk_enable()/clk_disable() calls are mainly for performing reference | ||
| 85 | * counting higher up in the clock hierarchy. | ||
| 86 | */ | ||
| 87 | static struct clk *clk; | ||
| 88 | |||
| 89 | /* | ||
| 90 | * Enabling and disabling functions. | ||
| 91 | */ | ||
| 92 | static void coh901327_enable(u16 timeout) | ||
| 93 | { | ||
| 94 | u16 val; | ||
| 95 | |||
| 96 | clk_enable(clk); | ||
| 97 | /* Restart timer if it is disabled */ | ||
| 98 | val = readw(virtbase + U300_WDOG_D2R); | ||
| 99 | if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED) | ||
| 100 | writew(U300_WDOG_RR_RESTART_VALUE_RESUME, | ||
| 101 | virtbase + U300_WDOG_RR); | ||
| 102 | /* Acknowledge any pending interrupt so it doesn't just fire off */ | ||
| 103 | writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, | ||
| 104 | virtbase + U300_WDOG_IER); | ||
| 105 | /* Enable the watchdog interrupt */ | ||
| 106 | writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR); | ||
| 107 | /* Activate the watchdog timer */ | ||
| 108 | writew(timeout, virtbase + U300_WDOG_TR); | ||
| 109 | /* Start the watchdog timer */ | ||
| 110 | writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); | ||
| 111 | /* | ||
| 112 | * Extra read so that this change propagate in the watchdog. | ||
| 113 | */ | ||
| 114 | (void) readw(virtbase + U300_WDOG_CR); | ||
| 115 | val = readw(virtbase + U300_WDOG_D2R); | ||
| 116 | clk_disable(clk); | ||
| 117 | if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED) | ||
| 118 | dev_err(parent, | ||
| 119 | "%s(): watchdog not enabled! D2R value %04x\n", | ||
| 120 | __func__, val); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void coh901327_disable(void) | ||
| 124 | { | ||
| 125 | u16 val; | ||
| 126 | |||
| 127 | clk_enable(clk); | ||
| 128 | /* Disable the watchdog interrupt if it is active */ | ||
| 129 | writew(0x0000U, virtbase + U300_WDOG_IMR); | ||
| 130 | /* If the watchdog is currently enabled, attempt to disable it */ | ||
| 131 | val = readw(virtbase + U300_WDOG_D2R); | ||
| 132 | if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) { | ||
| 133 | writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER, | ||
| 134 | virtbase + U300_WDOG_D1R); | ||
| 135 | writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, | ||
| 136 | virtbase + U300_WDOG_D2R); | ||
| 137 | /* Write this twice (else problems occur) */ | ||
| 138 | writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, | ||
| 139 | virtbase + U300_WDOG_D2R); | ||
| 140 | } | ||
| 141 | val = readw(virtbase + U300_WDOG_D2R); | ||
| 142 | clk_disable(clk); | ||
| 143 | if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) | ||
| 144 | dev_err(parent, | ||
| 145 | "%s(): watchdog not disabled! D2R value %04x\n", | ||
| 146 | __func__, val); | ||
| 147 | } | ||
| 148 | |||
| 149 | static void coh901327_start(void) | ||
| 150 | { | ||
| 151 | coh901327_enable(margin * 100); | ||
| 152 | } | ||
| 153 | |||
| 154 | static void coh901327_keepalive(void) | ||
| 155 | { | ||
| 156 | clk_enable(clk); | ||
| 157 | /* Feed the watchdog */ | ||
| 158 | writew(U300_WDOG_FR_FEED_RESTART_TIMER, | ||
| 159 | virtbase + U300_WDOG_FR); | ||
| 160 | clk_disable(clk); | ||
| 161 | } | ||
| 162 | |||
| 163 | static int coh901327_settimeout(int time) | ||
| 164 | { | ||
| 165 | /* | ||
| 166 | * Max margin is 327 since the 10ms | ||
| 167 | * timeout register is max | ||
| 168 | * 0x7FFF = 327670ms ~= 327s. | ||
| 169 | */ | ||
| 170 | if (time <= 0 || time > 327) | ||
| 171 | return -EINVAL; | ||
| 172 | |||
| 173 | margin = time; | ||
| 174 | clk_enable(clk); | ||
| 175 | /* Set new timeout value */ | ||
| 176 | writew(margin * 100, virtbase + U300_WDOG_TR); | ||
| 177 | /* Feed the dog */ | ||
| 178 | writew(U300_WDOG_FR_FEED_RESTART_TIMER, | ||
| 179 | virtbase + U300_WDOG_FR); | ||
| 180 | clk_disable(clk); | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | * This interrupt occurs 10 ms before the watchdog WILL bark. | ||
| 186 | */ | ||
| 187 | static irqreturn_t coh901327_interrupt(int irq, void *data) | ||
| 188 | { | ||
| 189 | u16 val; | ||
| 190 | |||
| 191 | /* | ||
| 192 | * Ack IRQ? If this occurs we're FUBAR anyway, so | ||
| 193 | * just acknowledge, disable the interrupt and await the imminent end. | ||
| 194 | * If you at some point need a host of callbacks to be called | ||
| 195 | * when the system is about to watchdog-reset, add them here! | ||
| 196 | * | ||
| 197 | * NOTE: on future versions of this IP-block, it will be possible | ||
| 198 | * to prevent a watchdog reset by feeding the watchdog at this | ||
| 199 | * point. | ||
| 200 | */ | ||
| 201 | clk_enable(clk); | ||
| 202 | val = readw(virtbase + U300_WDOG_IER); | ||
| 203 | if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND) | ||
| 204 | writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, | ||
| 205 | virtbase + U300_WDOG_IER); | ||
| 206 | writew(0x0000U, virtbase + U300_WDOG_IMR); | ||
| 207 | clk_disable(clk); | ||
| 208 | dev_crit(parent, "watchdog is barking!\n"); | ||
| 209 | return IRQ_HANDLED; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | * Allow only one user (daemon) to open the watchdog | ||
| 214 | */ | ||
| 215 | static int coh901327_open(struct inode *inode, struct file *file) | ||
| 216 | { | ||
| 217 | if (test_and_set_bit(1, &coh901327_users)) | ||
| 218 | return -EBUSY; | ||
| 219 | coh901327_start(); | ||
| 220 | return nonseekable_open(inode, file); | ||
| 221 | } | ||
| 222 | |||
| 223 | static int coh901327_release(struct inode *inode, struct file *file) | ||
| 224 | { | ||
| 225 | clear_bit(1, &coh901327_users); | ||
| 226 | coh901327_disable(); | ||
| 227 | return 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | static ssize_t coh901327_write(struct file *file, const char __user *data, | ||
| 231 | size_t len, loff_t *ppos) | ||
| 232 | { | ||
| 233 | if (len) | ||
| 234 | coh901327_keepalive(); | ||
| 235 | return len; | ||
| 236 | } | ||
| 237 | |||
| 238 | static long coh901327_ioctl(struct file *file, unsigned int cmd, | ||
| 239 | unsigned long arg) | ||
| 240 | { | ||
| 241 | int ret = -ENOTTY; | ||
| 242 | u16 val; | ||
| 243 | int time; | ||
| 244 | int new_options; | ||
| 245 | union { | ||
| 246 | struct watchdog_info __user *ident; | ||
| 247 | int __user *i; | ||
| 248 | } uarg; | ||
| 249 | static struct watchdog_info ident = { | ||
| 250 | .options = WDIOF_CARDRESET | | ||
| 251 | WDIOF_SETTIMEOUT | | ||
| 252 | WDIOF_KEEPALIVEPING, | ||
| 253 | .identity = "COH 901 327 Watchdog", | ||
| 254 | .firmware_version = 1, | ||
| 255 | }; | ||
| 256 | uarg.i = (int __user *)arg; | ||
| 257 | |||
| 258 | switch (cmd) { | ||
| 259 | case WDIOC_GETSUPPORT: | ||
| 260 | ret = copy_to_user(uarg.ident, &ident, | ||
| 261 | sizeof(ident)) ? -EFAULT : 0; | ||
| 262 | break; | ||
| 263 | |||
| 264 | case WDIOC_GETSTATUS: | ||
| 265 | ret = put_user(0, uarg.i); | ||
| 266 | break; | ||
| 267 | |||
| 268 | case WDIOC_GETBOOTSTATUS: | ||
| 269 | ret = put_user(boot_status, uarg.i); | ||
| 270 | break; | ||
| 271 | |||
| 272 | case WDIOC_SETOPTIONS: | ||
| 273 | ret = get_user(new_options, uarg.i); | ||
| 274 | if (ret) | ||
| 275 | break; | ||
| 276 | if (new_options & WDIOS_DISABLECARD) | ||
| 277 | coh901327_disable(); | ||
| 278 | if (new_options & WDIOS_ENABLECARD) | ||
| 279 | coh901327_start(); | ||
| 280 | ret = 0; | ||
| 281 | break; | ||
| 282 | |||
| 283 | case WDIOC_KEEPALIVE: | ||
| 284 | coh901327_keepalive(); | ||
| 285 | ret = 0; | ||
| 286 | break; | ||
| 287 | |||
| 288 | case WDIOC_SETTIMEOUT: | ||
| 289 | ret = get_user(time, uarg.i); | ||
| 290 | if (ret) | ||
| 291 | break; | ||
| 292 | |||
| 293 | ret = coh901327_settimeout(time); | ||
| 294 | if (ret) | ||
| 295 | break; | ||
| 296 | /* Then fall through to return set value */ | ||
| 297 | |||
| 298 | case WDIOC_GETTIMEOUT: | ||
| 299 | ret = put_user(margin, uarg.i); | ||
| 300 | break; | ||
| 301 | |||
| 302 | case WDIOC_GETTIMELEFT: | ||
| 303 | clk_enable(clk); | ||
| 304 | /* Read repeatedly until the value is stable! */ | ||
| 305 | val = readw(virtbase + U300_WDOG_CR); | ||
| 306 | while (val & U300_WDOG_CR_VALID_IND) | ||
| 307 | val = readw(virtbase + U300_WDOG_CR); | ||
| 308 | val &= U300_WDOG_CR_COUNT_VALUE_MASK; | ||
| 309 | clk_disable(clk); | ||
| 310 | if (val != 0) | ||
| 311 | val /= 100; | ||
| 312 | ret = put_user(val, uarg.i); | ||
| 313 | break; | ||
| 314 | } | ||
| 315 | return ret; | ||
| 316 | } | ||
| 317 | |||
| 318 | static const struct file_operations coh901327_fops = { | ||
| 319 | .owner = THIS_MODULE, | ||
| 320 | .llseek = no_llseek, | ||
| 321 | .write = coh901327_write, | ||
| 322 | .unlocked_ioctl = coh901327_ioctl, | ||
| 323 | .open = coh901327_open, | ||
| 324 | .release = coh901327_release, | ||
| 325 | }; | ||
| 326 | |||
| 327 | static struct miscdevice coh901327_miscdev = { | ||
| 328 | .minor = WATCHDOG_MINOR, | ||
| 329 | .name = "watchdog", | ||
| 330 | .fops = &coh901327_fops, | ||
| 331 | }; | ||
| 332 | |||
| 333 | static int __exit coh901327_remove(struct platform_device *pdev) | ||
| 334 | { | ||
| 335 | misc_deregister(&coh901327_miscdev); | ||
| 336 | coh901327_disable(); | ||
| 337 | free_irq(irq, pdev); | ||
| 338 | clk_put(clk); | ||
| 339 | iounmap(virtbase); | ||
| 340 | release_mem_region(phybase, physize); | ||
| 341 | return 0; | ||
| 342 | } | ||
| 343 | |||
| 344 | |||
| 345 | static int __init coh901327_probe(struct platform_device *pdev) | ||
| 346 | { | ||
| 347 | int ret; | ||
| 348 | u16 val; | ||
| 349 | struct resource *res; | ||
| 350 | |||
| 351 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 352 | if (!res) | ||
| 353 | return -ENOENT; | ||
| 354 | |||
| 355 | parent = &pdev->dev; | ||
| 356 | physize = resource_size(res); | ||
| 357 | phybase = res->start; | ||
| 358 | |||
| 359 | if (request_mem_region(phybase, physize, DRV_NAME) == NULL) { | ||
| 360 | ret = -EBUSY; | ||
| 361 | goto out; | ||
| 362 | } | ||
| 363 | |||
| 364 | virtbase = ioremap(phybase, physize); | ||
| 365 | if (!virtbase) { | ||
| 366 | ret = -ENOMEM; | ||
| 367 | goto out_no_remap; | ||
| 368 | } | ||
| 369 | |||
| 370 | clk = clk_get(&pdev->dev, NULL); | ||
| 371 | if (IS_ERR(clk)) { | ||
| 372 | ret = PTR_ERR(clk); | ||
| 373 | dev_err(&pdev->dev, "could not get clock\n"); | ||
| 374 | goto out_no_clk; | ||
| 375 | } | ||
| 376 | ret = clk_enable(clk); | ||
| 377 | if (ret) { | ||
| 378 | dev_err(&pdev->dev, "could not enable clock\n"); | ||
| 379 | goto out_no_clk_enable; | ||
| 380 | } | ||
| 381 | |||
| 382 | val = readw(virtbase + U300_WDOG_SR); | ||
| 383 | switch (val) { | ||
| 384 | case U300_WDOG_SR_STATUS_TIMED_OUT: | ||
| 385 | dev_info(&pdev->dev, | ||
| 386 | "watchdog timed out since last chip reset!\n"); | ||
| 387 | boot_status = WDIOF_CARDRESET; | ||
| 388 | /* Status will be cleared below */ | ||
| 389 | break; | ||
| 390 | case U300_WDOG_SR_STATUS_NORMAL: | ||
| 391 | dev_info(&pdev->dev, | ||
| 392 | "in normal status, no timeouts have occurred.\n"); | ||
| 393 | break; | ||
| 394 | default: | ||
| 395 | dev_info(&pdev->dev, | ||
| 396 | "contains an illegal status code (%08x)\n", val); | ||
| 397 | break; | ||
| 398 | } | ||
| 399 | |||
| 400 | val = readw(virtbase + U300_WDOG_D2R); | ||
| 401 | switch (val) { | ||
| 402 | case U300_WDOG_D2R_DISABLE_STATUS_DISABLED: | ||
| 403 | dev_info(&pdev->dev, "currently disabled.\n"); | ||
| 404 | break; | ||
| 405 | case U300_WDOG_D2R_DISABLE_STATUS_ENABLED: | ||
| 406 | dev_info(&pdev->dev, | ||
| 407 | "currently enabled! (disabling it now)\n"); | ||
| 408 | coh901327_disable(); | ||
| 409 | break; | ||
| 410 | default: | ||
| 411 | dev_err(&pdev->dev, | ||
| 412 | "contains an illegal enable/disable code (%08x)\n", | ||
| 413 | val); | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | |||
| 417 | /* Reset the watchdog */ | ||
| 418 | writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR); | ||
| 419 | |||
| 420 | irq = platform_get_irq(pdev, 0); | ||
| 421 | if (request_irq(irq, coh901327_interrupt, IRQF_DISABLED, | ||
| 422 | DRV_NAME " Bark", pdev)) { | ||
| 423 | ret = -EIO; | ||
| 424 | goto out_no_irq; | ||
| 425 | } | ||
| 426 | |||
| 427 | clk_disable(clk); | ||
| 428 | |||
| 429 | ret = misc_register(&coh901327_miscdev); | ||
| 430 | if (ret == 0) | ||
| 431 | dev_info(&pdev->dev, | ||
| 432 | "initialized. timer margin=%d sec\n", margin); | ||
| 433 | else | ||
| 434 | goto out_no_wdog; | ||
| 435 | |||
| 436 | return 0; | ||
| 437 | |||
| 438 | out_no_wdog: | ||
| 439 | free_irq(irq, pdev); | ||
| 440 | out_no_irq: | ||
| 441 | clk_disable(clk); | ||
| 442 | out_no_clk_enable: | ||
| 443 | clk_put(clk); | ||
| 444 | out_no_clk: | ||
| 445 | iounmap(virtbase); | ||
| 446 | out_no_remap: | ||
| 447 | release_mem_region(phybase, SZ_4K); | ||
| 448 | out: | ||
| 449 | return ret; | ||
| 450 | } | ||
| 451 | |||
| 452 | #ifdef CONFIG_PM | ||
| 453 | static int coh901327_suspend(struct platform_device *pdev, pm_message_t state) | ||
| 454 | { | ||
| 455 | irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U; | ||
| 456 | wdogenablestore = readw(virtbase + U300_WDOG_D2R); | ||
| 457 | /* If watchdog is on, disable it here and now */ | ||
| 458 | if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) | ||
| 459 | coh901327_disable(); | ||
| 460 | return 0; | ||
| 461 | } | ||
| 462 | |||
| 463 | static int coh901327_resume(struct platform_device *pdev) | ||
| 464 | { | ||
| 465 | /* Restore the watchdog interrupt */ | ||
| 466 | writew(irqmaskstore, virtbase + U300_WDOG_IMR); | ||
| 467 | if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) { | ||
| 468 | /* Restart the watchdog timer */ | ||
| 469 | writew(U300_WDOG_RR_RESTART_VALUE_RESUME, | ||
| 470 | virtbase + U300_WDOG_RR); | ||
| 471 | writew(U300_WDOG_FR_FEED_RESTART_TIMER, | ||
| 472 | virtbase + U300_WDOG_FR); | ||
| 473 | } | ||
| 474 | return 0; | ||
| 475 | } | ||
| 476 | #else | ||
| 477 | #define coh901327_suspend NULL | ||
| 478 | #define coh901327_resume NULL | ||
| 479 | #endif | ||
| 480 | |||
| 481 | /* | ||
| 482 | * Mistreating the watchdog is the only way to perform a software reset of the | ||
| 483 | * system on EMP platforms. So we implement this and export a symbol for it. | ||
| 484 | */ | ||
| 485 | void coh901327_watchdog_reset(void) | ||
| 486 | { | ||
| 487 | /* Enable even if on JTAG too */ | ||
| 488 | writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE, | ||
| 489 | virtbase + U300_WDOG_JOR); | ||
| 490 | /* | ||
| 491 | * Timeout = 5s, we have to wait for the watchdog reset to | ||
| 492 | * actually take place: the watchdog will be reloaded with the | ||
| 493 | * default value immediately, so we HAVE to reboot and get back | ||
| 494 | * into the kernel in 30s, or the device will reboot again! | ||
| 495 | * The boot loader will typically deactivate the watchdog, so we | ||
| 496 | * need time enough for the boot loader to get to the point of | ||
| 497 | * deactivating the watchdog before it is shut down by it. | ||
| 498 | * | ||
| 499 | * NOTE: on future versions of the watchdog, this restriction is | ||
| 500 | * gone: the watchdog will be reloaded with a defaul value (1 min) | ||
| 501 | * instead of last value, and you can conveniently set the watchdog | ||
| 502 | * timeout to 10ms (value = 1) without any problems. | ||
| 503 | */ | ||
| 504 | coh901327_enable(500); | ||
| 505 | /* Return and await doom */ | ||
| 506 | } | ||
| 507 | |||
| 508 | static struct platform_driver coh901327_driver = { | ||
| 509 | .driver = { | ||
| 510 | .owner = THIS_MODULE, | ||
| 511 | .name = "coh901327_wdog", | ||
| 512 | }, | ||
| 513 | .remove = __exit_p(coh901327_remove), | ||
| 514 | .suspend = coh901327_suspend, | ||
| 515 | .resume = coh901327_resume, | ||
| 516 | }; | ||
| 517 | |||
| 518 | static int __init coh901327_init(void) | ||
| 519 | { | ||
| 520 | return platform_driver_probe(&coh901327_driver, coh901327_probe); | ||
| 521 | } | ||
| 522 | module_init(coh901327_init); | ||
| 523 | |||
| 524 | static void __exit coh901327_exit(void) | ||
| 525 | { | ||
| 526 | platform_driver_unregister(&coh901327_driver); | ||
| 527 | } | ||
| 528 | module_exit(coh901327_exit); | ||
| 529 | |||
| 530 | MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); | ||
| 531 | MODULE_DESCRIPTION("COH 901 327 Watchdog"); | ||
| 532 | |||
| 533 | module_param(margin, int, 0); | ||
| 534 | MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); | ||
| 535 | |||
| 536 | MODULE_LICENSE("GPL"); | ||
| 537 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index c0b9169ba5d5..a6c5674c78e6 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
| @@ -120,7 +120,8 @@ static int nowayout = WATCHDOG_NOWAYOUT; | |||
| 120 | static char expect_release; | 120 | static char expect_release; |
| 121 | static unsigned long hpwdt_is_open; | 121 | static unsigned long hpwdt_is_open; |
| 122 | static unsigned int allow_kdump; | 122 | static unsigned int allow_kdump; |
| 123 | static int hpwdt_nmi_sourcing; | 123 | static unsigned int hpwdt_nmi_sourcing; |
| 124 | static unsigned int priority; /* hpwdt at end of die_notify list */ | ||
| 124 | 125 | ||
| 125 | static void __iomem *pci_mem_addr; /* the PCI-memory address */ | 126 | static void __iomem *pci_mem_addr; /* the PCI-memory address */ |
| 126 | static unsigned long __iomem *hpwdt_timer_reg; | 127 | static unsigned long __iomem *hpwdt_timer_reg; |
| @@ -623,7 +624,7 @@ static struct miscdevice hpwdt_miscdev = { | |||
| 623 | 624 | ||
| 624 | static struct notifier_block die_notifier = { | 625 | static struct notifier_block die_notifier = { |
| 625 | .notifier_call = hpwdt_pretimeout, | 626 | .notifier_call = hpwdt_pretimeout, |
| 626 | .priority = 0x7FFFFFFF, | 627 | .priority = 0, |
| 627 | }; | 628 | }; |
| 628 | 629 | ||
| 629 | /* | 630 | /* |
| @@ -641,7 +642,8 @@ static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) | |||
| 641 | hpwdt_nmi_sourcing = 1; | 642 | hpwdt_nmi_sourcing = 1; |
| 642 | else | 643 | else |
| 643 | dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " | 644 | dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " |
| 644 | "functionality you must reboot with nmi_watchdog=0.\n"); | 645 | "functionality you must reboot with nmi_watchdog=0 " |
| 646 | "and load the hpwdt driver with priority=1.\n"); | ||
| 645 | } | 647 | } |
| 646 | #else | 648 | #else |
| 647 | static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) | 649 | static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) |
| @@ -714,6 +716,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
| 714 | cmn_regs.u1.rah = 0x0D; | 716 | cmn_regs.u1.rah = 0x0D; |
| 715 | cmn_regs.u1.ral = 0x02; | 717 | cmn_regs.u1.ral = 0x02; |
| 716 | 718 | ||
| 719 | /* | ||
| 720 | * If the priority is set to 1, then we will be put first on the | ||
| 721 | * die notify list to handle a critical NMI. The default is to | ||
| 722 | * be last so other users of the NMI signal can function. | ||
| 723 | */ | ||
| 724 | if (priority) | ||
| 725 | die_notifier.priority = 0x7FFFFFFF; | ||
| 726 | |||
| 717 | retval = register_die_notifier(&die_notifier); | 727 | retval = register_die_notifier(&die_notifier); |
| 718 | if (retval != 0) { | 728 | if (retval != 0) { |
| 719 | dev_warn(&dev->dev, | 729 | dev_warn(&dev->dev, |
| @@ -733,9 +743,11 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
| 733 | printk(KERN_INFO | 743 | printk(KERN_INFO |
| 734 | "hp Watchdog Timer Driver: %s" | 744 | "hp Watchdog Timer Driver: %s" |
| 735 | ", timer margin: %d seconds (nowayout=%d)" | 745 | ", timer margin: %d seconds (nowayout=%d)" |
| 736 | ", allow kernel dump: %s (default = 0/OFF).\n", | 746 | ", allow kernel dump: %s (default = 0/OFF)" |
| 747 | ", priority: %s (default = 0/LAST).\n", | ||
| 737 | HPWDT_VERSION, soft_margin, nowayout, | 748 | HPWDT_VERSION, soft_margin, nowayout, |
| 738 | (allow_kdump == 0) ? "OFF" : "ON"); | 749 | (allow_kdump == 0) ? "OFF" : "ON", |
| 750 | (priority == 0) ? "LAST" : "FIRST"); | ||
| 739 | 751 | ||
| 740 | return 0; | 752 | return 0; |
| 741 | 753 | ||
| @@ -798,5 +810,9 @@ module_param(nowayout, int, 0); | |||
| 798 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | 810 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
| 799 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 811 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
| 800 | 812 | ||
| 813 | module_param(priority, int, 0); | ||
| 814 | MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" | ||
| 815 | " (default = 0/Last)\n"); | ||
| 816 | |||
| 801 | module_init(hpwdt_init); | 817 | module_init(hpwdt_init); |
| 802 | module_exit(hpwdt_cleanup); | 818 | module_exit(hpwdt_cleanup); |
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index f2713851aaab..3ed571a2ab18 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c | |||
| @@ -159,6 +159,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file) | |||
| 159 | file->private_data = (void *) wdev; | 159 | file->private_data = (void *) wdev; |
| 160 | 160 | ||
| 161 | omap_wdt_set_timeout(wdev); | 161 | omap_wdt_set_timeout(wdev); |
| 162 | omap_wdt_ping(wdev); /* trigger loading of new timeout value */ | ||
| 162 | omap_wdt_enable(wdev); | 163 | omap_wdt_enable(wdev); |
| 163 | 164 | ||
| 164 | return nonseekable_open(inode, file); | 165 | return nonseekable_open(inode, file); |
| @@ -313,6 +314,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) | |||
| 313 | 314 | ||
| 314 | platform_set_drvdata(pdev, wdev); | 315 | platform_set_drvdata(pdev, wdev); |
| 315 | 316 | ||
| 317 | clk_enable(wdev->ick); | ||
| 318 | clk_enable(wdev->fck); | ||
| 319 | |||
| 316 | omap_wdt_disable(wdev); | 320 | omap_wdt_disable(wdev); |
| 317 | omap_wdt_adjust_timeout(timer_margin); | 321 | omap_wdt_adjust_timeout(timer_margin); |
| 318 | 322 | ||
| @@ -332,6 +336,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) | |||
| 332 | /* autogate OCP interface clock */ | 336 | /* autogate OCP interface clock */ |
| 333 | __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); | 337 | __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); |
| 334 | 338 | ||
| 339 | clk_disable(wdev->ick); | ||
| 340 | clk_disable(wdev->fck); | ||
| 341 | |||
| 335 | omap_wdt_dev = pdev; | 342 | omap_wdt_dev = pdev; |
| 336 | 343 | ||
| 337 | return 0; | 344 | return 0; |
diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c new file mode 100644 index 000000000000..538ec2c05197 --- /dev/null +++ b/drivers/watchdog/pnx833x_wdt.c | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | /* | ||
| 2 | * PNX833x Hardware Watchdog Driver | ||
| 3 | * Copyright 2008 NXP Semiconductors | ||
| 4 | * Daniel Laird <daniel.j.laird@nxp.com> | ||
| 5 | * Andre McCurdy <andre.mccurdy@nxp.com> | ||
| 6 | * | ||
| 7 | * Heavily based upon - IndyDog 0.3 | ||
| 8 | * A Hardware Watchdog Device for SGI IP22 | ||
| 9 | * | ||
| 10 | * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved. | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or | ||
| 13 | * modify it under the terms of the GNU General Public License | ||
| 14 | * as published by the Free Software Foundation; either version | ||
| 15 | * 2 of the License, or (at your option) any later version. | ||
| 16 | * | ||
| 17 | * based on softdog.c by Alan Cox <alan@redhat.com> | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/moduleparam.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/fs.h> | ||
| 25 | #include <linux/mm.h> | ||
| 26 | #include <linux/miscdevice.h> | ||
| 27 | #include <linux/watchdog.h> | ||
| 28 | #include <linux/notifier.h> | ||
| 29 | #include <linux/reboot.h> | ||
| 30 | #include <linux/init.h> | ||
| 31 | #include <asm/mach-pnx833x/pnx833x.h> | ||
| 32 | |||
| 33 | #define PFX "pnx833x: " | ||
| 34 | #define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */ | ||
| 35 | #define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */ | ||
| 36 | |||
| 37 | /** CONFIG block */ | ||
| 38 | #define PNX833X_CONFIG (0x07000U) | ||
| 39 | #define PNX833X_CONFIG_CPU_WATCHDOG (0x54) | ||
| 40 | #define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58) | ||
| 41 | #define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c) | ||
| 42 | |||
| 43 | /** RESET block */ | ||
| 44 | #define PNX833X_RESET (0x08000U) | ||
| 45 | #define PNX833X_RESET_CONFIG (0x08) | ||
| 46 | |||
| 47 | static int pnx833x_wdt_alive; | ||
| 48 | |||
| 49 | /* Set default timeout in MHZ.*/ | ||
| 50 | static int pnx833x_wdt_timeout = (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY); | ||
| 51 | module_param(pnx833x_wdt_timeout, int, 0); | ||
| 52 | MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default=" | ||
| 53 | __MODULE_STRING(pnx833x_wdt_timeout) "(30 seconds)."); | ||
| 54 | |||
| 55 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 56 | module_param(nowayout, int, 0); | ||
| 57 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
| 58 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
| 59 | |||
| 60 | static int start_enabled = 1; | ||
| 61 | module_param(start_enabled, int, 0); | ||
| 62 | MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion " | ||
| 63 | "(default=" __MODULE_STRING(start_enabled) ")"); | ||
| 64 | |||
| 65 | static void pnx833x_wdt_start(void) | ||
| 66 | { | ||
| 67 | /* Enable watchdog causing reset. */ | ||
| 68 | PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1; | ||
| 69 | /* Set timeout.*/ | ||
| 70 | PNX833X_REG(PNX833X_CONFIG + | ||
| 71 | PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; | ||
| 72 | /* Enable watchdog. */ | ||
| 73 | PNX833X_REG(PNX833X_CONFIG + | ||
| 74 | PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1; | ||
| 75 | |||
| 76 | printk(KERN_INFO PFX "Started watchdog timer.\n"); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void pnx833x_wdt_stop(void) | ||
| 80 | { | ||
| 81 | /* Disable watchdog causing reset. */ | ||
| 82 | PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE; | ||
| 83 | /* Disable watchdog.*/ | ||
| 84 | PNX833X_REG(PNX833X_CONFIG + | ||
| 85 | PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE; | ||
| 86 | |||
| 87 | printk(KERN_INFO PFX "Stopped watchdog timer.\n"); | ||
| 88 | } | ||
| 89 | |||
| 90 | static void pnx833x_wdt_ping(void) | ||
| 91 | { | ||
| 92 | PNX833X_REG(PNX833X_CONFIG + | ||
| 93 | PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* | ||
| 97 | * Allow only one person to hold it open | ||
| 98 | */ | ||
| 99 | static int pnx833x_wdt_open(struct inode *inode, struct file *file) | ||
| 100 | { | ||
| 101 | if (test_and_set_bit(0, &pnx833x_wdt_alive)) | ||
| 102 | return -EBUSY; | ||
| 103 | |||
| 104 | if (nowayout) | ||
| 105 | __module_get(THIS_MODULE); | ||
| 106 | |||
| 107 | /* Activate timer */ | ||
| 108 | if (!start_enabled) | ||
| 109 | pnx833x_wdt_start(); | ||
| 110 | |||
| 111 | pnx833x_wdt_ping(); | ||
| 112 | |||
| 113 | printk(KERN_INFO "Started watchdog timer.\n"); | ||
| 114 | |||
| 115 | return nonseekable_open(inode, file); | ||
| 116 | } | ||
| 117 | |||
| 118 | static int pnx833x_wdt_release(struct inode *inode, struct file *file) | ||
| 119 | { | ||
| 120 | /* Shut off the timer. | ||
| 121 | * Lock it in if it's a module and we defined ...NOWAYOUT */ | ||
| 122 | if (!nowayout) | ||
| 123 | pnx833x_wdt_stop(); /* Turn the WDT off */ | ||
| 124 | |||
| 125 | clear_bit(0, &pnx833x_wdt_alive); | ||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) | ||
| 130 | { | ||
| 131 | /* Refresh the timer. */ | ||
| 132 | if (len) | ||
| 133 | pnx833x_wdt_ping(); | ||
| 134 | |||
| 135 | return len; | ||
| 136 | } | ||
| 137 | |||
| 138 | static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd, | ||
| 139 | unsigned long arg) | ||
| 140 | { | ||
| 141 | int options, new_timeout = 0; | ||
| 142 | uint32_t timeout, timeout_left = 0; | ||
| 143 | |||
| 144 | static struct watchdog_info ident = { | ||
| 145 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, | ||
| 146 | .firmware_version = 0, | ||
| 147 | .identity = "Hardware Watchdog for PNX833x", | ||
| 148 | }; | ||
| 149 | |||
| 150 | switch (cmd) { | ||
| 151 | default: | ||
| 152 | return -ENOTTY; | ||
| 153 | |||
| 154 | case WDIOC_GETSUPPORT: | ||
| 155 | if (copy_to_user((struct watchdog_info *)arg, | ||
| 156 | &ident, sizeof(ident))) | ||
| 157 | return -EFAULT; | ||
| 158 | return 0; | ||
| 159 | |||
| 160 | case WDIOC_GETSTATUS: | ||
| 161 | case WDIOC_GETBOOTSTATUS: | ||
| 162 | return put_user(0, (int *)arg); | ||
| 163 | |||
| 164 | case WDIOC_SETOPTIONS: | ||
| 165 | if (get_user(options, (int *)arg)) | ||
| 166 | return -EFAULT; | ||
| 167 | |||
| 168 | if (options & WDIOS_DISABLECARD) | ||
| 169 | pnx833x_wdt_stop(); | ||
| 170 | |||
| 171 | if (options & WDIOS_ENABLECARD) | ||
| 172 | pnx833x_wdt_start(); | ||
| 173 | |||
| 174 | return 0; | ||
| 175 | |||
| 176 | case WDIOC_KEEPALIVE: | ||
| 177 | pnx833x_wdt_ping(); | ||
| 178 | return 0; | ||
| 179 | |||
| 180 | case WDIOC_SETTIMEOUT: | ||
| 181 | { | ||
| 182 | if (get_user(new_timeout, (int *)arg)) | ||
| 183 | return -EFAULT; | ||
| 184 | |||
| 185 | pnx833x_wdt_timeout = new_timeout; | ||
| 186 | PNX833X_REG(PNX833X_CONFIG + | ||
| 187 | PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout; | ||
| 188 | return put_user(new_timeout, (int *)arg); | ||
| 189 | } | ||
| 190 | |||
| 191 | case WDIOC_GETTIMEOUT: | ||
| 192 | timeout = PNX833X_REG(PNX833X_CONFIG + | ||
| 193 | PNX833X_CONFIG_CPU_WATCHDOG_COMPARE); | ||
| 194 | return put_user(timeout, (int *)arg); | ||
| 195 | |||
| 196 | case WDIOC_GETTIMELEFT: | ||
| 197 | timeout_left = PNX833X_REG(PNX833X_CONFIG + | ||
| 198 | PNX833X_CONFIG_CPU_WATCHDOG); | ||
| 199 | return put_user(timeout_left, (int *)arg); | ||
| 200 | |||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | static int pnx833x_wdt_notify_sys(struct notifier_block *this, | ||
| 205 | unsigned long code, void *unused) | ||
| 206 | { | ||
| 207 | if (code == SYS_DOWN || code == SYS_HALT) | ||
| 208 | pnx833x_wdt_stop(); /* Turn the WDT off */ | ||
| 209 | |||
| 210 | return NOTIFY_DONE; | ||
| 211 | } | ||
| 212 | |||
| 213 | static const struct file_operations pnx833x_wdt_fops = { | ||
| 214 | .owner = THIS_MODULE, | ||
| 215 | .llseek = no_llseek, | ||
| 216 | .write = pnx833x_wdt_write, | ||
| 217 | .unlocked_ioctl = pnx833x_wdt_ioctl, | ||
| 218 | .open = pnx833x_wdt_open, | ||
| 219 | .release = pnx833x_wdt_release, | ||
| 220 | }; | ||
| 221 | |||
| 222 | static struct miscdevice pnx833x_wdt_miscdev = { | ||
| 223 | .minor = WATCHDOG_MINOR, | ||
| 224 | .name = "watchdog", | ||
| 225 | .fops = &pnx833x_wdt_fops, | ||
| 226 | }; | ||
| 227 | |||
| 228 | static struct notifier_block pnx833x_wdt_notifier = { | ||
| 229 | .notifier_call = pnx833x_wdt_notify_sys, | ||
| 230 | }; | ||
| 231 | |||
| 232 | static char banner[] __initdata = | ||
| 233 | KERN_INFO PFX "Hardware Watchdog Timer for PNX833x: Version 0.1\n"; | ||
| 234 | |||
| 235 | static int __init watchdog_init(void) | ||
| 236 | { | ||
| 237 | int ret, cause; | ||
| 238 | |||
| 239 | /* Lets check the reason for the reset.*/ | ||
| 240 | cause = PNX833X_REG(PNX833X_RESET); | ||
| 241 | /*If bit 31 is set then watchdog was cause of reset.*/ | ||
| 242 | if (cause & 0x80000000) { | ||
| 243 | printk(KERN_INFO PFX "The system was previously reset due to " | ||
| 244 | "the watchdog firing - please investigate...\n"); | ||
| 245 | } | ||
| 246 | |||
| 247 | ret = register_reboot_notifier(&pnx833x_wdt_notifier); | ||
| 248 | if (ret) { | ||
| 249 | printk(KERN_ERR PFX | ||
| 250 | "cannot register reboot notifier (err=%d)\n", ret); | ||
| 251 | return ret; | ||
| 252 | } | ||
| 253 | |||
| 254 | ret = misc_register(&pnx833x_wdt_miscdev); | ||
| 255 | if (ret) { | ||
| 256 | printk(KERN_ERR PFX | ||
| 257 | "cannot register miscdev on minor=%d (err=%d)\n", | ||
| 258 | WATCHDOG_MINOR, ret); | ||
| 259 | unregister_reboot_notifier(&pnx833x_wdt_notifier); | ||
| 260 | return ret; | ||
| 261 | } | ||
| 262 | |||
| 263 | printk(banner); | ||
| 264 | if (start_enabled) | ||
| 265 | pnx833x_wdt_start(); | ||
| 266 | |||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | static void __exit watchdog_exit(void) | ||
| 271 | { | ||
| 272 | misc_deregister(&pnx833x_wdt_miscdev); | ||
| 273 | unregister_reboot_notifier(&pnx833x_wdt_notifier); | ||
| 274 | } | ||
| 275 | |||
| 276 | module_init(watchdog_init); | ||
| 277 | module_exit(watchdog_exit); | ||
| 278 | |||
| 279 | MODULE_AUTHOR("Daniel Laird/Andre McCurdy"); | ||
| 280 | MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x"); | ||
| 281 | MODULE_LICENSE("GPL"); | ||
| 282 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c new file mode 100644 index 000000000000..5dd952681f32 --- /dev/null +++ b/drivers/watchdog/stmp3xxx_wdt.c | |||
| @@ -0,0 +1,296 @@ | |||
| 1 | /* | ||
| 2 | * Watchdog driver for Freescale STMP37XX/STMP378X | ||
| 3 | * | ||
| 4 | * Author: Vitaly Wool <vital@embeddedalley.com> | ||
| 5 | * | ||
| 6 | * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. | ||
| 7 | * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. | ||
| 8 | */ | ||
| 9 | #include <linux/init.h> | ||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/fs.h> | ||
| 12 | #include <linux/miscdevice.h> | ||
| 13 | #include <linux/watchdog.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/spinlock.h> | ||
| 16 | #include <linux/uaccess.h> | ||
| 17 | |||
| 18 | #include <mach/platform.h> | ||
| 19 | #include <mach/regs-rtc.h> | ||
| 20 | |||
| 21 | #define DEFAULT_HEARTBEAT 19 | ||
| 22 | #define MAX_HEARTBEAT (0x10000000 >> 6) | ||
| 23 | |||
| 24 | /* missing bitmask in headers */ | ||
| 25 | #define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000 | ||
| 26 | |||
| 27 | #define WDT_IN_USE 0 | ||
| 28 | #define WDT_OK_TO_CLOSE 1 | ||
| 29 | |||
| 30 | #define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */ | ||
| 31 | |||
| 32 | static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock); | ||
| 33 | static unsigned long wdt_status; | ||
| 34 | static const int nowayout = WATCHDOG_NOWAYOUT; | ||
| 35 | static int heartbeat = DEFAULT_HEARTBEAT; | ||
| 36 | static unsigned long boot_status; | ||
| 37 | |||
| 38 | static void wdt_enable(u32 value) | ||
| 39 | { | ||
| 40 | spin_lock(&stmp3xxx_wdt_io_lock); | ||
| 41 | __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG); | ||
| 42 | stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); | ||
| 43 | stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, | ||
| 44 | REGS_RTC_BASE + HW_RTC_PERSISTENT1); | ||
| 45 | spin_unlock(&stmp3xxx_wdt_io_lock); | ||
| 46 | } | ||
| 47 | |||
| 48 | static void wdt_disable(void) | ||
| 49 | { | ||
| 50 | spin_lock(&stmp3xxx_wdt_io_lock); | ||
| 51 | stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, | ||
| 52 | REGS_RTC_BASE + HW_RTC_PERSISTENT1); | ||
| 53 | stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); | ||
| 54 | spin_unlock(&stmp3xxx_wdt_io_lock); | ||
| 55 | } | ||
| 56 | |||
| 57 | static void wdt_ping(void) | ||
| 58 | { | ||
| 59 | wdt_enable(heartbeat * WDOG_COUNTER_RATE); | ||
| 60 | } | ||
| 61 | |||
| 62 | static int stmp3xxx_wdt_open(struct inode *inode, struct file *file) | ||
| 63 | { | ||
| 64 | if (test_and_set_bit(WDT_IN_USE, &wdt_status)) | ||
| 65 | return -EBUSY; | ||
| 66 | |||
| 67 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 68 | wdt_ping(); | ||
| 69 | |||
| 70 | return nonseekable_open(inode, file); | ||
| 71 | } | ||
| 72 | |||
| 73 | static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data, | ||
| 74 | size_t len, loff_t *ppos) | ||
| 75 | { | ||
| 76 | if (len) { | ||
| 77 | if (!nowayout) { | ||
| 78 | size_t i; | ||
| 79 | |||
| 80 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 81 | |||
| 82 | for (i = 0; i != len; i++) { | ||
| 83 | char c; | ||
| 84 | |||
| 85 | if (get_user(c, data + i)) | ||
| 86 | return -EFAULT; | ||
| 87 | if (c == 'V') | ||
| 88 | set_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | wdt_ping(); | ||
| 92 | } | ||
| 93 | |||
| 94 | return len; | ||
| 95 | } | ||
| 96 | |||
| 97 | static struct watchdog_info ident = { | ||
| 98 | .options = WDIOF_CARDRESET | | ||
| 99 | WDIOF_MAGICCLOSE | | ||
| 100 | WDIOF_SETTIMEOUT | | ||
| 101 | WDIOF_KEEPALIVEPING, | ||
| 102 | .identity = "STMP3XXX Watchdog", | ||
| 103 | }; | ||
| 104 | |||
| 105 | static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd, | ||
| 106 | unsigned long arg) | ||
| 107 | { | ||
| 108 | void __user *argp = (void __user *)arg; | ||
| 109 | int __user *p = argp; | ||
| 110 | int new_heartbeat, opts; | ||
| 111 | int ret = -ENOTTY; | ||
| 112 | |||
| 113 | switch (cmd) { | ||
| 114 | case WDIOC_GETSUPPORT: | ||
| 115 | ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; | ||
| 116 | break; | ||
| 117 | |||
| 118 | case WDIOC_GETSTATUS: | ||
| 119 | ret = put_user(0, p); | ||
| 120 | break; | ||
| 121 | |||
| 122 | case WDIOC_GETBOOTSTATUS: | ||
| 123 | ret = put_user(boot_status, p); | ||
| 124 | break; | ||
| 125 | |||
| 126 | case WDIOC_SETOPTIONS: | ||
| 127 | if (get_user(opts, p)) { | ||
| 128 | ret = -EFAULT; | ||
| 129 | break; | ||
| 130 | } | ||
| 131 | if (opts & WDIOS_DISABLECARD) | ||
| 132 | wdt_disable(); | ||
| 133 | else if (opts & WDIOS_ENABLECARD) | ||
| 134 | wdt_ping(); | ||
| 135 | else { | ||
| 136 | pr_debug("%s: unknown option 0x%x\n", __func__, opts); | ||
| 137 | ret = -EINVAL; | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | ret = 0; | ||
| 141 | break; | ||
| 142 | |||
| 143 | case WDIOC_KEEPALIVE: | ||
| 144 | wdt_ping(); | ||
| 145 | ret = 0; | ||
| 146 | break; | ||
| 147 | |||
| 148 | case WDIOC_SETTIMEOUT: | ||
| 149 | if (get_user(new_heartbeat, p)) { | ||
| 150 | ret = -EFAULT; | ||
| 151 | break; | ||
| 152 | } | ||
| 153 | if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) { | ||
| 154 | ret = -EINVAL; | ||
| 155 | break; | ||
| 156 | } | ||
| 157 | |||
| 158 | heartbeat = new_heartbeat; | ||
| 159 | wdt_ping(); | ||
| 160 | /* Fall through */ | ||
| 161 | |||
| 162 | case WDIOC_GETTIMEOUT: | ||
| 163 | ret = put_user(heartbeat, p); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | return ret; | ||
| 167 | } | ||
| 168 | |||
| 169 | static int stmp3xxx_wdt_release(struct inode *inode, struct file *file) | ||
| 170 | { | ||
| 171 | int ret = 0; | ||
| 172 | |||
| 173 | if (!nowayout) { | ||
| 174 | if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { | ||
| 175 | wdt_ping(); | ||
| 176 | pr_debug("%s: Device closed unexpectdly\n", __func__); | ||
| 177 | ret = -EINVAL; | ||
| 178 | } else { | ||
| 179 | wdt_disable(); | ||
| 180 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | clear_bit(WDT_IN_USE, &wdt_status); | ||
| 184 | |||
| 185 | return ret; | ||
| 186 | } | ||
| 187 | |||
| 188 | static const struct file_operations stmp3xxx_wdt_fops = { | ||
| 189 | .owner = THIS_MODULE, | ||
| 190 | .llseek = no_llseek, | ||
| 191 | .write = stmp3xxx_wdt_write, | ||
| 192 | .unlocked_ioctl = stmp3xxx_wdt_ioctl, | ||
| 193 | .open = stmp3xxx_wdt_open, | ||
| 194 | .release = stmp3xxx_wdt_release, | ||
| 195 | }; | ||
| 196 | |||
| 197 | static struct miscdevice stmp3xxx_wdt_miscdev = { | ||
| 198 | .minor = WATCHDOG_MINOR, | ||
| 199 | .name = "watchdog", | ||
| 200 | .fops = &stmp3xxx_wdt_fops, | ||
| 201 | }; | ||
| 202 | |||
| 203 | static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev) | ||
| 204 | { | ||
| 205 | int ret = 0; | ||
| 206 | |||
| 207 | if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) | ||
| 208 | heartbeat = DEFAULT_HEARTBEAT; | ||
| 209 | |||
| 210 | boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) & | ||
| 211 | BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER; | ||
| 212 | boot_status = !!boot_status; | ||
| 213 | stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, | ||
| 214 | REGS_RTC_BASE + HW_RTC_PERSISTENT1); | ||
| 215 | wdt_disable(); /* disable for now */ | ||
| 216 | |||
| 217 | ret = misc_register(&stmp3xxx_wdt_miscdev); | ||
| 218 | if (ret < 0) { | ||
| 219 | dev_err(&pdev->dev, "cannot register misc device\n"); | ||
| 220 | return ret; | ||
| 221 | } | ||
| 222 | |||
| 223 | printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n", | ||
| 224 | heartbeat); | ||
| 225 | |||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | |||
| 229 | static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev) | ||
| 230 | { | ||
| 231 | misc_deregister(&stmp3xxx_wdt_miscdev); | ||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | #ifdef CONFIG_PM | ||
| 236 | static int wdt_suspended; | ||
| 237 | static u32 wdt_saved_time; | ||
| 238 | |||
| 239 | static int stmp3xxx_wdt_suspend(struct platform_device *pdev, | ||
| 240 | pm_message_t state) | ||
| 241 | { | ||
| 242 | if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) & | ||
| 243 | BM_RTC_CTRL_WATCHDOGEN) { | ||
| 244 | wdt_suspended = 1; | ||
| 245 | wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG); | ||
| 246 | wdt_disable(); | ||
| 247 | } | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static int stmp3xxx_wdt_resume(struct platform_device *pdev) | ||
| 252 | { | ||
| 253 | if (wdt_suspended) { | ||
| 254 | wdt_enable(wdt_saved_time); | ||
| 255 | wdt_suspended = 0; | ||
| 256 | } | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | #else | ||
| 260 | #define stmp3xxx_wdt_suspend NULL | ||
| 261 | #define stmp3xxx_wdt_resume NULL | ||
| 262 | #endif | ||
| 263 | |||
| 264 | static struct platform_driver platform_wdt_driver = { | ||
| 265 | .driver = { | ||
| 266 | .name = "stmp3xxx_wdt", | ||
| 267 | }, | ||
| 268 | .probe = stmp3xxx_wdt_probe, | ||
| 269 | .remove = __devexit_p(stmp3xxx_wdt_remove), | ||
| 270 | .suspend = stmp3xxx_wdt_suspend, | ||
| 271 | .resume = stmp3xxx_wdt_resume, | ||
| 272 | }; | ||
| 273 | |||
| 274 | static int __init stmp3xxx_wdt_init(void) | ||
| 275 | { | ||
| 276 | return platform_driver_register(&platform_wdt_driver); | ||
| 277 | } | ||
| 278 | |||
| 279 | static void __exit stmp3xxx_wdt_exit(void) | ||
| 280 | { | ||
| 281 | return platform_driver_unregister(&platform_wdt_driver); | ||
| 282 | } | ||
| 283 | |||
| 284 | module_init(stmp3xxx_wdt_init); | ||
| 285 | module_exit(stmp3xxx_wdt_exit); | ||
| 286 | |||
| 287 | MODULE_DESCRIPTION("STMP3XXX Watchdog Driver"); | ||
| 288 | MODULE_LICENSE("GPL"); | ||
| 289 | |||
| 290 | module_param(heartbeat, int, 0); | ||
| 291 | MODULE_PARM_DESC(heartbeat, | ||
| 292 | "Watchdog heartbeat period in seconds from 1 to " | ||
| 293 | __MODULE_STRING(MAX_HEARTBEAT) ", default " | ||
| 294 | __MODULE_STRING(DEFAULT_HEARTBEAT)); | ||
| 295 | |||
| 296 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c new file mode 100644 index 000000000000..cb46556f2973 --- /dev/null +++ b/drivers/watchdog/twl4030_wdt.c | |||
| @@ -0,0 +1,272 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) Nokia Corporation | ||
| 3 | * | ||
| 4 | * Written by Timo Kokkonen <timo.t.kokkonen at nokia.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/fs.h> | ||
| 25 | #include <linux/watchdog.h> | ||
| 26 | #include <linux/platform_device.h> | ||
| 27 | #include <linux/miscdevice.h> | ||
| 28 | #include <linux/uaccess.h> | ||
| 29 | #include <linux/i2c/twl4030.h> | ||
| 30 | |||
| 31 | #define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 | ||
| 32 | |||
| 33 | #define TWL4030_WDT_STATE_OPEN 0x1 | ||
| 34 | #define TWL4030_WDT_STATE_ACTIVE 0x8 | ||
| 35 | |||
| 36 | static struct platform_device *twl4030_wdt_dev; | ||
| 37 | |||
| 38 | struct twl4030_wdt { | ||
| 39 | struct miscdevice miscdev; | ||
| 40 | int timer_margin; | ||
| 41 | unsigned long state; | ||
| 42 | }; | ||
| 43 | |||
| 44 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 45 | module_param(nowayout, int, 0); | ||
| 46 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | ||
| 47 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
| 48 | |||
| 49 | static int twl4030_wdt_write(unsigned char val) | ||
| 50 | { | ||
| 51 | return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val, | ||
| 52 | TWL4030_WATCHDOG_CFG_REG_OFFS); | ||
| 53 | } | ||
| 54 | |||
| 55 | static int twl4030_wdt_enable(struct twl4030_wdt *wdt) | ||
| 56 | { | ||
| 57 | return twl4030_wdt_write(wdt->timer_margin + 1); | ||
| 58 | } | ||
| 59 | |||
| 60 | static int twl4030_wdt_disable(struct twl4030_wdt *wdt) | ||
| 61 | { | ||
| 62 | return twl4030_wdt_write(0); | ||
| 63 | } | ||
| 64 | |||
| 65 | static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout) | ||
| 66 | { | ||
| 67 | if (timeout < 0 || timeout > 30) { | ||
| 68 | dev_warn(wdt->miscdev.parent, | ||
| 69 | "Timeout can only be in the range [0-30] seconds"); | ||
| 70 | return -EINVAL; | ||
| 71 | } | ||
| 72 | wdt->timer_margin = timeout; | ||
| 73 | return twl4030_wdt_enable(wdt); | ||
| 74 | } | ||
| 75 | |||
| 76 | static ssize_t twl4030_wdt_write_fop(struct file *file, | ||
| 77 | const char __user *data, size_t len, loff_t *ppos) | ||
| 78 | { | ||
| 79 | struct twl4030_wdt *wdt = file->private_data; | ||
| 80 | |||
| 81 | if (len) | ||
| 82 | twl4030_wdt_enable(wdt); | ||
| 83 | |||
| 84 | return len; | ||
| 85 | } | ||
| 86 | |||
| 87 | static long twl4030_wdt_ioctl(struct file *file, | ||
| 88 | unsigned int cmd, unsigned long arg) | ||
| 89 | { | ||
| 90 | void __user *argp = (void __user *)arg; | ||
| 91 | int __user *p = argp; | ||
| 92 | int new_margin; | ||
| 93 | struct twl4030_wdt *wdt = file->private_data; | ||
| 94 | |||
| 95 | static const struct watchdog_info twl4030_wd_ident = { | ||
| 96 | .identity = "TWL4030 Watchdog", | ||
| 97 | .options = WDIOF_SETTIMEOUT, | ||
| 98 | .firmware_version = 0, | ||
| 99 | }; | ||
| 100 | |||
| 101 | switch (cmd) { | ||
| 102 | case WDIOC_GETSUPPORT: | ||
| 103 | return copy_to_user(argp, &twl4030_wd_ident, | ||
| 104 | sizeof(twl4030_wd_ident)) ? -EFAULT : 0; | ||
| 105 | |||
| 106 | case WDIOC_GETSTATUS: | ||
| 107 | case WDIOC_GETBOOTSTATUS: | ||
| 108 | return put_user(0, p); | ||
| 109 | |||
| 110 | case WDIOC_KEEPALIVE: | ||
| 111 | twl4030_wdt_enable(wdt); | ||
| 112 | break; | ||
| 113 | |||
| 114 | case WDIOC_SETTIMEOUT: | ||
| 115 | if (get_user(new_margin, p)) | ||
| 116 | return -EFAULT; | ||
| 117 | if (twl4030_wdt_set_timeout(wdt, new_margin)) | ||
| 118 | return -EINVAL; | ||
| 119 | return put_user(wdt->timer_margin, p); | ||
| 120 | |||
| 121 | case WDIOC_GETTIMEOUT: | ||
| 122 | return put_user(wdt->timer_margin, p); | ||
| 123 | |||
| 124 | default: | ||
| 125 | return -ENOTTY; | ||
| 126 | } | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int twl4030_wdt_open(struct inode *inode, struct file *file) | ||
| 132 | { | ||
| 133 | struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev); | ||
| 134 | |||
| 135 | /* /dev/watchdog can only be opened once */ | ||
| 136 | if (test_and_set_bit(0, &wdt->state)) | ||
| 137 | return -EBUSY; | ||
| 138 | |||
| 139 | wdt->state |= TWL4030_WDT_STATE_ACTIVE; | ||
| 140 | file->private_data = (void *) wdt; | ||
| 141 | |||
| 142 | twl4030_wdt_enable(wdt); | ||
| 143 | return nonseekable_open(inode, file); | ||
| 144 | } | ||
| 145 | |||
| 146 | static int twl4030_wdt_release(struct inode *inode, struct file *file) | ||
| 147 | { | ||
| 148 | struct twl4030_wdt *wdt = file->private_data; | ||
| 149 | if (nowayout) { | ||
| 150 | dev_alert(wdt->miscdev.parent, | ||
| 151 | "Unexpected close, watchdog still running!\n"); | ||
| 152 | twl4030_wdt_enable(wdt); | ||
| 153 | } else { | ||
| 154 | if (twl4030_wdt_disable(wdt)) | ||
| 155 | return -EFAULT; | ||
| 156 | wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; | ||
| 157 | } | ||
| 158 | |||
| 159 | clear_bit(0, &wdt->state); | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static const struct file_operations twl4030_wdt_fops = { | ||
| 164 | .owner = THIS_MODULE, | ||
| 165 | .llseek = no_llseek, | ||
| 166 | .open = twl4030_wdt_open, | ||
| 167 | .release = twl4030_wdt_release, | ||
| 168 | .unlocked_ioctl = twl4030_wdt_ioctl, | ||
| 169 | .write = twl4030_wdt_write_fop, | ||
| 170 | }; | ||
| 171 | |||
| 172 | static int __devinit twl4030_wdt_probe(struct platform_device *pdev) | ||
| 173 | { | ||
| 174 | int ret = 0; | ||
| 175 | struct twl4030_wdt *wdt; | ||
| 176 | |||
| 177 | wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL); | ||
| 178 | if (!wdt) | ||
| 179 | return -ENOMEM; | ||
| 180 | |||
| 181 | wdt->state = 0; | ||
| 182 | wdt->timer_margin = 30; | ||
| 183 | wdt->miscdev.parent = &pdev->dev; | ||
| 184 | wdt->miscdev.fops = &twl4030_wdt_fops; | ||
| 185 | wdt->miscdev.minor = WATCHDOG_MINOR; | ||
| 186 | wdt->miscdev.name = "watchdog"; | ||
| 187 | |||
| 188 | platform_set_drvdata(pdev, wdt); | ||
| 189 | |||
| 190 | twl4030_wdt_dev = pdev; | ||
| 191 | |||
| 192 | ret = misc_register(&wdt->miscdev); | ||
| 193 | if (ret) { | ||
| 194 | dev_err(wdt->miscdev.parent, | ||
| 195 | "Failed to register misc device\n"); | ||
| 196 | platform_set_drvdata(pdev, NULL); | ||
| 197 | kfree(wdt); | ||
| 198 | twl4030_wdt_dev = NULL; | ||
| 199 | return ret; | ||
| 200 | } | ||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | static int __devexit twl4030_wdt_remove(struct platform_device *pdev) | ||
| 205 | { | ||
| 206 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | ||
| 207 | |||
| 208 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | ||
| 209 | if (twl4030_wdt_disable(wdt)) | ||
| 210 | return -EFAULT; | ||
| 211 | |||
| 212 | wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; | ||
| 213 | misc_deregister(&wdt->miscdev); | ||
| 214 | |||
| 215 | platform_set_drvdata(pdev, NULL); | ||
| 216 | kfree(wdt); | ||
| 217 | twl4030_wdt_dev = NULL; | ||
| 218 | |||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | |||
| 222 | #ifdef CONFIG_PM | ||
| 223 | static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) | ||
| 224 | { | ||
| 225 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | ||
| 226 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | ||
| 227 | return twl4030_wdt_disable(wdt); | ||
| 228 | |||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | static int twl4030_wdt_resume(struct platform_device *pdev) | ||
| 233 | { | ||
| 234 | struct twl4030_wdt *wdt = platform_get_drvdata(pdev); | ||
| 235 | if (wdt->state & TWL4030_WDT_STATE_ACTIVE) | ||
| 236 | return twl4030_wdt_enable(wdt); | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | #else | ||
| 241 | #define twl4030_wdt_suspend NULL | ||
| 242 | #define twl4030_wdt_resume NULL | ||
| 243 | #endif | ||
| 244 | |||
| 245 | static struct platform_driver twl4030_wdt_driver = { | ||
| 246 | .probe = twl4030_wdt_probe, | ||
| 247 | .remove = __devexit_p(twl4030_wdt_remove), | ||
| 248 | .suspend = twl4030_wdt_suspend, | ||
| 249 | .resume = twl4030_wdt_resume, | ||
| 250 | .driver = { | ||
| 251 | .owner = THIS_MODULE, | ||
| 252 | .name = "twl4030_wdt", | ||
| 253 | }, | ||
| 254 | }; | ||
| 255 | |||
| 256 | static int __devinit twl4030_wdt_init(void) | ||
| 257 | { | ||
| 258 | return platform_driver_register(&twl4030_wdt_driver); | ||
| 259 | } | ||
| 260 | module_init(twl4030_wdt_init); | ||
| 261 | |||
| 262 | static void __devexit twl4030_wdt_exit(void) | ||
| 263 | { | ||
| 264 | platform_driver_unregister(&twl4030_wdt_driver); | ||
| 265 | } | ||
| 266 | module_exit(twl4030_wdt_exit); | ||
| 267 | |||
| 268 | MODULE_AUTHOR("Nokia Corporation"); | ||
| 269 | MODULE_LICENSE("GPL"); | ||
| 270 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
| 271 | MODULE_ALIAS("platform:twl4030_wdt"); | ||
| 272 | |||
diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index c45839a4a34d..7a1bdc7c95a9 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | * Industrial Computer Source PCI-WDT500/501 driver | 2 | * Industrial Computer Source PCI-WDT500/501 driver |
| 3 | * | 3 | * |
| 4 | * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, | 4 | * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, |
| 5 | * All Rights Reserved. | 5 | * All Rights Reserved. |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
| @@ -99,14 +99,16 @@ MODULE_PARM_DESC(nowayout, | |||
| 99 | "Watchdog cannot be stopped once started (default=" | 99 | "Watchdog cannot be stopped once started (default=" |
| 100 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 100 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
| 101 | 101 | ||
| 102 | #ifdef CONFIG_WDT_501_PCI | ||
| 103 | /* Support for the Fan Tachometer on the PCI-WDT501 */ | 102 | /* Support for the Fan Tachometer on the PCI-WDT501 */ |
| 104 | static int tachometer; | 103 | static int tachometer; |
| 105 | |||
| 106 | module_param(tachometer, int, 0); | 104 | module_param(tachometer, int, 0); |
| 107 | MODULE_PARM_DESC(tachometer, | 105 | MODULE_PARM_DESC(tachometer, |
| 108 | "PCI-WDT501 Fan Tachometer support (0=disable, default=0)"); | 106 | "PCI-WDT501 Fan Tachometer support (0=disable, default=0)"); |
| 109 | #endif /* CONFIG_WDT_501_PCI */ | 107 | |
| 108 | static int type = 500; | ||
| 109 | module_param(type, int, 0); | ||
| 110 | MODULE_PARM_DESC(type, | ||
| 111 | "PCI-WDT501 Card type (500 or 501 , default=500)"); | ||
| 110 | 112 | ||
| 111 | /* | 113 | /* |
| 112 | * Programming support | 114 | * Programming support |
| @@ -266,22 +268,21 @@ static int wdtpci_get_status(int *status) | |||
| 266 | *status |= WDIOF_EXTERN1; | 268 | *status |= WDIOF_EXTERN1; |
| 267 | if (new_status & WDC_SR_ISII1) | 269 | if (new_status & WDC_SR_ISII1) |
| 268 | *status |= WDIOF_EXTERN2; | 270 | *status |= WDIOF_EXTERN2; |
| 269 | #ifdef CONFIG_WDT_501_PCI | 271 | if (type == 501) { |
| 270 | if (!(new_status & WDC_SR_TGOOD)) | 272 | if (!(new_status & WDC_SR_TGOOD)) |
| 271 | *status |= WDIOF_OVERHEAT; | 273 | *status |= WDIOF_OVERHEAT; |
| 272 | if (!(new_status & WDC_SR_PSUOVER)) | 274 | if (!(new_status & WDC_SR_PSUOVER)) |
| 273 | *status |= WDIOF_POWEROVER; | 275 | *status |= WDIOF_POWEROVER; |
| 274 | if (!(new_status & WDC_SR_PSUUNDR)) | 276 | if (!(new_status & WDC_SR_PSUUNDR)) |
| 275 | *status |= WDIOF_POWERUNDER; | 277 | *status |= WDIOF_POWERUNDER; |
| 276 | if (tachometer) { | 278 | if (tachometer) { |
| 277 | if (!(new_status & WDC_SR_FANGOOD)) | 279 | if (!(new_status & WDC_SR_FANGOOD)) |
| 278 | *status |= WDIOF_FANFAULT; | 280 | *status |= WDIOF_FANFAULT; |
| 281 | } | ||
| 279 | } | 282 | } |
| 280 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 281 | return 0; | 283 | return 0; |
| 282 | } | 284 | } |
| 283 | 285 | ||
| 284 | #ifdef CONFIG_WDT_501_PCI | ||
| 285 | /** | 286 | /** |
| 286 | * wdtpci_get_temperature: | 287 | * wdtpci_get_temperature: |
| 287 | * | 288 | * |
| @@ -300,7 +301,6 @@ static int wdtpci_get_temperature(int *temperature) | |||
| 300 | *temperature = (c * 11 / 15) + 7; | 301 | *temperature = (c * 11 / 15) + 7; |
| 301 | return 0; | 302 | return 0; |
| 302 | } | 303 | } |
| 303 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 304 | 304 | ||
| 305 | /** | 305 | /** |
| 306 | * wdtpci_interrupt: | 306 | * wdtpci_interrupt: |
| @@ -327,22 +327,22 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) | |||
| 327 | 327 | ||
| 328 | printk(KERN_CRIT PFX "status %d\n", status); | 328 | printk(KERN_CRIT PFX "status %d\n", status); |
| 329 | 329 | ||
| 330 | #ifdef CONFIG_WDT_501_PCI | 330 | if (type == 501) { |
| 331 | if (!(status & WDC_SR_TGOOD)) { | 331 | if (!(status & WDC_SR_TGOOD)) { |
| 332 | u8 alarm = inb(WDT_RT); | 332 | printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", |
| 333 | printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", alarm); | 333 | inb(WDT_RT)); |
| 334 | udelay(8); | 334 | udelay(8); |
| 335 | } | 335 | } |
| 336 | if (!(status & WDC_SR_PSUOVER)) | 336 | if (!(status & WDC_SR_PSUOVER)) |
| 337 | printk(KERN_CRIT PFX "PSU over voltage.\n"); | 337 | printk(KERN_CRIT PFX "PSU over voltage.\n"); |
| 338 | if (!(status & WDC_SR_PSUUNDR)) | 338 | if (!(status & WDC_SR_PSUUNDR)) |
| 339 | printk(KERN_CRIT PFX "PSU under voltage.\n"); | 339 | printk(KERN_CRIT PFX "PSU under voltage.\n"); |
| 340 | if (tachometer) { | 340 | if (tachometer) { |
| 341 | if (!(status & WDC_SR_FANGOOD)) | 341 | if (!(status & WDC_SR_FANGOOD)) |
| 342 | printk(KERN_CRIT PFX "Possible fan fault.\n"); | 342 | printk(KERN_CRIT PFX "Possible fan fault.\n"); |
| 343 | } | ||
| 343 | } | 344 | } |
| 344 | #endif /* CONFIG_WDT_501_PCI */ | 345 | if (!(status & WDC_SR_WCCR)) { |
| 345 | if (!(status&WDC_SR_WCCR)) { | ||
| 346 | #ifdef SOFTWARE_REBOOT | 346 | #ifdef SOFTWARE_REBOOT |
| 347 | #ifdef ONLY_TESTING | 347 | #ifdef ONLY_TESTING |
| 348 | printk(KERN_CRIT PFX "Would Reboot.\n"); | 348 | printk(KERN_CRIT PFX "Would Reboot.\n"); |
| @@ -371,12 +371,13 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) | |||
| 371 | */ | 371 | */ |
| 372 | 372 | ||
| 373 | static ssize_t wdtpci_write(struct file *file, const char __user *buf, | 373 | static ssize_t wdtpci_write(struct file *file, const char __user *buf, |
| 374 | size_t count, loff_t *ppos) | 374 | size_t count, loff_t *ppos) |
| 375 | { | 375 | { |
| 376 | if (count) { | 376 | if (count) { |
| 377 | if (!nowayout) { | 377 | if (!nowayout) { |
| 378 | size_t i; | 378 | size_t i; |
| 379 | 379 | ||
| 380 | /* In case it was set long ago */ | ||
| 380 | expect_close = 0; | 381 | expect_close = 0; |
| 381 | 382 | ||
| 382 | for (i = 0; i != count; i++) { | 383 | for (i = 0; i != count; i++) { |
| @@ -406,10 +407,10 @@ static ssize_t wdtpci_write(struct file *file, const char __user *buf, | |||
| 406 | static long wdtpci_ioctl(struct file *file, unsigned int cmd, | 407 | static long wdtpci_ioctl(struct file *file, unsigned int cmd, |
| 407 | unsigned long arg) | 408 | unsigned long arg) |
| 408 | { | 409 | { |
| 409 | int new_heartbeat; | ||
| 410 | int status; | ||
| 411 | void __user *argp = (void __user *)arg; | 410 | void __user *argp = (void __user *)arg; |
| 412 | int __user *p = argp; | 411 | int __user *p = argp; |
| 412 | int new_heartbeat; | ||
| 413 | int status; | ||
| 413 | 414 | ||
| 414 | static struct watchdog_info ident = { | 415 | static struct watchdog_info ident = { |
| 415 | .options = WDIOF_SETTIMEOUT| | 416 | .options = WDIOF_SETTIMEOUT| |
| @@ -421,11 +422,12 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, | |||
| 421 | 422 | ||
| 422 | /* Add options according to the card we have */ | 423 | /* Add options according to the card we have */ |
| 423 | ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); | 424 | ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); |
| 424 | #ifdef CONFIG_WDT_501_PCI | 425 | if (type == 501) { |
| 425 | ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); | 426 | ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER| |
| 426 | if (tachometer) | 427 | WDIOF_POWEROVER); |
| 427 | ident.options |= WDIOF_FANFAULT; | 428 | if (tachometer) |
| 428 | #endif /* CONFIG_WDT_501_PCI */ | 429 | ident.options |= WDIOF_FANFAULT; |
| 430 | } | ||
| 429 | 431 | ||
| 430 | switch (cmd) { | 432 | switch (cmd) { |
| 431 | case WDIOC_GETSUPPORT: | 433 | case WDIOC_GETSUPPORT: |
| @@ -503,7 +505,6 @@ static int wdtpci_release(struct inode *inode, struct file *file) | |||
| 503 | return 0; | 505 | return 0; |
| 504 | } | 506 | } |
| 505 | 507 | ||
| 506 | #ifdef CONFIG_WDT_501_PCI | ||
| 507 | /** | 508 | /** |
| 508 | * wdtpci_temp_read: | 509 | * wdtpci_temp_read: |
| 509 | * @file: file handle to the watchdog board | 510 | * @file: file handle to the watchdog board |
| @@ -554,7 +555,6 @@ static int wdtpci_temp_release(struct inode *inode, struct file *file) | |||
| 554 | { | 555 | { |
| 555 | return 0; | 556 | return 0; |
| 556 | } | 557 | } |
| 557 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 558 | 558 | ||
| 559 | /** | 559 | /** |
| 560 | * notify_sys: | 560 | * notify_sys: |
| @@ -596,7 +596,6 @@ static struct miscdevice wdtpci_miscdev = { | |||
| 596 | .fops = &wdtpci_fops, | 596 | .fops = &wdtpci_fops, |
| 597 | }; | 597 | }; |
| 598 | 598 | ||
| 599 | #ifdef CONFIG_WDT_501_PCI | ||
| 600 | static const struct file_operations wdtpci_temp_fops = { | 599 | static const struct file_operations wdtpci_temp_fops = { |
| 601 | .owner = THIS_MODULE, | 600 | .owner = THIS_MODULE, |
| 602 | .llseek = no_llseek, | 601 | .llseek = no_llseek, |
| @@ -610,7 +609,6 @@ static struct miscdevice temp_miscdev = { | |||
| 610 | .name = "temperature", | 609 | .name = "temperature", |
| 611 | .fops = &wdtpci_temp_fops, | 610 | .fops = &wdtpci_temp_fops, |
| 612 | }; | 611 | }; |
| 613 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 614 | 612 | ||
| 615 | /* | 613 | /* |
| 616 | * The WDT card needs to learn about soft shutdowns in order to | 614 | * The WDT card needs to learn about soft shutdowns in order to |
| @@ -633,6 +631,11 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, | |||
| 633 | return -ENODEV; | 631 | return -ENODEV; |
| 634 | } | 632 | } |
| 635 | 633 | ||
| 634 | if (type != 500 && type != 501) { | ||
| 635 | printk(KERN_ERR PFX "unknown card type '%d'.\n", type); | ||
| 636 | return -ENODEV; | ||
| 637 | } | ||
| 638 | |||
| 636 | if (pci_enable_device(dev)) { | 639 | if (pci_enable_device(dev)) { |
| 637 | printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); | 640 | printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); |
| 638 | return -ENODEV; | 641 | return -ENODEV; |
| @@ -678,15 +681,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, | |||
| 678 | goto out_irq; | 681 | goto out_irq; |
| 679 | } | 682 | } |
| 680 | 683 | ||
| 681 | #ifdef CONFIG_WDT_501_PCI | 684 | if (type == 501) { |
| 682 | ret = misc_register(&temp_miscdev); | 685 | ret = misc_register(&temp_miscdev); |
| 683 | if (ret) { | 686 | if (ret) { |
| 684 | printk(KERN_ERR PFX | 687 | printk(KERN_ERR PFX |
| 685 | "cannot register miscdev on minor=%d (err=%d)\n", | 688 | "cannot register miscdev on minor=%d (err=%d)\n", |
| 686 | TEMP_MINOR, ret); | 689 | TEMP_MINOR, ret); |
| 687 | goto out_rbt; | 690 | goto out_rbt; |
| 691 | } | ||
| 688 | } | 692 | } |
| 689 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 690 | 693 | ||
| 691 | ret = misc_register(&wdtpci_miscdev); | 694 | ret = misc_register(&wdtpci_miscdev); |
| 692 | if (ret) { | 695 | if (ret) { |
| @@ -698,20 +701,18 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, | |||
| 698 | 701 | ||
| 699 | printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", | 702 | printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", |
| 700 | heartbeat, nowayout); | 703 | heartbeat, nowayout); |
| 701 | #ifdef CONFIG_WDT_501_PCI | 704 | if (type == 501) |
| 702 | printk(KERN_INFO "wdt: Fan Tachometer is %s\n", | 705 | printk(KERN_INFO "wdt: Fan Tachometer is %s\n", |
| 703 | (tachometer ? "Enabled" : "Disabled")); | 706 | (tachometer ? "Enabled" : "Disabled")); |
| 704 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 705 | 707 | ||
| 706 | ret = 0; | 708 | ret = 0; |
| 707 | out: | 709 | out: |
| 708 | return ret; | 710 | return ret; |
| 709 | 711 | ||
| 710 | out_misc: | 712 | out_misc: |
| 711 | #ifdef CONFIG_WDT_501_PCI | 713 | if (type == 501) |
| 712 | misc_deregister(&temp_miscdev); | 714 | misc_deregister(&temp_miscdev); |
| 713 | out_rbt: | 715 | out_rbt: |
| 714 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 715 | unregister_reboot_notifier(&wdtpci_notifier); | 716 | unregister_reboot_notifier(&wdtpci_notifier); |
| 716 | out_irq: | 717 | out_irq: |
| 717 | free_irq(irq, &wdtpci_miscdev); | 718 | free_irq(irq, &wdtpci_miscdev); |
| @@ -728,9 +729,8 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev) | |||
| 728 | /* here we assume only one device will ever have | 729 | /* here we assume only one device will ever have |
| 729 | * been picked up and registered by probe function */ | 730 | * been picked up and registered by probe function */ |
| 730 | misc_deregister(&wdtpci_miscdev); | 731 | misc_deregister(&wdtpci_miscdev); |
| 731 | #ifdef CONFIG_WDT_501_PCI | 732 | if (type == 501) |
| 732 | misc_deregister(&temp_miscdev); | 733 | misc_deregister(&temp_miscdev); |
| 733 | #endif /* CONFIG_WDT_501_PCI */ | ||
| 734 | unregister_reboot_notifier(&wdtpci_notifier); | 734 | unregister_reboot_notifier(&wdtpci_notifier); |
| 735 | free_irq(irq, &wdtpci_miscdev); | 735 | free_irq(irq, &wdtpci_miscdev); |
| 736 | release_region(io, 16); | 736 | release_region(io, 16); |
