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); |