diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-30 02:28:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-30 02:28:47 -0400 |
commit | 2313bcdcc9ff1e42f51b200dc65ddaae14c347f4 (patch) | |
tree | fdb0e3f953841cdd661e36c40a965c9e044d82f5 /drivers/watchdog | |
parent | 59ed2bb27492f74ab898ee1e26c69dfd78444640 (diff) | |
parent | 3f43f68e29f1dcb853d70280c7412fc0ef9a0da6 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-watchdog
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-watchdog: (25 commits)
watchdog: WatchDog Timer Driver Core - Add minimum and max timeout
watchdog: WatchDog Timer Driver Core - Add ioctl call
watchdog: WatchDog Timer Driver Core - Add nowayout feature
watchdog: WatchDog Timer Driver Core - Add Magic Close feature
watchdog: WatchDog Timer Driver Core - Add WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT ioctl
watchdog: WatchDog Timer Driver Core - Add WDIOC_SETOPTIONS ioctl
watchdog: WatchDog Timer Driver Core - Add WDIOC_KEEPALIVE ioctl
watchdog: WatchDog Timer Driver Core - Add basic ioctl functionality
watchdog: WatchDog Timer Driver Core - Add basic framework
watchdog: hpwdt: add next gen HP servers
watchdog: it8712f_wdt.c: improve includes
watchdog: at91sam9/wdt: move register header to drivers
watchdog: Add Xilinx watchdog timer driver
watchdog: remove empty pm-functions
watchdog: sp805: Flush posted writes in enable/disable.
watchdog: sp805: Don't write 0 to the load value register.
watchdog: imx2_wdt: add device tree probe support
watchdog: s3c2410: Add support for device tree based probe
watchdog: mpcore_wdt: Add suspend/resume support.
watchdog: mtx1-wdt: use dev_{err,info} instead of printk()
...
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 36 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 8 | ||||
-rw-r--r-- | drivers/watchdog/at91sam9_wdt.c | 21 | ||||
-rw-r--r-- | drivers/watchdog/at91sam9_wdt.h | 37 | ||||
-rw-r--r-- | drivers/watchdog/dw_wdt.c | 376 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 104 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 412 | ||||
-rw-r--r-- | drivers/watchdog/imx2_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/it8712f_wdt.c | 63 | ||||
-rw-r--r-- | drivers/watchdog/it87_wdt.c | 168 | ||||
-rw-r--r-- | drivers/watchdog/mpcore_wdt.c | 23 | ||||
-rw-r--r-- | drivers/watchdog/mtx-1_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/of_xilinx_wdt.c | 433 | ||||
-rw-r--r-- | drivers/watchdog/pc87413_wdt.c | 96 | ||||
-rw-r--r-- | drivers/watchdog/s3c2410_wdt.c | 10 | ||||
-rw-r--r-- | drivers/watchdog/sch311x_wdt.c | 5 | ||||
-rw-r--r-- | drivers/watchdog/sp805_wdt.c | 5 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 111 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 395 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.h | 33 |
20 files changed, 1875 insertions, 471 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 21d816e9dfa5..f441726ddf2b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -28,6 +28,17 @@ menuconfig WATCHDOG | |||
28 | 28 | ||
29 | if WATCHDOG | 29 | if WATCHDOG |
30 | 30 | ||
31 | config WATCHDOG_CORE | ||
32 | bool "WatchDog Timer Driver Core" | ||
33 | ---help--- | ||
34 | Say Y here if you want to use the new watchdog timer driver core. | ||
35 | This driver provides a framework for all watchdog timer drivers | ||
36 | and gives them the /dev/watchdog interface (and later also the | ||
37 | sysfs interface). | ||
38 | |||
39 | To compile this driver as a module, choose M here: the module will | ||
40 | be called watchdog. | ||
41 | |||
31 | config WATCHDOG_NOWAYOUT | 42 | config WATCHDOG_NOWAYOUT |
32 | bool "Disable watchdog shutdown on close" | 43 | bool "Disable watchdog shutdown on close" |
33 | help | 44 | help |
@@ -186,6 +197,15 @@ config SA1100_WATCHDOG | |||
186 | To compile this driver as a module, choose M here: the | 197 | To compile this driver as a module, choose M here: the |
187 | module will be called sa1100_wdt. | 198 | module will be called sa1100_wdt. |
188 | 199 | ||
200 | config DW_WATCHDOG | ||
201 | tristate "Synopsys DesignWare watchdog" | ||
202 | depends on ARM && HAVE_CLK | ||
203 | help | ||
204 | Say Y here if to include support for the Synopsys DesignWare | ||
205 | watchdog timer found in many ARM chips. | ||
206 | To compile this driver as a module, choose M here: the | ||
207 | module will be called dw_wdt. | ||
208 | |||
189 | config MPCORE_WATCHDOG | 209 | config MPCORE_WATCHDOG |
190 | tristate "MPcore watchdog" | 210 | tristate "MPcore watchdog" |
191 | depends on HAVE_ARM_TWD | 211 | depends on HAVE_ARM_TWD |
@@ -321,7 +341,7 @@ config MAX63XX_WATCHDOG | |||
321 | 341 | ||
322 | config IMX2_WDT | 342 | config IMX2_WDT |
323 | tristate "IMX2+ Watchdog" | 343 | tristate "IMX2+ Watchdog" |
324 | depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5 | 344 | depends on IMX_HAVE_PLATFORM_IMX2_WDT |
325 | help | 345 | help |
326 | This is the driver for the hardware watchdog | 346 | This is the driver for the hardware watchdog |
327 | on the Freescale IMX2 and later processors. | 347 | on the Freescale IMX2 and later processors. |
@@ -879,6 +899,20 @@ config M54xx_WATCHDOG | |||
879 | To compile this driver as a module, choose M here: the | 899 | To compile this driver as a module, choose M here: the |
880 | module will be called m54xx_wdt. | 900 | module will be called m54xx_wdt. |
881 | 901 | ||
902 | # MicroBlaze Architecture | ||
903 | |||
904 | config XILINX_WATCHDOG | ||
905 | tristate "Xilinx Watchdog timer" | ||
906 | depends on MICROBLAZE | ||
907 | ---help--- | ||
908 | Watchdog driver for the xps_timebase_wdt ip core. | ||
909 | |||
910 | IMPORTANT: The xps_timebase_wdt parent must have the property | ||
911 | "clock-frequency" at device tree. | ||
912 | |||
913 | To compile this driver as a module, choose M here: the | ||
914 | module will be called of_xilinx_wdt. | ||
915 | |||
882 | # MIPS Architecture | 916 | # MIPS Architecture |
883 | 917 | ||
884 | config ATH79_WDT | 918 | config ATH79_WDT |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ed26f7094e47..55bd5740e910 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -2,6 +2,10 @@ | |||
2 | # Makefile for the WatchDog device drivers. | 2 | # Makefile for the WatchDog device drivers. |
3 | # | 3 | # |
4 | 4 | ||
5 | # The WatchDog Timer Driver Core. | ||
6 | watchdog-objs += watchdog_core.o watchdog_dev.o | ||
7 | obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o | ||
8 | |||
5 | # Only one watchdog can succeed. We probe the ISA/PCI/USB based | 9 | # Only one watchdog can succeed. We probe the ISA/PCI/USB based |
6 | # watchdog-cards first, then the architecture specific watchdog | 10 | # watchdog-cards first, then the architecture specific watchdog |
7 | # drivers and then the architecture independent "softdog" driver. | 11 | # drivers and then the architecture independent "softdog" driver. |
@@ -37,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o | |||
37 | obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o | 41 | obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o |
38 | obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o | 42 | obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o |
39 | obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o | 43 | obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o |
44 | obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o | ||
40 | obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o | 45 | obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o |
41 | obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o | 46 | obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o |
42 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o | 47 | obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o |
@@ -109,6 +114,9 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o | |||
109 | # M68K Architecture | 114 | # M68K Architecture |
110 | obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o | 115 | obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o |
111 | 116 | ||
117 | # MicroBlaze Architecture | ||
118 | obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o | ||
119 | |||
112 | # MIPS Architecture | 120 | # MIPS Architecture |
113 | obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o | 121 | obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o |
114 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o | 122 | obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o |
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index eac26021e8da..87445b2d72a7 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c | |||
@@ -31,7 +31,7 @@ | |||
31 | #include <linux/bitops.h> | 31 | #include <linux/bitops.h> |
32 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
33 | 33 | ||
34 | #include <mach/at91_wdt.h> | 34 | #include "at91sam9_wdt.h" |
35 | 35 | ||
36 | #define DRV_NAME "AT91SAM9 Watchdog" | 36 | #define DRV_NAME "AT91SAM9 Watchdog" |
37 | 37 | ||
@@ -284,27 +284,8 @@ static int __exit at91wdt_remove(struct platform_device *pdev) | |||
284 | return res; | 284 | return res; |
285 | } | 285 | } |
286 | 286 | ||
287 | #ifdef CONFIG_PM | ||
288 | |||
289 | static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) | ||
290 | { | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int at91wdt_resume(struct platform_device *pdev) | ||
295 | { | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | #else | ||
300 | #define at91wdt_suspend NULL | ||
301 | #define at91wdt_resume NULL | ||
302 | #endif | ||
303 | |||
304 | static struct platform_driver at91wdt_driver = { | 287 | static struct platform_driver at91wdt_driver = { |
305 | .remove = __exit_p(at91wdt_remove), | 288 | .remove = __exit_p(at91wdt_remove), |
306 | .suspend = at91wdt_suspend, | ||
307 | .resume = at91wdt_resume, | ||
308 | .driver = { | 289 | .driver = { |
309 | .name = "at91_wdt", | 290 | .name = "at91_wdt", |
310 | .owner = THIS_MODULE, | 291 | .owner = THIS_MODULE, |
diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h new file mode 100644 index 000000000000..757f9cab5c82 --- /dev/null +++ b/drivers/watchdog/at91sam9_wdt.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * drivers/watchdog/at91sam9_wdt.h | ||
3 | * | ||
4 | * Copyright (C) 2007 Andrew Victor | ||
5 | * Copyright (C) 2007 Atmel Corporation. | ||
6 | * | ||
7 | * Watchdog Timer (WDT) - System peripherals regsters. | ||
8 | * Based on AT91SAM9261 datasheet revision D. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #ifndef AT91_WDT_H | ||
17 | #define AT91_WDT_H | ||
18 | |||
19 | #define AT91_WDT_CR (AT91_WDT + 0x00) /* Watchdog Control Register */ | ||
20 | #define AT91_WDT_WDRSTT (1 << 0) /* Restart */ | ||
21 | #define AT91_WDT_KEY (0xa5 << 24) /* KEY Password */ | ||
22 | |||
23 | #define AT91_WDT_MR (AT91_WDT + 0x04) /* Watchdog Mode Register */ | ||
24 | #define AT91_WDT_WDV (0xfff << 0) /* Counter Value */ | ||
25 | #define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */ | ||
26 | #define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */ | ||
27 | #define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */ | ||
28 | #define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */ | ||
29 | #define AT91_WDT_WDD (0xfff << 16) /* Delta Value */ | ||
30 | #define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */ | ||
31 | #define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */ | ||
32 | |||
33 | #define AT91_WDT_SR (AT91_WDT + 0x08) /* Watchdog Status Register */ | ||
34 | #define AT91_WDT_WDUNF (1 << 0) /* Watchdog Underflow */ | ||
35 | #define AT91_WDT_WDERR (1 << 1) /* Watchdog Error */ | ||
36 | |||
37 | #endif | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c new file mode 100644 index 000000000000..f10f8c0abba4 --- /dev/null +++ b/drivers/watchdog/dw_wdt.c | |||
@@ -0,0 +1,376 @@ | |||
1 | /* | ||
2 | * Copyright 2010-2011 Picochip Ltd., Jamie Iles | ||
3 | * http://www.picochip.com | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version | ||
8 | * 2 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This file implements a driver for the Synopsys DesignWare watchdog device | ||
11 | * in the many ARM subsystems. The watchdog has 16 different timeout periods | ||
12 | * and these are a function of the input clock frequency. | ||
13 | * | ||
14 | * The DesignWare watchdog cannot be stopped once it has been started so we | ||
15 | * use a software timer to implement a ping that will keep the watchdog alive. | ||
16 | * If we receive an expected close for the watchdog then we keep the timer | ||
17 | * running, otherwise the timer is stopped and the watchdog will expire. | ||
18 | */ | ||
19 | #define pr_fmt(fmt) "dw_wdt: " fmt | ||
20 | |||
21 | #include <linux/bitops.h> | ||
22 | #include <linux/clk.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/miscdevice.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <linux/pm.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/spinlock.h> | ||
34 | #include <linux/timer.h> | ||
35 | #include <linux/uaccess.h> | ||
36 | #include <linux/watchdog.h> | ||
37 | |||
38 | #define WDOG_CONTROL_REG_OFFSET 0x00 | ||
39 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 | ||
40 | #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 | ||
41 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 | ||
42 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c | ||
43 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 | ||
44 | |||
45 | /* The maximum TOP (timeout period) value that can be set in the watchdog. */ | ||
46 | #define DW_WDT_MAX_TOP 15 | ||
47 | |||
48 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
49 | module_param(nowayout, int, 0); | ||
50 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " | ||
51 | "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
52 | |||
53 | #define WDT_TIMEOUT (HZ / 2) | ||
54 | |||
55 | static struct { | ||
56 | spinlock_t lock; | ||
57 | void __iomem *regs; | ||
58 | struct clk *clk; | ||
59 | unsigned long in_use; | ||
60 | unsigned long next_heartbeat; | ||
61 | struct timer_list timer; | ||
62 | int expect_close; | ||
63 | } dw_wdt; | ||
64 | |||
65 | static inline int dw_wdt_is_enabled(void) | ||
66 | { | ||
67 | return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) & | ||
68 | WDOG_CONTROL_REG_WDT_EN_MASK; | ||
69 | } | ||
70 | |||
71 | static inline int dw_wdt_top_in_seconds(unsigned top) | ||
72 | { | ||
73 | /* | ||
74 | * There are 16 possible timeout values in 0..15 where the number of | ||
75 | * cycles is 2 ^ (16 + i) and the watchdog counts down. | ||
76 | */ | ||
77 | return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk); | ||
78 | } | ||
79 | |||
80 | static int dw_wdt_get_top(void) | ||
81 | { | ||
82 | int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; | ||
83 | |||
84 | return dw_wdt_top_in_seconds(top); | ||
85 | } | ||
86 | |||
87 | static inline void dw_wdt_set_next_heartbeat(void) | ||
88 | { | ||
89 | dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ; | ||
90 | } | ||
91 | |||
92 | static int dw_wdt_set_top(unsigned top_s) | ||
93 | { | ||
94 | int i, top_val = DW_WDT_MAX_TOP; | ||
95 | |||
96 | /* | ||
97 | * Iterate over the timeout values until we find the closest match. We | ||
98 | * always look for >=. | ||
99 | */ | ||
100 | for (i = 0; i <= DW_WDT_MAX_TOP; ++i) | ||
101 | if (dw_wdt_top_in_seconds(i) >= top_s) { | ||
102 | top_val = i; | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | /* Set the new value in the watchdog. */ | ||
107 | writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
108 | |||
109 | dw_wdt_set_next_heartbeat(); | ||
110 | |||
111 | return dw_wdt_top_in_seconds(top_val); | ||
112 | } | ||
113 | |||
114 | static void dw_wdt_keepalive(void) | ||
115 | { | ||
116 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + | ||
117 | WDOG_COUNTER_RESTART_REG_OFFSET); | ||
118 | } | ||
119 | |||
120 | static void dw_wdt_ping(unsigned long data) | ||
121 | { | ||
122 | if (time_before(jiffies, dw_wdt.next_heartbeat) || | ||
123 | (!nowayout && !dw_wdt.in_use)) { | ||
124 | dw_wdt_keepalive(); | ||
125 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
126 | } else | ||
127 | pr_crit("keepalive missed, machine will reset\n"); | ||
128 | } | ||
129 | |||
130 | static int dw_wdt_open(struct inode *inode, struct file *filp) | ||
131 | { | ||
132 | if (test_and_set_bit(0, &dw_wdt.in_use)) | ||
133 | return -EBUSY; | ||
134 | |||
135 | /* Make sure we don't get unloaded. */ | ||
136 | __module_get(THIS_MODULE); | ||
137 | |||
138 | spin_lock(&dw_wdt.lock); | ||
139 | if (!dw_wdt_is_enabled()) { | ||
140 | /* | ||
141 | * The watchdog is not currently enabled. Set the timeout to | ||
142 | * the maximum and then start it. | ||
143 | */ | ||
144 | dw_wdt_set_top(DW_WDT_MAX_TOP); | ||
145 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | ||
146 | dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
147 | } | ||
148 | |||
149 | dw_wdt_set_next_heartbeat(); | ||
150 | |||
151 | spin_unlock(&dw_wdt.lock); | ||
152 | |||
153 | return nonseekable_open(inode, filp); | ||
154 | } | ||
155 | |||
156 | ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len, | ||
157 | loff_t *offset) | ||
158 | { | ||
159 | if (!len) | ||
160 | return 0; | ||
161 | |||
162 | if (!nowayout) { | ||
163 | size_t i; | ||
164 | |||
165 | dw_wdt.expect_close = 0; | ||
166 | |||
167 | for (i = 0; i < len; ++i) { | ||
168 | char c; | ||
169 | |||
170 | if (get_user(c, buf + i)) | ||
171 | return -EFAULT; | ||
172 | |||
173 | if (c == 'V') { | ||
174 | dw_wdt.expect_close = 1; | ||
175 | break; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | dw_wdt_set_next_heartbeat(); | ||
181 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
182 | |||
183 | return len; | ||
184 | } | ||
185 | |||
186 | static u32 dw_wdt_time_left(void) | ||
187 | { | ||
188 | return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) / | ||
189 | clk_get_rate(dw_wdt.clk); | ||
190 | } | ||
191 | |||
192 | static const struct watchdog_info dw_wdt_ident = { | ||
193 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | | ||
194 | WDIOF_MAGICCLOSE, | ||
195 | .identity = "Synopsys DesignWare Watchdog", | ||
196 | }; | ||
197 | |||
198 | static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | ||
199 | { | ||
200 | unsigned long val; | ||
201 | int timeout; | ||
202 | |||
203 | switch (cmd) { | ||
204 | case WDIOC_GETSUPPORT: | ||
205 | return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident, | ||
206 | sizeof(dw_wdt_ident)) ? -EFAULT : 0; | ||
207 | |||
208 | case WDIOC_GETSTATUS: | ||
209 | case WDIOC_GETBOOTSTATUS: | ||
210 | return put_user(0, (int *)arg); | ||
211 | |||
212 | case WDIOC_KEEPALIVE: | ||
213 | dw_wdt_set_next_heartbeat(); | ||
214 | return 0; | ||
215 | |||
216 | case WDIOC_SETTIMEOUT: | ||
217 | if (get_user(val, (int __user *)arg)) | ||
218 | return -EFAULT; | ||
219 | timeout = dw_wdt_set_top(val); | ||
220 | return put_user(timeout , (int __user *)arg); | ||
221 | |||
222 | case WDIOC_GETTIMEOUT: | ||
223 | return put_user(dw_wdt_get_top(), (int __user *)arg); | ||
224 | |||
225 | case WDIOC_GETTIMELEFT: | ||
226 | /* Get the time left until expiry. */ | ||
227 | if (get_user(val, (int __user *)arg)) | ||
228 | return -EFAULT; | ||
229 | return put_user(dw_wdt_time_left(), (int __user *)arg); | ||
230 | |||
231 | default: | ||
232 | return -ENOTTY; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | static int dw_wdt_release(struct inode *inode, struct file *filp) | ||
237 | { | ||
238 | clear_bit(0, &dw_wdt.in_use); | ||
239 | |||
240 | if (!dw_wdt.expect_close) { | ||
241 | del_timer(&dw_wdt.timer); | ||
242 | |||
243 | if (!nowayout) | ||
244 | pr_crit("unexpected close, system will reboot soon\n"); | ||
245 | else | ||
246 | pr_crit("watchdog cannot be disabled, system will reboot soon\n"); | ||
247 | } | ||
248 | |||
249 | dw_wdt.expect_close = 0; | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | #ifdef CONFIG_PM | ||
255 | static int dw_wdt_suspend(struct device *dev) | ||
256 | { | ||
257 | clk_disable(dw_wdt.clk); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int dw_wdt_resume(struct device *dev) | ||
263 | { | ||
264 | int err = clk_enable(dw_wdt.clk); | ||
265 | |||
266 | if (err) | ||
267 | return err; | ||
268 | |||
269 | dw_wdt_keepalive(); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static const struct dev_pm_ops dw_wdt_pm_ops = { | ||
275 | .suspend = dw_wdt_suspend, | ||
276 | .resume = dw_wdt_resume, | ||
277 | }; | ||
278 | #endif /* CONFIG_PM */ | ||
279 | |||
280 | static const struct file_operations wdt_fops = { | ||
281 | .owner = THIS_MODULE, | ||
282 | .llseek = no_llseek, | ||
283 | .open = dw_wdt_open, | ||
284 | .write = dw_wdt_write, | ||
285 | .unlocked_ioctl = dw_wdt_ioctl, | ||
286 | .release = dw_wdt_release | ||
287 | }; | ||
288 | |||
289 | static struct miscdevice dw_wdt_miscdev = { | ||
290 | .fops = &wdt_fops, | ||
291 | .name = "watchdog", | ||
292 | .minor = WATCHDOG_MINOR, | ||
293 | }; | ||
294 | |||
295 | static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) | ||
296 | { | ||
297 | int ret; | ||
298 | struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
299 | |||
300 | if (!mem) | ||
301 | return -EINVAL; | ||
302 | |||
303 | if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), | ||
304 | "dw_wdt")) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); | ||
308 | if (!dw_wdt.regs) | ||
309 | return -ENOMEM; | ||
310 | |||
311 | dw_wdt.clk = clk_get(&pdev->dev, NULL); | ||
312 | if (IS_ERR(dw_wdt.clk)) | ||
313 | return PTR_ERR(dw_wdt.clk); | ||
314 | |||
315 | ret = clk_enable(dw_wdt.clk); | ||
316 | if (ret) | ||
317 | goto out_put_clk; | ||
318 | |||
319 | spin_lock_init(&dw_wdt.lock); | ||
320 | |||
321 | ret = misc_register(&dw_wdt_miscdev); | ||
322 | if (ret) | ||
323 | goto out_disable_clk; | ||
324 | |||
325 | dw_wdt_set_next_heartbeat(); | ||
326 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); | ||
327 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | ||
328 | |||
329 | return 0; | ||
330 | |||
331 | out_disable_clk: | ||
332 | clk_disable(dw_wdt.clk); | ||
333 | out_put_clk: | ||
334 | clk_put(dw_wdt.clk); | ||
335 | |||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | static int __devexit dw_wdt_drv_remove(struct platform_device *pdev) | ||
340 | { | ||
341 | misc_deregister(&dw_wdt_miscdev); | ||
342 | |||
343 | clk_disable(dw_wdt.clk); | ||
344 | clk_put(dw_wdt.clk); | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static struct platform_driver dw_wdt_driver = { | ||
350 | .probe = dw_wdt_drv_probe, | ||
351 | .remove = __devexit_p(dw_wdt_drv_remove), | ||
352 | .driver = { | ||
353 | .name = "dw_wdt", | ||
354 | .owner = THIS_MODULE, | ||
355 | #ifdef CONFIG_PM | ||
356 | .pm = &dw_wdt_pm_ops, | ||
357 | #endif /* CONFIG_PM */ | ||
358 | }, | ||
359 | }; | ||
360 | |||
361 | static int __init dw_wdt_watchdog_init(void) | ||
362 | { | ||
363 | return platform_driver_register(&dw_wdt_driver); | ||
364 | } | ||
365 | module_init(dw_wdt_watchdog_init); | ||
366 | |||
367 | static void __exit dw_wdt_watchdog_exit(void) | ||
368 | { | ||
369 | platform_driver_unregister(&dw_wdt_driver); | ||
370 | } | ||
371 | module_exit(dw_wdt_watchdog_exit); | ||
372 | |||
373 | MODULE_AUTHOR("Jamie Iles"); | ||
374 | MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver"); | ||
375 | MODULE_LICENSE("GPL"); | ||
376 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8cb26855bfed..410fba45378d 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -36,7 +36,7 @@ | |||
36 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | 37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
38 | 38 | ||
39 | #define HPWDT_VERSION "1.2.0" | 39 | #define HPWDT_VERSION "1.3.0" |
40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) | 40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) |
41 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) | 41 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) |
42 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) | 42 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) |
@@ -87,6 +87,19 @@ struct smbios_cru64_info { | |||
87 | }; | 87 | }; |
88 | #define SMBIOS_CRU64_INFORMATION 212 | 88 | #define SMBIOS_CRU64_INFORMATION 212 |
89 | 89 | ||
90 | /* type 219 */ | ||
91 | struct smbios_proliant_info { | ||
92 | u8 type; | ||
93 | u8 byte_length; | ||
94 | u16 handle; | ||
95 | u32 power_features; | ||
96 | u32 omega_features; | ||
97 | u32 reserved; | ||
98 | u32 misc_features; | ||
99 | }; | ||
100 | #define SMBIOS_ICRU_INFORMATION 219 | ||
101 | |||
102 | |||
90 | struct cmn_registers { | 103 | struct cmn_registers { |
91 | union { | 104 | union { |
92 | struct { | 105 | struct { |
@@ -132,6 +145,7 @@ struct cmn_registers { | |||
132 | static unsigned int hpwdt_nmi_decoding; | 145 | static unsigned int hpwdt_nmi_decoding; |
133 | static unsigned int allow_kdump; | 146 | static unsigned int allow_kdump; |
134 | static unsigned int priority; /* hpwdt at end of die_notify list */ | 147 | static unsigned int priority; /* hpwdt at end of die_notify list */ |
148 | static unsigned int is_icru; | ||
135 | static DEFINE_SPINLOCK(rom_lock); | 149 | static DEFINE_SPINLOCK(rom_lock); |
136 | static void *cru_rom_addr; | 150 | static void *cru_rom_addr; |
137 | static struct cmn_registers cmn_regs; | 151 | static struct cmn_registers cmn_regs; |
@@ -476,19 +490,22 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, | |||
476 | goto out; | 490 | goto out; |
477 | 491 | ||
478 | spin_lock_irqsave(&rom_lock, rom_pl); | 492 | spin_lock_irqsave(&rom_lock, rom_pl); |
479 | if (!die_nmi_called) | 493 | if (!die_nmi_called && !is_icru) |
480 | asminline_call(&cmn_regs, cru_rom_addr); | 494 | asminline_call(&cmn_regs, cru_rom_addr); |
481 | die_nmi_called = 1; | 495 | die_nmi_called = 1; |
482 | spin_unlock_irqrestore(&rom_lock, rom_pl); | 496 | spin_unlock_irqrestore(&rom_lock, rom_pl); |
483 | if (cmn_regs.u1.ral == 0) { | 497 | if (!is_icru) { |
484 | printk(KERN_WARNING "hpwdt: An NMI occurred, " | 498 | if (cmn_regs.u1.ral == 0) { |
485 | "but unable to determine source.\n"); | 499 | printk(KERN_WARNING "hpwdt: An NMI occurred, " |
486 | } else { | 500 | "but unable to determine source.\n"); |
487 | if (allow_kdump) | 501 | } |
488 | hpwdt_stop(); | ||
489 | panic("An NMI occurred, please see the Integrated " | ||
490 | "Management Log for details.\n"); | ||
491 | } | 502 | } |
503 | |||
504 | if (allow_kdump) | ||
505 | hpwdt_stop(); | ||
506 | panic("An NMI occurred, please see the Integrated " | ||
507 | "Management Log for details.\n"); | ||
508 | |||
492 | out: | 509 | out: |
493 | return NOTIFY_OK; | 510 | return NOTIFY_OK; |
494 | } | 511 | } |
@@ -659,30 +676,63 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) | |||
659 | } | 676 | } |
660 | #endif /* CONFIG_X86_LOCAL_APIC */ | 677 | #endif /* CONFIG_X86_LOCAL_APIC */ |
661 | 678 | ||
679 | /* | ||
680 | * dmi_find_icru | ||
681 | * | ||
682 | * Routine Description: | ||
683 | * This function checks whether or not we are on an iCRU-based server. | ||
684 | * This check is independent of architecture and needs to be made for | ||
685 | * any ProLiant system. | ||
686 | */ | ||
687 | static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy) | ||
688 | { | ||
689 | struct smbios_proliant_info *smbios_proliant_ptr; | ||
690 | |||
691 | if (dm->type == SMBIOS_ICRU_INFORMATION) { | ||
692 | smbios_proliant_ptr = (struct smbios_proliant_info *) dm; | ||
693 | if (smbios_proliant_ptr->misc_features & 0x01) | ||
694 | is_icru = 1; | ||
695 | } | ||
696 | } | ||
697 | |||
662 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | 698 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) |
663 | { | 699 | { |
664 | int retval; | 700 | int retval; |
665 | 701 | ||
666 | /* | 702 | /* |
667 | * We need to map the ROM to get the CRU service. | 703 | * On typical CRU-based systems we need to map that service in |
668 | * For 32 bit Operating Systems we need to go through the 32 Bit | 704 | * the BIOS. For 32 bit Operating Systems we need to go through |
669 | * BIOS Service Directory | 705 | * the 32 Bit BIOS Service Directory. For 64 bit Operating |
670 | * For 64 bit Operating Systems we get that service through SMBIOS. | 706 | * Systems we get that service through SMBIOS. |
707 | * | ||
708 | * On systems that support the new iCRU service all we need to | ||
709 | * do is call dmi_walk to get the supported flag value and skip | ||
710 | * the old cru detect code. | ||
671 | */ | 711 | */ |
672 | retval = detect_cru_service(); | 712 | dmi_walk(dmi_find_icru, NULL); |
673 | if (retval < 0) { | 713 | if (!is_icru) { |
674 | dev_warn(&dev->dev, | 714 | |
675 | "Unable to detect the %d Bit CRU Service.\n", | 715 | /* |
676 | HPWDT_ARCH); | 716 | * We need to map the ROM to get the CRU service. |
677 | return retval; | 717 | * For 32 bit Operating Systems we need to go through the 32 Bit |
678 | } | 718 | * BIOS Service Directory |
719 | * For 64 bit Operating Systems we get that service through SMBIOS. | ||
720 | */ | ||
721 | retval = detect_cru_service(); | ||
722 | if (retval < 0) { | ||
723 | dev_warn(&dev->dev, | ||
724 | "Unable to detect the %d Bit CRU Service.\n", | ||
725 | HPWDT_ARCH); | ||
726 | return retval; | ||
727 | } | ||
679 | 728 | ||
680 | /* | 729 | /* |
681 | * We know this is the only CRU call we need to make so lets keep as | 730 | * We know this is the only CRU call we need to make so lets keep as |
682 | * few instructions as possible once the NMI comes in. | 731 | * few instructions as possible once the NMI comes in. |
683 | */ | 732 | */ |
684 | cmn_regs.u1.rah = 0x0D; | 733 | cmn_regs.u1.rah = 0x0D; |
685 | cmn_regs.u1.ral = 0x02; | 734 | cmn_regs.u1.ral = 0x02; |
735 | } | ||
686 | 736 | ||
687 | /* | 737 | /* |
688 | * If the priority is set to 1, then we will be put first on the | 738 | * If the priority is set to 1, then we will be put first on the |
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 5fd020da7c55..751a591684da 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c | |||
@@ -120,72 +120,12 @@ enum iTCO_chipsets { | |||
120 | TCO_3420, /* 3420 */ | 120 | TCO_3420, /* 3420 */ |
121 | TCO_3450, /* 3450 */ | 121 | TCO_3450, /* 3450 */ |
122 | TCO_EP80579, /* EP80579 */ | 122 | TCO_EP80579, /* EP80579 */ |
123 | TCO_CPT1, /* Cougar Point */ | 123 | TCO_CPT, /* Cougar Point */ |
124 | TCO_CPT2, /* Cougar Point Desktop */ | 124 | TCO_CPTD, /* Cougar Point Desktop */ |
125 | TCO_CPT3, /* Cougar Point Mobile */ | 125 | TCO_CPTM, /* Cougar Point Mobile */ |
126 | TCO_CPT4, /* Cougar Point */ | 126 | TCO_PBG, /* Patsburg */ |
127 | TCO_CPT5, /* Cougar Point */ | ||
128 | TCO_CPT6, /* Cougar Point */ | ||
129 | TCO_CPT7, /* Cougar Point */ | ||
130 | TCO_CPT8, /* Cougar Point */ | ||
131 | TCO_CPT9, /* Cougar Point */ | ||
132 | TCO_CPT10, /* Cougar Point */ | ||
133 | TCO_CPT11, /* Cougar Point */ | ||
134 | TCO_CPT12, /* Cougar Point */ | ||
135 | TCO_CPT13, /* Cougar Point */ | ||
136 | TCO_CPT14, /* Cougar Point */ | ||
137 | TCO_CPT15, /* Cougar Point */ | ||
138 | TCO_CPT16, /* Cougar Point */ | ||
139 | TCO_CPT17, /* Cougar Point */ | ||
140 | TCO_CPT18, /* Cougar Point */ | ||
141 | TCO_CPT19, /* Cougar Point */ | ||
142 | TCO_CPT20, /* Cougar Point */ | ||
143 | TCO_CPT21, /* Cougar Point */ | ||
144 | TCO_CPT22, /* Cougar Point */ | ||
145 | TCO_CPT23, /* Cougar Point */ | ||
146 | TCO_CPT24, /* Cougar Point */ | ||
147 | TCO_CPT25, /* Cougar Point */ | ||
148 | TCO_CPT26, /* Cougar Point */ | ||
149 | TCO_CPT27, /* Cougar Point */ | ||
150 | TCO_CPT28, /* Cougar Point */ | ||
151 | TCO_CPT29, /* Cougar Point */ | ||
152 | TCO_CPT30, /* Cougar Point */ | ||
153 | TCO_CPT31, /* Cougar Point */ | ||
154 | TCO_PBG1, /* Patsburg */ | ||
155 | TCO_PBG2, /* Patsburg */ | ||
156 | TCO_DH89XXCC, /* DH89xxCC */ | 127 | TCO_DH89XXCC, /* DH89xxCC */ |
157 | TCO_PPT0, /* Panther Point */ | 128 | TCO_PPT, /* Panther Point */ |
158 | TCO_PPT1, /* Panther Point */ | ||
159 | TCO_PPT2, /* Panther Point */ | ||
160 | TCO_PPT3, /* Panther Point */ | ||
161 | TCO_PPT4, /* Panther Point */ | ||
162 | TCO_PPT5, /* Panther Point */ | ||
163 | TCO_PPT6, /* Panther Point */ | ||
164 | TCO_PPT7, /* Panther Point */ | ||
165 | TCO_PPT8, /* Panther Point */ | ||
166 | TCO_PPT9, /* Panther Point */ | ||
167 | TCO_PPT10, /* Panther Point */ | ||
168 | TCO_PPT11, /* Panther Point */ | ||
169 | TCO_PPT12, /* Panther Point */ | ||
170 | TCO_PPT13, /* Panther Point */ | ||
171 | TCO_PPT14, /* Panther Point */ | ||
172 | TCO_PPT15, /* Panther Point */ | ||
173 | TCO_PPT16, /* Panther Point */ | ||
174 | TCO_PPT17, /* Panther Point */ | ||
175 | TCO_PPT18, /* Panther Point */ | ||
176 | TCO_PPT19, /* Panther Point */ | ||
177 | TCO_PPT20, /* Panther Point */ | ||
178 | TCO_PPT21, /* Panther Point */ | ||
179 | TCO_PPT22, /* Panther Point */ | ||
180 | TCO_PPT23, /* Panther Point */ | ||
181 | TCO_PPT24, /* Panther Point */ | ||
182 | TCO_PPT25, /* Panther Point */ | ||
183 | TCO_PPT26, /* Panther Point */ | ||
184 | TCO_PPT27, /* Panther Point */ | ||
185 | TCO_PPT28, /* Panther Point */ | ||
186 | TCO_PPT29, /* Panther Point */ | ||
187 | TCO_PPT30, /* Panther Point */ | ||
188 | TCO_PPT31, /* Panther Point */ | ||
189 | }; | 129 | }; |
190 | 130 | ||
191 | static struct { | 131 | static struct { |
@@ -244,83 +184,14 @@ static struct { | |||
244 | {"3450", 2}, | 184 | {"3450", 2}, |
245 | {"EP80579", 2}, | 185 | {"EP80579", 2}, |
246 | {"Cougar Point", 2}, | 186 | {"Cougar Point", 2}, |
247 | {"Cougar Point", 2}, | 187 | {"Cougar Point Desktop", 2}, |
248 | {"Cougar Point", 2}, | 188 | {"Cougar Point Mobile", 2}, |
249 | {"Cougar Point", 2}, | ||
250 | {"Cougar Point", 2}, | ||
251 | {"Cougar Point", 2}, | ||
252 | {"Cougar Point", 2}, | ||
253 | {"Cougar Point", 2}, | ||
254 | {"Cougar Point", 2}, | ||
255 | {"Cougar Point", 2}, | ||
256 | {"Cougar Point", 2}, | ||
257 | {"Cougar Point", 2}, | ||
258 | {"Cougar Point", 2}, | ||
259 | {"Cougar Point", 2}, | ||
260 | {"Cougar Point", 2}, | ||
261 | {"Cougar Point", 2}, | ||
262 | {"Cougar Point", 2}, | ||
263 | {"Cougar Point", 2}, | ||
264 | {"Cougar Point", 2}, | ||
265 | {"Cougar Point", 2}, | ||
266 | {"Cougar Point", 2}, | ||
267 | {"Cougar Point", 2}, | ||
268 | {"Cougar Point", 2}, | ||
269 | {"Cougar Point", 2}, | ||
270 | {"Cougar Point", 2}, | ||
271 | {"Cougar Point", 2}, | ||
272 | {"Cougar Point", 2}, | ||
273 | {"Cougar Point", 2}, | ||
274 | {"Cougar Point", 2}, | ||
275 | {"Cougar Point", 2}, | ||
276 | {"Cougar Point", 2}, | ||
277 | {"Patsburg", 2}, | ||
278 | {"Patsburg", 2}, | 189 | {"Patsburg", 2}, |
279 | {"DH89xxCC", 2}, | 190 | {"DH89xxCC", 2}, |
280 | {"Panther Point", 2}, | 191 | {"Panther Point", 2}, |
281 | {"Panther Point", 2}, | ||
282 | {"Panther Point", 2}, | ||
283 | {"Panther Point", 2}, | ||
284 | {"Panther Point", 2}, | ||
285 | {"Panther Point", 2}, | ||
286 | {"Panther Point", 2}, | ||
287 | {"Panther Point", 2}, | ||
288 | {"Panther Point", 2}, | ||
289 | {"Panther Point", 2}, | ||
290 | {"Panther Point", 2}, | ||
291 | {"Panther Point", 2}, | ||
292 | {"Panther Point", 2}, | ||
293 | {"Panther Point", 2}, | ||
294 | {"Panther Point", 2}, | ||
295 | {"Panther Point", 2}, | ||
296 | {"Panther Point", 2}, | ||
297 | {"Panther Point", 2}, | ||
298 | {"Panther Point", 2}, | ||
299 | {"Panther Point", 2}, | ||
300 | {"Panther Point", 2}, | ||
301 | {"Panther Point", 2}, | ||
302 | {"Panther Point", 2}, | ||
303 | {"Panther Point", 2}, | ||
304 | {"Panther Point", 2}, | ||
305 | {"Panther Point", 2}, | ||
306 | {"Panther Point", 2}, | ||
307 | {"Panther Point", 2}, | ||
308 | {"Panther Point", 2}, | ||
309 | {"Panther Point", 2}, | ||
310 | {"Panther Point", 2}, | ||
311 | {"Panther Point", 2}, | ||
312 | {NULL, 0} | 192 | {NULL, 0} |
313 | }; | 193 | }; |
314 | 194 | ||
315 | #define ITCO_PCI_DEVICE(dev, data) \ | ||
316 | .vendor = PCI_VENDOR_ID_INTEL, \ | ||
317 | .device = dev, \ | ||
318 | .subvendor = PCI_ANY_ID, \ | ||
319 | .subdevice = PCI_ANY_ID, \ | ||
320 | .class = 0, \ | ||
321 | .class_mask = 0, \ | ||
322 | .driver_data = data | ||
323 | |||
324 | /* | 195 | /* |
325 | * This data only exists for exporting the supported PCI ids | 196 | * This data only exists for exporting the supported PCI ids |
326 | * via MODULE_DEVICE_TABLE. We do not actually register a | 197 | * via MODULE_DEVICE_TABLE. We do not actually register a |
@@ -328,138 +199,138 @@ static struct { | |||
328 | * functions that probably will be registered by other drivers. | 199 | * functions that probably will be registered by other drivers. |
329 | */ | 200 | */ |
330 | static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { | 201 | static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { |
331 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0, TCO_ICH)}, | 202 | { PCI_VDEVICE(INTEL, 0x2410), TCO_ICH}, |
332 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0, TCO_ICH0)}, | 203 | { PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0}, |
333 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0, TCO_ICH2)}, | 204 | { PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2}, |
334 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10, TCO_ICH2M)}, | 205 | { PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M}, |
335 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0, TCO_ICH3)}, | 206 | { PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3}, |
336 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12, TCO_ICH3M)}, | 207 | { PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M}, |
337 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0, TCO_ICH4)}, | 208 | { PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4}, |
338 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12, TCO_ICH4M)}, | 209 | { PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M}, |
339 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0, TCO_CICH)}, | 210 | { PCI_VDEVICE(INTEL, 0x2450), TCO_CICH}, |
340 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0, TCO_ICH5)}, | 211 | { PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5}, |
341 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1, TCO_6300ESB)}, | 212 | { PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB}, |
342 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0, TCO_ICH6)}, | 213 | { PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6}, |
343 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1, TCO_ICH6M)}, | 214 | { PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M}, |
344 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2, TCO_ICH6W)}, | 215 | { PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W}, |
345 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0, TCO_631XESB)}, | 216 | { PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB}, |
346 | { ITCO_PCI_DEVICE(0x2671, TCO_631XESB)}, | 217 | { PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB}, |
347 | { ITCO_PCI_DEVICE(0x2672, TCO_631XESB)}, | 218 | { PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB}, |
348 | { ITCO_PCI_DEVICE(0x2673, TCO_631XESB)}, | 219 | { PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB}, |
349 | { ITCO_PCI_DEVICE(0x2674, TCO_631XESB)}, | 220 | { PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB}, |
350 | { ITCO_PCI_DEVICE(0x2675, TCO_631XESB)}, | 221 | { PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB}, |
351 | { ITCO_PCI_DEVICE(0x2676, TCO_631XESB)}, | 222 | { PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB}, |
352 | { ITCO_PCI_DEVICE(0x2677, TCO_631XESB)}, | 223 | { PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB}, |
353 | { ITCO_PCI_DEVICE(0x2678, TCO_631XESB)}, | 224 | { PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB}, |
354 | { ITCO_PCI_DEVICE(0x2679, TCO_631XESB)}, | 225 | { PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB}, |
355 | { ITCO_PCI_DEVICE(0x267a, TCO_631XESB)}, | 226 | { PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB}, |
356 | { ITCO_PCI_DEVICE(0x267b, TCO_631XESB)}, | 227 | { PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB}, |
357 | { ITCO_PCI_DEVICE(0x267c, TCO_631XESB)}, | 228 | { PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB}, |
358 | { ITCO_PCI_DEVICE(0x267d, TCO_631XESB)}, | 229 | { PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB}, |
359 | { ITCO_PCI_DEVICE(0x267e, TCO_631XESB)}, | 230 | { PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB}, |
360 | { ITCO_PCI_DEVICE(0x267f, TCO_631XESB)}, | 231 | { PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB}, |
361 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0, TCO_ICH7)}, | 232 | { PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7}, |
362 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)}, | 233 | { PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH}, |
363 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)}, | 234 | { PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M}, |
364 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)}, | 235 | { PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH}, |
365 | { ITCO_PCI_DEVICE(0x27bc, TCO_NM10)}, | 236 | { PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10}, |
366 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)}, | 237 | { PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8}, |
367 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)}, | 238 | { PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH}, |
368 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)}, | 239 | { PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO}, |
369 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4, TCO_ICH8M)}, | 240 | { PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M}, |
370 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1, TCO_ICH8ME)}, | 241 | { PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME}, |
371 | { ITCO_PCI_DEVICE(0x2918, TCO_ICH9)}, | 242 | { PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9}, |
372 | { ITCO_PCI_DEVICE(0x2916, TCO_ICH9R)}, | 243 | { PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R}, |
373 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2, TCO_ICH9DH)}, | 244 | { PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH}, |
374 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4, TCO_ICH9DO)}, | 245 | { PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO}, |
375 | { ITCO_PCI_DEVICE(0x2919, TCO_ICH9M)}, | 246 | { PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M}, |
376 | { ITCO_PCI_DEVICE(0x2917, TCO_ICH9ME)}, | 247 | { PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME}, |
377 | { ITCO_PCI_DEVICE(0x3a18, TCO_ICH10)}, | 248 | { PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10}, |
378 | { ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)}, | 249 | { PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R}, |
379 | { ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)}, | 250 | { PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D}, |
380 | { ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)}, | 251 | { PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO}, |
381 | { ITCO_PCI_DEVICE(0x3b00, TCO_PCH)}, | 252 | { PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH}, |
382 | { ITCO_PCI_DEVICE(0x3b01, TCO_PCHM)}, | 253 | { PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM}, |
383 | { ITCO_PCI_DEVICE(0x3b02, TCO_P55)}, | 254 | { PCI_VDEVICE(INTEL, 0x3b02), TCO_P55}, |
384 | { ITCO_PCI_DEVICE(0x3b03, TCO_PM55)}, | 255 | { PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55}, |
385 | { ITCO_PCI_DEVICE(0x3b06, TCO_H55)}, | 256 | { PCI_VDEVICE(INTEL, 0x3b06), TCO_H55}, |
386 | { ITCO_PCI_DEVICE(0x3b07, TCO_QM57)}, | 257 | { PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57}, |
387 | { ITCO_PCI_DEVICE(0x3b08, TCO_H57)}, | 258 | { PCI_VDEVICE(INTEL, 0x3b08), TCO_H57}, |
388 | { ITCO_PCI_DEVICE(0x3b09, TCO_HM55)}, | 259 | { PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55}, |
389 | { ITCO_PCI_DEVICE(0x3b0a, TCO_Q57)}, | 260 | { PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57}, |
390 | { ITCO_PCI_DEVICE(0x3b0b, TCO_HM57)}, | 261 | { PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57}, |
391 | { ITCO_PCI_DEVICE(0x3b0d, TCO_PCHMSFF)}, | 262 | { PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF}, |
392 | { ITCO_PCI_DEVICE(0x3b0f, TCO_QS57)}, | 263 | { PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57}, |
393 | { ITCO_PCI_DEVICE(0x3b12, TCO_3400)}, | 264 | { PCI_VDEVICE(INTEL, 0x3b12), TCO_3400}, |
394 | { ITCO_PCI_DEVICE(0x3b14, TCO_3420)}, | 265 | { PCI_VDEVICE(INTEL, 0x3b14), TCO_3420}, |
395 | { ITCO_PCI_DEVICE(0x3b16, TCO_3450)}, | 266 | { PCI_VDEVICE(INTEL, 0x3b16), TCO_3450}, |
396 | { ITCO_PCI_DEVICE(0x5031, TCO_EP80579)}, | 267 | { PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579}, |
397 | { ITCO_PCI_DEVICE(0x1c41, TCO_CPT1)}, | 268 | { PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT}, |
398 | { ITCO_PCI_DEVICE(0x1c42, TCO_CPT2)}, | 269 | { PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD}, |
399 | { ITCO_PCI_DEVICE(0x1c43, TCO_CPT3)}, | 270 | { PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM}, |
400 | { ITCO_PCI_DEVICE(0x1c44, TCO_CPT4)}, | 271 | { PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT}, |
401 | { ITCO_PCI_DEVICE(0x1c45, TCO_CPT5)}, | 272 | { PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT}, |
402 | { ITCO_PCI_DEVICE(0x1c46, TCO_CPT6)}, | 273 | { PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT}, |
403 | { ITCO_PCI_DEVICE(0x1c47, TCO_CPT7)}, | 274 | { PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT}, |
404 | { ITCO_PCI_DEVICE(0x1c48, TCO_CPT8)}, | 275 | { PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT}, |
405 | { ITCO_PCI_DEVICE(0x1c49, TCO_CPT9)}, | 276 | { PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT}, |
406 | { ITCO_PCI_DEVICE(0x1c4a, TCO_CPT10)}, | 277 | { PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT}, |
407 | { ITCO_PCI_DEVICE(0x1c4b, TCO_CPT11)}, | 278 | { PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT}, |
408 | { ITCO_PCI_DEVICE(0x1c4c, TCO_CPT12)}, | 279 | { PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT}, |
409 | { ITCO_PCI_DEVICE(0x1c4d, TCO_CPT13)}, | 280 | { PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT}, |
410 | { ITCO_PCI_DEVICE(0x1c4e, TCO_CPT14)}, | 281 | { PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT}, |
411 | { ITCO_PCI_DEVICE(0x1c4f, TCO_CPT15)}, | 282 | { PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT}, |
412 | { ITCO_PCI_DEVICE(0x1c50, TCO_CPT16)}, | 283 | { PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT}, |
413 | { ITCO_PCI_DEVICE(0x1c51, TCO_CPT17)}, | 284 | { PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT}, |
414 | { ITCO_PCI_DEVICE(0x1c52, TCO_CPT18)}, | 285 | { PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT}, |
415 | { ITCO_PCI_DEVICE(0x1c53, TCO_CPT19)}, | 286 | { PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT}, |
416 | { ITCO_PCI_DEVICE(0x1c54, TCO_CPT20)}, | 287 | { PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT}, |
417 | { ITCO_PCI_DEVICE(0x1c55, TCO_CPT21)}, | 288 | { PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT}, |
418 | { ITCO_PCI_DEVICE(0x1c56, TCO_CPT22)}, | 289 | { PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT}, |
419 | { ITCO_PCI_DEVICE(0x1c57, TCO_CPT23)}, | 290 | { PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT}, |
420 | { ITCO_PCI_DEVICE(0x1c58, TCO_CPT24)}, | 291 | { PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT}, |
421 | { ITCO_PCI_DEVICE(0x1c59, TCO_CPT25)}, | 292 | { PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT}, |
422 | { ITCO_PCI_DEVICE(0x1c5a, TCO_CPT26)}, | 293 | { PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT}, |
423 | { ITCO_PCI_DEVICE(0x1c5b, TCO_CPT27)}, | 294 | { PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT}, |
424 | { ITCO_PCI_DEVICE(0x1c5c, TCO_CPT28)}, | 295 | { PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT}, |
425 | { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)}, | 296 | { PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT}, |
426 | { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)}, | 297 | { PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT}, |
427 | { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)}, | 298 | { PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT}, |
428 | { ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)}, | 299 | { PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG}, |
429 | { ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)}, | 300 | { PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG}, |
430 | { ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)}, | 301 | { PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC}, |
431 | { ITCO_PCI_DEVICE(0x1e40, TCO_PPT0)}, | 302 | { PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT}, |
432 | { ITCO_PCI_DEVICE(0x1e41, TCO_PPT1)}, | 303 | { PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT}, |
433 | { ITCO_PCI_DEVICE(0x1e42, TCO_PPT2)}, | 304 | { PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT}, |
434 | { ITCO_PCI_DEVICE(0x1e43, TCO_PPT3)}, | 305 | { PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT}, |
435 | { ITCO_PCI_DEVICE(0x1e44, TCO_PPT4)}, | 306 | { PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT}, |
436 | { ITCO_PCI_DEVICE(0x1e45, TCO_PPT5)}, | 307 | { PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT}, |
437 | { ITCO_PCI_DEVICE(0x1e46, TCO_PPT6)}, | 308 | { PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT}, |
438 | { ITCO_PCI_DEVICE(0x1e47, TCO_PPT7)}, | 309 | { PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT}, |
439 | { ITCO_PCI_DEVICE(0x1e48, TCO_PPT8)}, | 310 | { PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT}, |
440 | { ITCO_PCI_DEVICE(0x1e49, TCO_PPT9)}, | 311 | { PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT}, |
441 | { ITCO_PCI_DEVICE(0x1e4a, TCO_PPT10)}, | 312 | { PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT}, |
442 | { ITCO_PCI_DEVICE(0x1e4b, TCO_PPT11)}, | 313 | { PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT}, |
443 | { ITCO_PCI_DEVICE(0x1e4c, TCO_PPT12)}, | 314 | { PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT}, |
444 | { ITCO_PCI_DEVICE(0x1e4d, TCO_PPT13)}, | 315 | { PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT}, |
445 | { ITCO_PCI_DEVICE(0x1e4e, TCO_PPT14)}, | 316 | { PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT}, |
446 | { ITCO_PCI_DEVICE(0x1e4f, TCO_PPT15)}, | 317 | { PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT}, |
447 | { ITCO_PCI_DEVICE(0x1e50, TCO_PPT16)}, | 318 | { PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT}, |
448 | { ITCO_PCI_DEVICE(0x1e51, TCO_PPT17)}, | 319 | { PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT}, |
449 | { ITCO_PCI_DEVICE(0x1e52, TCO_PPT18)}, | 320 | { PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT}, |
450 | { ITCO_PCI_DEVICE(0x1e53, TCO_PPT19)}, | 321 | { PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT}, |
451 | { ITCO_PCI_DEVICE(0x1e54, TCO_PPT20)}, | 322 | { PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT}, |
452 | { ITCO_PCI_DEVICE(0x1e55, TCO_PPT21)}, | 323 | { PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT}, |
453 | { ITCO_PCI_DEVICE(0x1e56, TCO_PPT22)}, | 324 | { PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT}, |
454 | { ITCO_PCI_DEVICE(0x1e57, TCO_PPT23)}, | 325 | { PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT}, |
455 | { ITCO_PCI_DEVICE(0x1e58, TCO_PPT24)}, | 326 | { PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT}, |
456 | { ITCO_PCI_DEVICE(0x1e59, TCO_PPT25)}, | 327 | { PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT}, |
457 | { ITCO_PCI_DEVICE(0x1e5a, TCO_PPT26)}, | 328 | { PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT}, |
458 | { ITCO_PCI_DEVICE(0x1e5b, TCO_PPT27)}, | 329 | { PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT}, |
459 | { ITCO_PCI_DEVICE(0x1e5c, TCO_PPT28)}, | 330 | { PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT}, |
460 | { ITCO_PCI_DEVICE(0x1e5d, TCO_PPT29)}, | 331 | { PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, |
461 | { ITCO_PCI_DEVICE(0x1e5e, TCO_PPT30)}, | 332 | { PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, |
462 | { ITCO_PCI_DEVICE(0x1e5f, TCO_PPT31)}, | 333 | { PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT}, |
463 | { 0, }, /* End of list */ | 334 | { 0, }, /* End of list */ |
464 | }; | 335 | }; |
465 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); | 336 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); |
@@ -1052,15 +923,10 @@ static void iTCO_wdt_shutdown(struct platform_device *dev) | |||
1052 | iTCO_wdt_stop(); | 923 | iTCO_wdt_stop(); |
1053 | } | 924 | } |
1054 | 925 | ||
1055 | #define iTCO_wdt_suspend NULL | ||
1056 | #define iTCO_wdt_resume NULL | ||
1057 | |||
1058 | static struct platform_driver iTCO_wdt_driver = { | 926 | static struct platform_driver iTCO_wdt_driver = { |
1059 | .probe = iTCO_wdt_probe, | 927 | .probe = iTCO_wdt_probe, |
1060 | .remove = __devexit_p(iTCO_wdt_remove), | 928 | .remove = __devexit_p(iTCO_wdt_remove), |
1061 | .shutdown = iTCO_wdt_shutdown, | 929 | .shutdown = iTCO_wdt_shutdown, |
1062 | .suspend = iTCO_wdt_suspend, | ||
1063 | .resume = iTCO_wdt_resume, | ||
1064 | .driver = { | 930 | .driver = { |
1065 | .owner = THIS_MODULE, | 931 | .owner = THIS_MODULE, |
1066 | .name = DRV_NAME, | 932 | .name = DRV_NAME, |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 86f7cac1026c..b8ef2c6dca7c 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -329,12 +329,18 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) | |||
329 | } | 329 | } |
330 | } | 330 | } |
331 | 331 | ||
332 | static const struct of_device_id imx2_wdt_dt_ids[] = { | ||
333 | { .compatible = "fsl,imx21-wdt", }, | ||
334 | { /* sentinel */ } | ||
335 | }; | ||
336 | |||
332 | static struct platform_driver imx2_wdt_driver = { | 337 | static struct platform_driver imx2_wdt_driver = { |
333 | .remove = __exit_p(imx2_wdt_remove), | 338 | .remove = __exit_p(imx2_wdt_remove), |
334 | .shutdown = imx2_wdt_shutdown, | 339 | .shutdown = imx2_wdt_shutdown, |
335 | .driver = { | 340 | .driver = { |
336 | .name = DRIVER_NAME, | 341 | .name = DRIVER_NAME, |
337 | .owner = THIS_MODULE, | 342 | .owner = THIS_MODULE, |
343 | .of_match_table = imx2_wdt_dt_ids, | ||
338 | }, | 344 | }, |
339 | }; | 345 | }; |
340 | 346 | ||
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 6143f52ba6b8..8d2d8502d3e8 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c | |||
@@ -28,10 +28,10 @@ | |||
28 | #include <linux/notifier.h> | 28 | #include <linux/notifier.h> |
29 | #include <linux/reboot.h> | 29 | #include <linux/reboot.h> |
30 | #include <linux/fs.h> | 30 | #include <linux/fs.h> |
31 | #include <linux/pci.h> | ||
32 | #include <linux/spinlock.h> | 31 | #include <linux/spinlock.h> |
33 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
34 | #include <linux/io.h> | 33 | #include <linux/io.h> |
34 | #include <linux/ioport.h> | ||
35 | 35 | ||
36 | #define NAME "it8712f_wdt" | 36 | #define NAME "it8712f_wdt" |
37 | 37 | ||
@@ -51,7 +51,6 @@ MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); | |||
51 | 51 | ||
52 | static unsigned long wdt_open; | 52 | static unsigned long wdt_open; |
53 | static unsigned expect_close; | 53 | static unsigned expect_close; |
54 | static spinlock_t io_lock; | ||
55 | static unsigned char revision; | 54 | static unsigned char revision; |
56 | 55 | ||
57 | /* Dog Food address - We use the game port address */ | 56 | /* Dog Food address - We use the game port address */ |
@@ -121,20 +120,26 @@ static inline void superio_select(int ldn) | |||
121 | outb(ldn, VAL); | 120 | outb(ldn, VAL); |
122 | } | 121 | } |
123 | 122 | ||
124 | static inline void superio_enter(void) | 123 | static inline int superio_enter(void) |
125 | { | 124 | { |
126 | spin_lock(&io_lock); | 125 | /* |
126 | * Try to reserve REG and REG + 1 for exclusive access. | ||
127 | */ | ||
128 | if (!request_muxed_region(REG, 2, NAME)) | ||
129 | return -EBUSY; | ||
130 | |||
127 | outb(0x87, REG); | 131 | outb(0x87, REG); |
128 | outb(0x01, REG); | 132 | outb(0x01, REG); |
129 | outb(0x55, REG); | 133 | outb(0x55, REG); |
130 | outb(0x55, REG); | 134 | outb(0x55, REG); |
135 | return 0; | ||
131 | } | 136 | } |
132 | 137 | ||
133 | static inline void superio_exit(void) | 138 | static inline void superio_exit(void) |
134 | { | 139 | { |
135 | outb(0x02, REG); | 140 | outb(0x02, REG); |
136 | outb(0x02, VAL); | 141 | outb(0x02, VAL); |
137 | spin_unlock(&io_lock); | 142 | release_region(REG, 2); |
138 | } | 143 | } |
139 | 144 | ||
140 | static inline void it8712f_wdt_ping(void) | 145 | static inline void it8712f_wdt_ping(void) |
@@ -173,10 +178,13 @@ static int it8712f_wdt_get_status(void) | |||
173 | return 0; | 178 | return 0; |
174 | } | 179 | } |
175 | 180 | ||
176 | static void it8712f_wdt_enable(void) | 181 | static int it8712f_wdt_enable(void) |
177 | { | 182 | { |
183 | int ret = superio_enter(); | ||
184 | if (ret) | ||
185 | return ret; | ||
186 | |||
178 | printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); | 187 | printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); |
179 | superio_enter(); | ||
180 | superio_select(LDN_GPIO); | 188 | superio_select(LDN_GPIO); |
181 | 189 | ||
182 | superio_outb(wdt_control_reg, WDT_CONTROL); | 190 | superio_outb(wdt_control_reg, WDT_CONTROL); |
@@ -186,13 +194,17 @@ static void it8712f_wdt_enable(void) | |||
186 | superio_exit(); | 194 | superio_exit(); |
187 | 195 | ||
188 | it8712f_wdt_ping(); | 196 | it8712f_wdt_ping(); |
197 | |||
198 | return 0; | ||
189 | } | 199 | } |
190 | 200 | ||
191 | static void it8712f_wdt_disable(void) | 201 | static int it8712f_wdt_disable(void) |
192 | { | 202 | { |
193 | printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); | 203 | int ret = superio_enter(); |
204 | if (ret) | ||
205 | return ret; | ||
194 | 206 | ||
195 | superio_enter(); | 207 | printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); |
196 | superio_select(LDN_GPIO); | 208 | superio_select(LDN_GPIO); |
197 | 209 | ||
198 | superio_outb(0, WDT_CONFIG); | 210 | superio_outb(0, WDT_CONFIG); |
@@ -202,6 +214,7 @@ static void it8712f_wdt_disable(void) | |||
202 | superio_outb(0, WDT_TIMEOUT); | 214 | superio_outb(0, WDT_TIMEOUT); |
203 | 215 | ||
204 | superio_exit(); | 216 | superio_exit(); |
217 | return 0; | ||
205 | } | 218 | } |
206 | 219 | ||
207 | static int it8712f_wdt_notify(struct notifier_block *this, | 220 | static int it8712f_wdt_notify(struct notifier_block *this, |
@@ -252,6 +265,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
252 | WDIOF_MAGICCLOSE, | 265 | WDIOF_MAGICCLOSE, |
253 | }; | 266 | }; |
254 | int value; | 267 | int value; |
268 | int ret; | ||
255 | 269 | ||
256 | switch (cmd) { | 270 | switch (cmd) { |
257 | case WDIOC_GETSUPPORT: | 271 | case WDIOC_GETSUPPORT: |
@@ -259,7 +273,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
259 | return -EFAULT; | 273 | return -EFAULT; |
260 | return 0; | 274 | return 0; |
261 | case WDIOC_GETSTATUS: | 275 | case WDIOC_GETSTATUS: |
262 | superio_enter(); | 276 | ret = superio_enter(); |
277 | if (ret) | ||
278 | return ret; | ||
263 | superio_select(LDN_GPIO); | 279 | superio_select(LDN_GPIO); |
264 | 280 | ||
265 | value = it8712f_wdt_get_status(); | 281 | value = it8712f_wdt_get_status(); |
@@ -280,7 +296,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
280 | if (value > (max_units * 60)) | 296 | if (value > (max_units * 60)) |
281 | return -EINVAL; | 297 | return -EINVAL; |
282 | margin = value; | 298 | margin = value; |
283 | superio_enter(); | 299 | ret = superio_enter(); |
300 | if (ret) | ||
301 | return ret; | ||
284 | superio_select(LDN_GPIO); | 302 | superio_select(LDN_GPIO); |
285 | 303 | ||
286 | it8712f_wdt_update_margin(); | 304 | it8712f_wdt_update_margin(); |
@@ -299,10 +317,14 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, | |||
299 | 317 | ||
300 | static int it8712f_wdt_open(struct inode *inode, struct file *file) | 318 | static int it8712f_wdt_open(struct inode *inode, struct file *file) |
301 | { | 319 | { |
320 | int ret; | ||
302 | /* only allow one at a time */ | 321 | /* only allow one at a time */ |
303 | if (test_and_set_bit(0, &wdt_open)) | 322 | if (test_and_set_bit(0, &wdt_open)) |
304 | return -EBUSY; | 323 | return -EBUSY; |
305 | it8712f_wdt_enable(); | 324 | |
325 | ret = it8712f_wdt_enable(); | ||
326 | if (ret) | ||
327 | return ret; | ||
306 | return nonseekable_open(inode, file); | 328 | return nonseekable_open(inode, file); |
307 | } | 329 | } |
308 | 330 | ||
@@ -313,7 +335,8 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file) | |||
313 | ": watchdog device closed unexpectedly, will not" | 335 | ": watchdog device closed unexpectedly, will not" |
314 | " disable the watchdog timer\n"); | 336 | " disable the watchdog timer\n"); |
315 | } else if (!nowayout) { | 337 | } else if (!nowayout) { |
316 | it8712f_wdt_disable(); | 338 | if (it8712f_wdt_disable()) |
339 | printk(KERN_WARNING NAME "Watchdog disable failed\n"); | ||
317 | } | 340 | } |
318 | expect_close = 0; | 341 | expect_close = 0; |
319 | clear_bit(0, &wdt_open); | 342 | clear_bit(0, &wdt_open); |
@@ -340,8 +363,10 @@ static int __init it8712f_wdt_find(unsigned short *address) | |||
340 | { | 363 | { |
341 | int err = -ENODEV; | 364 | int err = -ENODEV; |
342 | int chip_type; | 365 | int chip_type; |
366 | int ret = superio_enter(); | ||
367 | if (ret) | ||
368 | return ret; | ||
343 | 369 | ||
344 | superio_enter(); | ||
345 | chip_type = superio_inw(DEVID); | 370 | chip_type = superio_inw(DEVID); |
346 | if (chip_type != IT8712F_DEVID) | 371 | if (chip_type != IT8712F_DEVID) |
347 | goto exit; | 372 | goto exit; |
@@ -382,8 +407,6 @@ static int __init it8712f_wdt_init(void) | |||
382 | { | 407 | { |
383 | int err = 0; | 408 | int err = 0; |
384 | 409 | ||
385 | spin_lock_init(&io_lock); | ||
386 | |||
387 | if (it8712f_wdt_find(&address)) | 410 | if (it8712f_wdt_find(&address)) |
388 | return -ENODEV; | 411 | return -ENODEV; |
389 | 412 | ||
@@ -392,7 +415,11 @@ static int __init it8712f_wdt_init(void) | |||
392 | return -EBUSY; | 415 | return -EBUSY; |
393 | } | 416 | } |
394 | 417 | ||
395 | it8712f_wdt_disable(); | 418 | err = it8712f_wdt_disable(); |
419 | if (err) { | ||
420 | printk(KERN_ERR NAME ": unable to disable watchdog timer.\n"); | ||
421 | goto out; | ||
422 | } | ||
396 | 423 | ||
397 | err = register_reboot_notifier(&it8712f_wdt_notifier); | 424 | err = register_reboot_notifier(&it8712f_wdt_notifier); |
398 | if (err) { | 425 | if (err) { |
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index b1bc72f9a209..a2d9a1266a23 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c | |||
@@ -137,7 +137,6 @@ | |||
137 | 137 | ||
138 | static unsigned int base, gpact, ciract, max_units, chip_type; | 138 | static unsigned int base, gpact, ciract, max_units, chip_type; |
139 | static unsigned long wdt_status; | 139 | static unsigned long wdt_status; |
140 | static DEFINE_SPINLOCK(spinlock); | ||
141 | 140 | ||
142 | static int nogameport = DEFAULT_NOGAMEPORT; | 141 | static int nogameport = DEFAULT_NOGAMEPORT; |
143 | static int exclusive = DEFAULT_EXCLUSIVE; | 142 | static int exclusive = DEFAULT_EXCLUSIVE; |
@@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default=" | |||
163 | 162 | ||
164 | /* Superio Chip */ | 163 | /* Superio Chip */ |
165 | 164 | ||
166 | static inline void superio_enter(void) | 165 | static inline int superio_enter(void) |
167 | { | 166 | { |
167 | /* | ||
168 | * Try to reserve REG and REG + 1 for exclusive access. | ||
169 | */ | ||
170 | if (!request_muxed_region(REG, 2, WATCHDOG_NAME)) | ||
171 | return -EBUSY; | ||
172 | |||
168 | outb(0x87, REG); | 173 | outb(0x87, REG); |
169 | outb(0x01, REG); | 174 | outb(0x01, REG); |
170 | outb(0x55, REG); | 175 | outb(0x55, REG); |
171 | outb(0x55, REG); | 176 | outb(0x55, REG); |
177 | return 0; | ||
172 | } | 178 | } |
173 | 179 | ||
174 | static inline void superio_exit(void) | 180 | static inline void superio_exit(void) |
175 | { | 181 | { |
176 | outb(0x02, REG); | 182 | outb(0x02, REG); |
177 | outb(0x02, VAL); | 183 | outb(0x02, VAL); |
184 | release_region(REG, 2); | ||
178 | } | 185 | } |
179 | 186 | ||
180 | static inline void superio_select(int ldn) | 187 | static inline void superio_select(int ldn) |
@@ -255,12 +262,11 @@ static void wdt_keepalive(void) | |||
255 | set_bit(WDTS_KEEPALIVE, &wdt_status); | 262 | set_bit(WDTS_KEEPALIVE, &wdt_status); |
256 | } | 263 | } |
257 | 264 | ||
258 | static void wdt_start(void) | 265 | static int wdt_start(void) |
259 | { | 266 | { |
260 | unsigned long flags; | 267 | int ret = superio_enter(); |
261 | 268 | if (ret) | |
262 | spin_lock_irqsave(&spinlock, flags); | 269 | return ret; |
263 | superio_enter(); | ||
264 | 270 | ||
265 | superio_select(GPIO); | 271 | superio_select(GPIO); |
266 | if (test_bit(WDTS_USE_GP, &wdt_status)) | 272 | if (test_bit(WDTS_USE_GP, &wdt_status)) |
@@ -270,15 +276,15 @@ static void wdt_start(void) | |||
270 | wdt_update_timeout(); | 276 | wdt_update_timeout(); |
271 | 277 | ||
272 | superio_exit(); | 278 | superio_exit(); |
273 | spin_unlock_irqrestore(&spinlock, flags); | 279 | |
280 | return 0; | ||
274 | } | 281 | } |
275 | 282 | ||
276 | static void wdt_stop(void) | 283 | static int wdt_stop(void) |
277 | { | 284 | { |
278 | unsigned long flags; | 285 | int ret = superio_enter(); |
279 | 286 | if (ret) | |
280 | spin_lock_irqsave(&spinlock, flags); | 287 | return ret; |
281 | superio_enter(); | ||
282 | 288 | ||
283 | superio_select(GPIO); | 289 | superio_select(GPIO); |
284 | superio_outb(0x00, WDTCTRL); | 290 | superio_outb(0x00, WDTCTRL); |
@@ -288,7 +294,7 @@ static void wdt_stop(void) | |||
288 | superio_outb(0x00, WDTVALMSB); | 294 | superio_outb(0x00, WDTVALMSB); |
289 | 295 | ||
290 | superio_exit(); | 296 | superio_exit(); |
291 | spin_unlock_irqrestore(&spinlock, flags); | 297 | return 0; |
292 | } | 298 | } |
293 | 299 | ||
294 | /** | 300 | /** |
@@ -303,8 +309,6 @@ static void wdt_stop(void) | |||
303 | 309 | ||
304 | static int wdt_set_timeout(int t) | 310 | static int wdt_set_timeout(int t) |
305 | { | 311 | { |
306 | unsigned long flags; | ||
307 | |||
308 | if (t < 1 || t > max_units * 60) | 312 | if (t < 1 || t > max_units * 60) |
309 | return -EINVAL; | 313 | return -EINVAL; |
310 | 314 | ||
@@ -313,14 +317,15 @@ static int wdt_set_timeout(int t) | |||
313 | else | 317 | else |
314 | timeout = t; | 318 | timeout = t; |
315 | 319 | ||
316 | spin_lock_irqsave(&spinlock, flags); | ||
317 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | 320 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
318 | superio_enter(); | 321 | int ret = superio_enter(); |
322 | if (ret) | ||
323 | return ret; | ||
324 | |||
319 | superio_select(GPIO); | 325 | superio_select(GPIO); |
320 | wdt_update_timeout(); | 326 | wdt_update_timeout(); |
321 | superio_exit(); | 327 | superio_exit(); |
322 | } | 328 | } |
323 | spin_unlock_irqrestore(&spinlock, flags); | ||
324 | return 0; | 329 | return 0; |
325 | } | 330 | } |
326 | 331 | ||
@@ -339,12 +344,12 @@ static int wdt_set_timeout(int t) | |||
339 | 344 | ||
340 | static int wdt_get_status(int *status) | 345 | static int wdt_get_status(int *status) |
341 | { | 346 | { |
342 | unsigned long flags; | ||
343 | |||
344 | *status = 0; | 347 | *status = 0; |
345 | if (testmode) { | 348 | if (testmode) { |
346 | spin_lock_irqsave(&spinlock, flags); | 349 | int ret = superio_enter(); |
347 | superio_enter(); | 350 | if (ret) |
351 | return ret; | ||
352 | |||
348 | superio_select(GPIO); | 353 | superio_select(GPIO); |
349 | if (superio_inb(WDTCTRL) & WDT_ZERO) { | 354 | if (superio_inb(WDTCTRL) & WDT_ZERO) { |
350 | superio_outb(0x00, WDTCTRL); | 355 | superio_outb(0x00, WDTCTRL); |
@@ -353,7 +358,6 @@ static int wdt_get_status(int *status) | |||
353 | } | 358 | } |
354 | 359 | ||
355 | superio_exit(); | 360 | superio_exit(); |
356 | spin_unlock_irqrestore(&spinlock, flags); | ||
357 | } | 361 | } |
358 | if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) | 362 | if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) |
359 | *status |= WDIOF_KEEPALIVEPING; | 363 | *status |= WDIOF_KEEPALIVEPING; |
@@ -379,9 +383,17 @@ static int wdt_open(struct inode *inode, struct file *file) | |||
379 | if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) | 383 | if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) |
380 | return -EBUSY; | 384 | return -EBUSY; |
381 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { | 385 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { |
386 | int ret; | ||
382 | if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) | 387 | if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) |
383 | __module_get(THIS_MODULE); | 388 | __module_get(THIS_MODULE); |
384 | wdt_start(); | 389 | |
390 | ret = wdt_start(); | ||
391 | if (ret) { | ||
392 | clear_bit(WDTS_LOCKED, &wdt_status); | ||
393 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
394 | clear_bit(WDTS_DEV_OPEN, &wdt_status); | ||
395 | return ret; | ||
396 | } | ||
385 | } | 397 | } |
386 | return nonseekable_open(inode, file); | 398 | return nonseekable_open(inode, file); |
387 | } | 399 | } |
@@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file) | |||
403 | { | 415 | { |
404 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { | 416 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
405 | if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { | 417 | if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { |
406 | wdt_stop(); | 418 | int ret = wdt_stop(); |
419 | if (ret) { | ||
420 | /* | ||
421 | * Stop failed. Just keep the watchdog alive | ||
422 | * and hope nothing bad happens. | ||
423 | */ | ||
424 | set_bit(WDTS_EXPECTED, &wdt_status); | ||
425 | wdt_keepalive(); | ||
426 | return ret; | ||
427 | } | ||
407 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | 428 | clear_bit(WDTS_TIMER_RUN, &wdt_status); |
408 | } else { | 429 | } else { |
409 | wdt_keepalive(); | 430 | wdt_keepalive(); |
@@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
484 | &ident, sizeof(ident)) ? -EFAULT : 0; | 505 | &ident, sizeof(ident)) ? -EFAULT : 0; |
485 | 506 | ||
486 | case WDIOC_GETSTATUS: | 507 | case WDIOC_GETSTATUS: |
487 | wdt_get_status(&status); | 508 | rc = wdt_get_status(&status); |
509 | if (rc) | ||
510 | return rc; | ||
488 | return put_user(status, uarg.i); | 511 | return put_user(status, uarg.i); |
489 | 512 | ||
490 | case WDIOC_GETBOOTSTATUS: | 513 | case WDIOC_GETBOOTSTATUS: |
@@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
500 | 523 | ||
501 | switch (new_options) { | 524 | switch (new_options) { |
502 | case WDIOS_DISABLECARD: | 525 | case WDIOS_DISABLECARD: |
503 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) | 526 | if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { |
504 | wdt_stop(); | 527 | rc = wdt_stop(); |
528 | if (rc) | ||
529 | return rc; | ||
530 | } | ||
505 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | 531 | clear_bit(WDTS_TIMER_RUN, &wdt_status); |
506 | return 0; | 532 | return 0; |
507 | 533 | ||
508 | case WDIOS_ENABLECARD: | 534 | case WDIOS_ENABLECARD: |
509 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) | 535 | if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { |
510 | wdt_start(); | 536 | rc = wdt_start(); |
537 | if (rc) { | ||
538 | clear_bit(WDTS_TIMER_RUN, &wdt_status); | ||
539 | return rc; | ||
540 | } | ||
541 | } | ||
511 | return 0; | 542 | return 0; |
512 | 543 | ||
513 | default: | 544 | default: |
@@ -560,16 +591,17 @@ static int __init it87_wdt_init(void) | |||
560 | int rc = 0; | 591 | int rc = 0; |
561 | int try_gameport = !nogameport; | 592 | int try_gameport = !nogameport; |
562 | u8 chip_rev; | 593 | u8 chip_rev; |
563 | unsigned long flags; | 594 | int gp_rreq_fail = 0; |
564 | 595 | ||
565 | wdt_status = 0; | 596 | wdt_status = 0; |
566 | 597 | ||
567 | spin_lock_irqsave(&spinlock, flags); | 598 | rc = superio_enter(); |
568 | superio_enter(); | 599 | if (rc) |
600 | return rc; | ||
601 | |||
569 | chip_type = superio_inw(CHIPID); | 602 | chip_type = superio_inw(CHIPID); |
570 | chip_rev = superio_inb(CHIPREV) & 0x0f; | 603 | chip_rev = superio_inb(CHIPREV) & 0x0f; |
571 | superio_exit(); | 604 | superio_exit(); |
572 | spin_unlock_irqrestore(&spinlock, flags); | ||
573 | 605 | ||
574 | switch (chip_type) { | 606 | switch (chip_type) { |
575 | case IT8702_ID: | 607 | case IT8702_ID: |
@@ -603,8 +635,9 @@ static int __init it87_wdt_init(void) | |||
603 | return -ENODEV; | 635 | return -ENODEV; |
604 | } | 636 | } |
605 | 637 | ||
606 | spin_lock_irqsave(&spinlock, flags); | 638 | rc = superio_enter(); |
607 | superio_enter(); | 639 | if (rc) |
640 | return rc; | ||
608 | 641 | ||
609 | superio_select(GPIO); | 642 | superio_select(GPIO); |
610 | superio_outb(WDT_TOV1, WDTCFG); | 643 | superio_outb(WDT_TOV1, WDTCFG); |
@@ -620,21 +653,16 @@ static int __init it87_wdt_init(void) | |||
620 | } | 653 | } |
621 | gpact = superio_inb(ACTREG); | 654 | gpact = superio_inb(ACTREG); |
622 | superio_outb(0x01, ACTREG); | 655 | superio_outb(0x01, ACTREG); |
623 | superio_exit(); | ||
624 | spin_unlock_irqrestore(&spinlock, flags); | ||
625 | if (request_region(base, 1, WATCHDOG_NAME)) | 656 | if (request_region(base, 1, WATCHDOG_NAME)) |
626 | set_bit(WDTS_USE_GP, &wdt_status); | 657 | set_bit(WDTS_USE_GP, &wdt_status); |
627 | else | 658 | else |
628 | rc = -EIO; | 659 | gp_rreq_fail = 1; |
629 | } else { | ||
630 | superio_exit(); | ||
631 | spin_unlock_irqrestore(&spinlock, flags); | ||
632 | } | 660 | } |
633 | 661 | ||
634 | /* If we haven't Gameport support, try to get CIR support */ | 662 | /* If we haven't Gameport support, try to get CIR support */ |
635 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | 663 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { |
636 | if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { | 664 | if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { |
637 | if (rc == -EIO) | 665 | if (gp_rreq_fail) |
638 | printk(KERN_ERR PFX | 666 | printk(KERN_ERR PFX |
639 | "I/O Address 0x%04x and 0x%04x" | 667 | "I/O Address 0x%04x and 0x%04x" |
640 | " already in use\n", base, CIR_BASE); | 668 | " already in use\n", base, CIR_BASE); |
@@ -646,21 +674,16 @@ static int __init it87_wdt_init(void) | |||
646 | goto err_out; | 674 | goto err_out; |
647 | } | 675 | } |
648 | base = CIR_BASE; | 676 | base = CIR_BASE; |
649 | spin_lock_irqsave(&spinlock, flags); | ||
650 | superio_enter(); | ||
651 | 677 | ||
652 | superio_select(CIR); | 678 | superio_select(CIR); |
653 | superio_outw(base, BASEREG); | 679 | superio_outw(base, BASEREG); |
654 | superio_outb(0x00, CIR_ILS); | 680 | superio_outb(0x00, CIR_ILS); |
655 | ciract = superio_inb(ACTREG); | 681 | ciract = superio_inb(ACTREG); |
656 | superio_outb(0x01, ACTREG); | 682 | superio_outb(0x01, ACTREG); |
657 | if (rc == -EIO) { | 683 | if (gp_rreq_fail) { |
658 | superio_select(GAMEPORT); | 684 | superio_select(GAMEPORT); |
659 | superio_outb(gpact, ACTREG); | 685 | superio_outb(gpact, ACTREG); |
660 | } | 686 | } |
661 | |||
662 | superio_exit(); | ||
663 | spin_unlock_irqrestore(&spinlock, flags); | ||
664 | } | 687 | } |
665 | 688 | ||
666 | if (timeout < 1 || timeout > max_units * 60) { | 689 | if (timeout < 1 || timeout > max_units * 60) { |
@@ -704,6 +727,7 @@ static int __init it87_wdt_init(void) | |||
704 | "nogameport=%d)\n", chip_type, chip_rev, timeout, | 727 | "nogameport=%d)\n", chip_type, chip_rev, timeout, |
705 | nowayout, testmode, exclusive, nogameport); | 728 | nowayout, testmode, exclusive, nogameport); |
706 | 729 | ||
730 | superio_exit(); | ||
707 | return 0; | 731 | return 0; |
708 | 732 | ||
709 | err_out_reboot: | 733 | err_out_reboot: |
@@ -711,49 +735,37 @@ err_out_reboot: | |||
711 | err_out_region: | 735 | err_out_region: |
712 | release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); | 736 | release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); |
713 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { | 737 | if (!test_bit(WDTS_USE_GP, &wdt_status)) { |
714 | spin_lock_irqsave(&spinlock, flags); | ||
715 | superio_enter(); | ||
716 | superio_select(CIR); | 738 | superio_select(CIR); |
717 | superio_outb(ciract, ACTREG); | 739 | superio_outb(ciract, ACTREG); |
718 | superio_exit(); | ||
719 | spin_unlock_irqrestore(&spinlock, flags); | ||
720 | } | 740 | } |
721 | err_out: | 741 | err_out: |
722 | if (try_gameport) { | 742 | if (try_gameport) { |
723 | spin_lock_irqsave(&spinlock, flags); | ||
724 | superio_enter(); | ||
725 | superio_select(GAMEPORT); | 743 | superio_select(GAMEPORT); |
726 | superio_outb(gpact, ACTREG); | 744 | superio_outb(gpact, ACTREG); |
727 | superio_exit(); | ||
728 | spin_unlock_irqrestore(&spinlock, flags); | ||
729 | } | 745 | } |
730 | 746 | ||
747 | superio_exit(); | ||
731 | return rc; | 748 | return rc; |
732 | } | 749 | } |
733 | 750 | ||
734 | static void __exit it87_wdt_exit(void) | 751 | static void __exit it87_wdt_exit(void) |
735 | { | 752 | { |
736 | unsigned long flags; | 753 | if (superio_enter() == 0) { |
737 | int nolock; | 754 | superio_select(GPIO); |
738 | 755 | superio_outb(0x00, WDTCTRL); | |
739 | nolock = !spin_trylock_irqsave(&spinlock, flags); | 756 | superio_outb(0x00, WDTCFG); |
740 | superio_enter(); | 757 | superio_outb(0x00, WDTVALLSB); |
741 | superio_select(GPIO); | 758 | if (max_units > 255) |
742 | superio_outb(0x00, WDTCTRL); | 759 | superio_outb(0x00, WDTVALMSB); |
743 | superio_outb(0x00, WDTCFG); | 760 | if (test_bit(WDTS_USE_GP, &wdt_status)) { |
744 | superio_outb(0x00, WDTVALLSB); | 761 | superio_select(GAMEPORT); |
745 | if (max_units > 255) | 762 | superio_outb(gpact, ACTREG); |
746 | superio_outb(0x00, WDTVALMSB); | 763 | } else { |
747 | if (test_bit(WDTS_USE_GP, &wdt_status)) { | 764 | superio_select(CIR); |
748 | superio_select(GAMEPORT); | 765 | superio_outb(ciract, ACTREG); |
749 | superio_outb(gpact, ACTREG); | 766 | } |
750 | } else { | 767 | superio_exit(); |
751 | superio_select(CIR); | ||
752 | superio_outb(ciract, ACTREG); | ||
753 | } | 768 | } |
754 | superio_exit(); | ||
755 | if (!nolock) | ||
756 | spin_unlock_irqrestore(&spinlock, flags); | ||
757 | 769 | ||
758 | misc_deregister(&wdt_miscdev); | 770 | misc_deregister(&wdt_miscdev); |
759 | unregister_reboot_notifier(&wdt_notifier); | 771 | unregister_reboot_notifier(&wdt_notifier); |
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c index 2b4af222b5f2..4dc31024d26c 100644 --- a/drivers/watchdog/mpcore_wdt.c +++ b/drivers/watchdog/mpcore_wdt.c | |||
@@ -407,12 +407,35 @@ static int __devexit mpcore_wdt_remove(struct platform_device *dev) | |||
407 | return 0; | 407 | return 0; |
408 | } | 408 | } |
409 | 409 | ||
410 | #ifdef CONFIG_PM | ||
411 | static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg) | ||
412 | { | ||
413 | struct mpcore_wdt *wdt = platform_get_drvdata(dev); | ||
414 | mpcore_wdt_stop(wdt); /* Turn the WDT off */ | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static int mpcore_wdt_resume(struct platform_device *dev) | ||
419 | { | ||
420 | struct mpcore_wdt *wdt = platform_get_drvdata(dev); | ||
421 | /* re-activate timer */ | ||
422 | if (test_bit(0, &wdt->timer_alive)) | ||
423 | mpcore_wdt_start(wdt); | ||
424 | return 0; | ||
425 | } | ||
426 | #else | ||
427 | #define mpcore_wdt_suspend NULL | ||
428 | #define mpcore_wdt_resume NULL | ||
429 | #endif | ||
430 | |||
410 | /* work with hotplug and coldplug */ | 431 | /* work with hotplug and coldplug */ |
411 | MODULE_ALIAS("platform:mpcore_wdt"); | 432 | MODULE_ALIAS("platform:mpcore_wdt"); |
412 | 433 | ||
413 | static struct platform_driver mpcore_wdt_driver = { | 434 | static struct platform_driver mpcore_wdt_driver = { |
414 | .probe = mpcore_wdt_probe, | 435 | .probe = mpcore_wdt_probe, |
415 | .remove = __devexit_p(mpcore_wdt_remove), | 436 | .remove = __devexit_p(mpcore_wdt_remove), |
437 | .suspend = mpcore_wdt_suspend, | ||
438 | .resume = mpcore_wdt_resume, | ||
416 | .shutdown = mpcore_wdt_shutdown, | 439 | .shutdown = mpcore_wdt_shutdown, |
417 | .driver = { | 440 | .driver = { |
418 | .owner = THIS_MODULE, | 441 | .owner = THIS_MODULE, |
diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 0430e093b1a0..ac37bb82392c 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c | |||
@@ -225,11 +225,11 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev) | |||
225 | 225 | ||
226 | ret = misc_register(&mtx1_wdt_misc); | 226 | ret = misc_register(&mtx1_wdt_misc); |
227 | if (ret < 0) { | 227 | if (ret < 0) { |
228 | printk(KERN_ERR " mtx-1_wdt : failed to register\n"); | 228 | dev_err(&pdev->dev, "failed to register\n"); |
229 | return ret; | 229 | return ret; |
230 | } | 230 | } |
231 | mtx1_wdt_start(); | 231 | mtx1_wdt_start(); |
232 | printk(KERN_INFO "MTX-1 Watchdog driver\n"); | 232 | dev_info(&pdev->dev, "MTX-1 Watchdog driver\n"); |
233 | return 0; | 233 | return 0; |
234 | } | 234 | } |
235 | 235 | ||
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c new file mode 100644 index 000000000000..4ec741ac952c --- /dev/null +++ b/drivers/watchdog/of_xilinx_wdt.c | |||
@@ -0,0 +1,433 @@ | |||
1 | /* | ||
2 | * of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt | ||
3 | * | ||
4 | * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) | ||
5 | * | ||
6 | * ----------------------- | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version | ||
11 | * 2 of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * ----------------------- | ||
14 | * 30-May-2011 Alejandro Cabrera <aldaya@gmail.com> | ||
15 | * - If "xlnx,wdt-enable-once" wasn't found on device tree the | ||
16 | * module will use CONFIG_WATCHDOG_NOWAYOUT | ||
17 | * - If the device tree parameters ("clock-frequency" and | ||
18 | * "xlnx,wdt-interval") wasn't found the driver won't | ||
19 | * know the wdt reset interval | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/miscdevice.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/watchdog.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/uaccess.h> | ||
32 | #include <linux/of.h> | ||
33 | #include <linux/of_device.h> | ||
34 | #include <linux/of_address.h> | ||
35 | |||
36 | /* Register offsets for the Wdt device */ | ||
37 | #define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */ | ||
38 | #define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */ | ||
39 | #define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */ | ||
40 | |||
41 | /* Control/Status Register Masks */ | ||
42 | #define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */ | ||
43 | #define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */ | ||
44 | #define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */ | ||
45 | |||
46 | /* Control/Status Register 0/1 bits */ | ||
47 | #define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */ | ||
48 | |||
49 | /* SelfTest constants */ | ||
50 | #define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000 | ||
51 | #define XWT_TIMER_FAILED 0xFFFFFFFF | ||
52 | |||
53 | #define WATCHDOG_NAME "Xilinx Watchdog" | ||
54 | #define PFX WATCHDOG_NAME ": " | ||
55 | |||
56 | struct xwdt_device { | ||
57 | struct resource res; | ||
58 | void __iomem *base; | ||
59 | u32 nowayout; | ||
60 | u32 wdt_interval; | ||
61 | u32 boot_status; | ||
62 | }; | ||
63 | |||
64 | static struct xwdt_device xdev; | ||
65 | |||
66 | static u32 timeout; | ||
67 | static u32 control_status_reg; | ||
68 | static u8 expect_close; | ||
69 | static u8 no_timeout; | ||
70 | static unsigned long driver_open; | ||
71 | |||
72 | static DEFINE_SPINLOCK(spinlock); | ||
73 | |||
74 | static void xwdt_start(void) | ||
75 | { | ||
76 | spin_lock(&spinlock); | ||
77 | |||
78 | /* Clean previous status and enable the watchdog timer */ | ||
79 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
80 | control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); | ||
81 | |||
82 | iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), | ||
83 | xdev.base + XWT_TWCSR0_OFFSET); | ||
84 | |||
85 | iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); | ||
86 | |||
87 | spin_unlock(&spinlock); | ||
88 | } | ||
89 | |||
90 | static void xwdt_stop(void) | ||
91 | { | ||
92 | spin_lock(&spinlock); | ||
93 | |||
94 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
95 | |||
96 | iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), | ||
97 | xdev.base + XWT_TWCSR0_OFFSET); | ||
98 | |||
99 | iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); | ||
100 | |||
101 | spin_unlock(&spinlock); | ||
102 | printk(KERN_INFO PFX "Stopped!\n"); | ||
103 | } | ||
104 | |||
105 | static void xwdt_keepalive(void) | ||
106 | { | ||
107 | spin_lock(&spinlock); | ||
108 | |||
109 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
110 | control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); | ||
111 | iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); | ||
112 | |||
113 | spin_unlock(&spinlock); | ||
114 | } | ||
115 | |||
116 | static void xwdt_get_status(int *status) | ||
117 | { | ||
118 | int new_status; | ||
119 | |||
120 | spin_lock(&spinlock); | ||
121 | |||
122 | control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); | ||
123 | new_status = ((control_status_reg & | ||
124 | (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); | ||
125 | spin_unlock(&spinlock); | ||
126 | |||
127 | *status = 0; | ||
128 | if (new_status & 1) | ||
129 | *status |= WDIOF_CARDRESET; | ||
130 | } | ||
131 | |||
132 | static u32 xwdt_selftest(void) | ||
133 | { | ||
134 | int i; | ||
135 | u32 timer_value1; | ||
136 | u32 timer_value2; | ||
137 | |||
138 | spin_lock(&spinlock); | ||
139 | |||
140 | timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); | ||
141 | timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); | ||
142 | |||
143 | for (i = 0; | ||
144 | ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && | ||
145 | (timer_value2 == timer_value1)); i++) { | ||
146 | timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); | ||
147 | } | ||
148 | |||
149 | spin_unlock(&spinlock); | ||
150 | |||
151 | if (timer_value2 != timer_value1) | ||
152 | return ~XWT_TIMER_FAILED; | ||
153 | else | ||
154 | return XWT_TIMER_FAILED; | ||
155 | } | ||
156 | |||
157 | static int xwdt_open(struct inode *inode, struct file *file) | ||
158 | { | ||
159 | /* Only one process can handle the wdt at a time */ | ||
160 | if (test_and_set_bit(0, &driver_open)) | ||
161 | return -EBUSY; | ||
162 | |||
163 | /* Make sure that the module are always loaded...*/ | ||
164 | if (xdev.nowayout) | ||
165 | __module_get(THIS_MODULE); | ||
166 | |||
167 | xwdt_start(); | ||
168 | printk(KERN_INFO PFX "Started...\n"); | ||
169 | |||
170 | return nonseekable_open(inode, file); | ||
171 | } | ||
172 | |||
173 | static int xwdt_release(struct inode *inode, struct file *file) | ||
174 | { | ||
175 | if (expect_close == 42) { | ||
176 | xwdt_stop(); | ||
177 | } else { | ||
178 | printk(KERN_CRIT PFX | ||
179 | "Unexpected close, not stopping watchdog!\n"); | ||
180 | xwdt_keepalive(); | ||
181 | } | ||
182 | |||
183 | clear_bit(0, &driver_open); | ||
184 | expect_close = 0; | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * xwdt_write: | ||
190 | * @file: file handle to the watchdog | ||
191 | * @buf: buffer to write (unused as data does not matter here | ||
192 | * @count: count of bytes | ||
193 | * @ppos: pointer to the position to write. No seeks allowed | ||
194 | * | ||
195 | * A write to a watchdog device is defined as a keepalive signal. Any | ||
196 | * write of data will do, as we don't define content meaning. | ||
197 | */ | ||
198 | static ssize_t xwdt_write(struct file *file, const char __user *buf, | ||
199 | size_t len, loff_t *ppos) | ||
200 | { | ||
201 | if (len) { | ||
202 | if (!xdev.nowayout) { | ||
203 | size_t i; | ||
204 | |||
205 | /* In case it was set long ago */ | ||
206 | expect_close = 0; | ||
207 | |||
208 | for (i = 0; i != len; i++) { | ||
209 | char c; | ||
210 | |||
211 | if (get_user(c, buf + i)) | ||
212 | return -EFAULT; | ||
213 | if (c == 'V') | ||
214 | expect_close = 42; | ||
215 | } | ||
216 | } | ||
217 | xwdt_keepalive(); | ||
218 | } | ||
219 | return len; | ||
220 | } | ||
221 | |||
222 | static const struct watchdog_info ident = { | ||
223 | .options = WDIOF_MAGICCLOSE | | ||
224 | WDIOF_KEEPALIVEPING, | ||
225 | .firmware_version = 1, | ||
226 | .identity = WATCHDOG_NAME, | ||
227 | }; | ||
228 | |||
229 | /* | ||
230 | * xwdt_ioctl: | ||
231 | * @file: file handle to the device | ||
232 | * @cmd: watchdog command | ||
233 | * @arg: argument pointer | ||
234 | * | ||
235 | * The watchdog API defines a common set of functions for all watchdogs | ||
236 | * according to their available features. | ||
237 | */ | ||
238 | static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
239 | { | ||
240 | int status; | ||
241 | |||
242 | union { | ||
243 | struct watchdog_info __user *ident; | ||
244 | int __user *i; | ||
245 | } uarg; | ||
246 | |||
247 | uarg.i = (int __user *)arg; | ||
248 | |||
249 | switch (cmd) { | ||
250 | case WDIOC_GETSUPPORT: | ||
251 | return copy_to_user(uarg.ident, &ident, | ||
252 | sizeof(ident)) ? -EFAULT : 0; | ||
253 | |||
254 | case WDIOC_GETBOOTSTATUS: | ||
255 | return put_user(xdev.boot_status, uarg.i); | ||
256 | |||
257 | case WDIOC_GETSTATUS: | ||
258 | xwdt_get_status(&status); | ||
259 | return put_user(status, uarg.i); | ||
260 | |||
261 | case WDIOC_KEEPALIVE: | ||
262 | xwdt_keepalive(); | ||
263 | return 0; | ||
264 | |||
265 | case WDIOC_GETTIMEOUT: | ||
266 | if (no_timeout) | ||
267 | return -ENOTTY; | ||
268 | else | ||
269 | return put_user(timeout, uarg.i); | ||
270 | |||
271 | default: | ||
272 | return -ENOTTY; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | static const struct file_operations xwdt_fops = { | ||
277 | .owner = THIS_MODULE, | ||
278 | .llseek = no_llseek, | ||
279 | .write = xwdt_write, | ||
280 | .open = xwdt_open, | ||
281 | .release = xwdt_release, | ||
282 | .unlocked_ioctl = xwdt_ioctl, | ||
283 | }; | ||
284 | |||
285 | static struct miscdevice xwdt_miscdev = { | ||
286 | .minor = WATCHDOG_MINOR, | ||
287 | .name = "watchdog", | ||
288 | .fops = &xwdt_fops, | ||
289 | }; | ||
290 | |||
291 | static int __devinit xwdt_probe(struct platform_device *pdev) | ||
292 | { | ||
293 | int rc; | ||
294 | u32 *tmptr; | ||
295 | u32 *pfreq; | ||
296 | |||
297 | no_timeout = 0; | ||
298 | |||
299 | pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent, | ||
300 | "clock-frequency", NULL); | ||
301 | |||
302 | if (pfreq == NULL) { | ||
303 | printk(KERN_WARNING PFX | ||
304 | "The watchdog clock frequency cannot be obtained!\n"); | ||
305 | no_timeout = 1; | ||
306 | } | ||
307 | |||
308 | rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); | ||
309 | if (rc) { | ||
310 | printk(KERN_WARNING PFX "invalid address!\n"); | ||
311 | return rc; | ||
312 | } | ||
313 | |||
314 | tmptr = (u32 *)of_get_property(pdev->dev.of_node, | ||
315 | "xlnx,wdt-interval", NULL); | ||
316 | if (tmptr == NULL) { | ||
317 | printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\"" | ||
318 | " not found in device tree!\n"); | ||
319 | no_timeout = 1; | ||
320 | } else { | ||
321 | xdev.wdt_interval = *tmptr; | ||
322 | } | ||
323 | |||
324 | tmptr = (u32 *)of_get_property(pdev->dev.of_node, | ||
325 | "xlnx,wdt-enable-once", NULL); | ||
326 | if (tmptr == NULL) { | ||
327 | printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\"" | ||
328 | " not found in device tree!\n"); | ||
329 | xdev.nowayout = WATCHDOG_NOWAYOUT; | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * Twice of the 2^wdt_interval / freq because the first wdt overflow is | ||
334 | * ignored (interrupt), reset is only generated at second wdt overflow | ||
335 | */ | ||
336 | if (!no_timeout) | ||
337 | timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); | ||
338 | |||
339 | if (!request_mem_region(xdev.res.start, | ||
340 | xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { | ||
341 | rc = -ENXIO; | ||
342 | printk(KERN_ERR PFX "memory request failure!\n"); | ||
343 | goto err_out; | ||
344 | } | ||
345 | |||
346 | xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); | ||
347 | if (xdev.base == NULL) { | ||
348 | rc = -ENOMEM; | ||
349 | printk(KERN_ERR PFX "ioremap failure!\n"); | ||
350 | goto release_mem; | ||
351 | } | ||
352 | |||
353 | rc = xwdt_selftest(); | ||
354 | if (rc == XWT_TIMER_FAILED) { | ||
355 | printk(KERN_ERR PFX "SelfTest routine error!\n"); | ||
356 | goto unmap_io; | ||
357 | } | ||
358 | |||
359 | xwdt_get_status(&xdev.boot_status); | ||
360 | |||
361 | rc = misc_register(&xwdt_miscdev); | ||
362 | if (rc) { | ||
363 | printk(KERN_ERR PFX | ||
364 | "cannot register miscdev on minor=%d (err=%d)\n", | ||
365 | xwdt_miscdev.minor, rc); | ||
366 | goto unmap_io; | ||
367 | } | ||
368 | |||
369 | if (no_timeout) | ||
370 | printk(KERN_INFO PFX | ||
371 | "driver loaded (timeout=? sec, nowayout=%d)\n", | ||
372 | xdev.nowayout); | ||
373 | else | ||
374 | printk(KERN_INFO PFX | ||
375 | "driver loaded (timeout=%d sec, nowayout=%d)\n", | ||
376 | timeout, xdev.nowayout); | ||
377 | |||
378 | expect_close = 0; | ||
379 | clear_bit(0, &driver_open); | ||
380 | |||
381 | return 0; | ||
382 | |||
383 | unmap_io: | ||
384 | iounmap(xdev.base); | ||
385 | release_mem: | ||
386 | release_mem_region(xdev.res.start, resource_size(&xdev.res)); | ||
387 | err_out: | ||
388 | return rc; | ||
389 | } | ||
390 | |||
391 | static int __devexit xwdt_remove(struct platform_device *dev) | ||
392 | { | ||
393 | misc_deregister(&xwdt_miscdev); | ||
394 | iounmap(xdev.base); | ||
395 | release_mem_region(xdev.res.start, resource_size(&xdev.res)); | ||
396 | |||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | /* Match table for of_platform binding */ | ||
401 | static struct of_device_id __devinitdata xwdt_of_match[] = { | ||
402 | { .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, | ||
403 | {}, | ||
404 | }; | ||
405 | MODULE_DEVICE_TABLE(of, xwdt_of_match); | ||
406 | |||
407 | static struct platform_driver xwdt_driver = { | ||
408 | .probe = xwdt_probe, | ||
409 | .remove = __devexit_p(xwdt_remove), | ||
410 | .driver = { | ||
411 | .owner = THIS_MODULE, | ||
412 | .name = WATCHDOG_NAME, | ||
413 | .of_match_table = xwdt_of_match, | ||
414 | }, | ||
415 | }; | ||
416 | |||
417 | static int __init xwdt_init(void) | ||
418 | { | ||
419 | return platform_driver_register(&xwdt_driver); | ||
420 | } | ||
421 | |||
422 | static void __exit xwdt_exit(void) | ||
423 | { | ||
424 | platform_driver_unregister(&xwdt_driver); | ||
425 | } | ||
426 | |||
427 | module_init(xwdt_init); | ||
428 | module_exit(xwdt_exit); | ||
429 | |||
430 | MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); | ||
431 | MODULE_DESCRIPTION("Xilinx Watchdog driver"); | ||
432 | MODULE_LICENSE("GPL"); | ||
433 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index b7c139051575..e78d89986768 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c | |||
@@ -56,6 +56,7 @@ | |||
56 | #define IO_DEFAULT 0x2E /* Address used on Portwell Boards */ | 56 | #define IO_DEFAULT 0x2E /* Address used on Portwell Boards */ |
57 | 57 | ||
58 | static int io = IO_DEFAULT; | 58 | static int io = IO_DEFAULT; |
59 | static int swc_base_addr = -1; | ||
59 | 60 | ||
60 | static int timeout = DEFAULT_TIMEOUT; /* timeout value */ | 61 | static int timeout = DEFAULT_TIMEOUT; /* timeout value */ |
61 | static unsigned long timer_enabled; /* is the timer enabled? */ | 62 | static unsigned long timer_enabled; /* is the timer enabled? */ |
@@ -116,9 +117,8 @@ static inline void pc87413_enable_swc(void) | |||
116 | 117 | ||
117 | /* Read SWC I/O base address */ | 118 | /* Read SWC I/O base address */ |
118 | 119 | ||
119 | static inline unsigned int pc87413_get_swc_base(void) | 120 | static void pc87413_get_swc_base_addr(void) |
120 | { | 121 | { |
121 | unsigned int swc_base_addr = 0; | ||
122 | unsigned char addr_l, addr_h = 0; | 122 | unsigned char addr_l, addr_h = 0; |
123 | 123 | ||
124 | /* Step 3: Read SWC I/O Base Address */ | 124 | /* Step 3: Read SWC I/O Base Address */ |
@@ -136,12 +136,11 @@ static inline unsigned int pc87413_get_swc_base(void) | |||
136 | "Read SWC I/O Base Address: low %d, high %d, res %d\n", | 136 | "Read SWC I/O Base Address: low %d, high %d, res %d\n", |
137 | addr_l, addr_h, swc_base_addr); | 137 | addr_l, addr_h, swc_base_addr); |
138 | #endif | 138 | #endif |
139 | return swc_base_addr; | ||
140 | } | 139 | } |
141 | 140 | ||
142 | /* Select Bank 3 of SWC */ | 141 | /* Select Bank 3 of SWC */ |
143 | 142 | ||
144 | static inline void pc87413_swc_bank3(unsigned int swc_base_addr) | 143 | static inline void pc87413_swc_bank3(void) |
145 | { | 144 | { |
146 | /* Step 4: Select Bank3 of SWC */ | 145 | /* Step 4: Select Bank3 of SWC */ |
147 | outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); | 146 | outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); |
@@ -152,8 +151,7 @@ static inline void pc87413_swc_bank3(unsigned int swc_base_addr) | |||
152 | 151 | ||
153 | /* Set watchdog timeout to x minutes */ | 152 | /* Set watchdog timeout to x minutes */ |
154 | 153 | ||
155 | static inline void pc87413_programm_wdto(unsigned int swc_base_addr, | 154 | static inline void pc87413_programm_wdto(char pc87413_time) |
156 | char pc87413_time) | ||
157 | { | 155 | { |
158 | /* Step 5: Programm WDTO, Twd. */ | 156 | /* Step 5: Programm WDTO, Twd. */ |
159 | outb_p(pc87413_time, swc_base_addr + WDTO); | 157 | outb_p(pc87413_time, swc_base_addr + WDTO); |
@@ -164,7 +162,7 @@ static inline void pc87413_programm_wdto(unsigned int swc_base_addr, | |||
164 | 162 | ||
165 | /* Enable WDEN */ | 163 | /* Enable WDEN */ |
166 | 164 | ||
167 | static inline void pc87413_enable_wden(unsigned int swc_base_addr) | 165 | static inline void pc87413_enable_wden(void) |
168 | { | 166 | { |
169 | /* Step 6: Enable WDEN */ | 167 | /* Step 6: Enable WDEN */ |
170 | outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); | 168 | outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); |
@@ -174,7 +172,7 @@ static inline void pc87413_enable_wden(unsigned int swc_base_addr) | |||
174 | } | 172 | } |
175 | 173 | ||
176 | /* Enable SW_WD_TREN */ | 174 | /* Enable SW_WD_TREN */ |
177 | static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) | 175 | static inline void pc87413_enable_sw_wd_tren(void) |
178 | { | 176 | { |
179 | /* Enable SW_WD_TREN */ | 177 | /* Enable SW_WD_TREN */ |
180 | outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); | 178 | outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); |
@@ -185,7 +183,7 @@ static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) | |||
185 | 183 | ||
186 | /* Disable SW_WD_TREN */ | 184 | /* Disable SW_WD_TREN */ |
187 | 185 | ||
188 | static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) | 186 | static inline void pc87413_disable_sw_wd_tren(void) |
189 | { | 187 | { |
190 | /* Disable SW_WD_TREN */ | 188 | /* Disable SW_WD_TREN */ |
191 | outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); | 189 | outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); |
@@ -196,7 +194,7 @@ static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) | |||
196 | 194 | ||
197 | /* Enable SW_WD_TRG */ | 195 | /* Enable SW_WD_TRG */ |
198 | 196 | ||
199 | static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) | 197 | static inline void pc87413_enable_sw_wd_trg(void) |
200 | { | 198 | { |
201 | /* Enable SW_WD_TRG */ | 199 | /* Enable SW_WD_TRG */ |
202 | outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); | 200 | outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); |
@@ -207,7 +205,7 @@ static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) | |||
207 | 205 | ||
208 | /* Disable SW_WD_TRG */ | 206 | /* Disable SW_WD_TRG */ |
209 | 207 | ||
210 | static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) | 208 | static inline void pc87413_disable_sw_wd_trg(void) |
211 | { | 209 | { |
212 | /* Disable SW_WD_TRG */ | 210 | /* Disable SW_WD_TRG */ |
213 | outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); | 211 | outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); |
@@ -222,18 +220,13 @@ static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) | |||
222 | 220 | ||
223 | static void pc87413_enable(void) | 221 | static void pc87413_enable(void) |
224 | { | 222 | { |
225 | unsigned int swc_base_addr; | ||
226 | |||
227 | spin_lock(&io_lock); | 223 | spin_lock(&io_lock); |
228 | 224 | ||
229 | pc87413_select_wdt_out(); | 225 | pc87413_swc_bank3(); |
230 | pc87413_enable_swc(); | 226 | pc87413_programm_wdto(timeout); |
231 | swc_base_addr = pc87413_get_swc_base(); | 227 | pc87413_enable_wden(); |
232 | pc87413_swc_bank3(swc_base_addr); | 228 | pc87413_enable_sw_wd_tren(); |
233 | pc87413_programm_wdto(swc_base_addr, timeout); | 229 | pc87413_enable_sw_wd_trg(); |
234 | pc87413_enable_wden(swc_base_addr); | ||
235 | pc87413_enable_sw_wd_tren(swc_base_addr); | ||
236 | pc87413_enable_sw_wd_trg(swc_base_addr); | ||
237 | 230 | ||
238 | spin_unlock(&io_lock); | 231 | spin_unlock(&io_lock); |
239 | } | 232 | } |
@@ -242,17 +235,12 @@ static void pc87413_enable(void) | |||
242 | 235 | ||
243 | static void pc87413_disable(void) | 236 | static void pc87413_disable(void) |
244 | { | 237 | { |
245 | unsigned int swc_base_addr; | ||
246 | |||
247 | spin_lock(&io_lock); | 238 | spin_lock(&io_lock); |
248 | 239 | ||
249 | pc87413_select_wdt_out(); | 240 | pc87413_swc_bank3(); |
250 | pc87413_enable_swc(); | 241 | pc87413_disable_sw_wd_tren(); |
251 | swc_base_addr = pc87413_get_swc_base(); | 242 | pc87413_disable_sw_wd_trg(); |
252 | pc87413_swc_bank3(swc_base_addr); | 243 | pc87413_programm_wdto(0); |
253 | pc87413_disable_sw_wd_tren(swc_base_addr); | ||
254 | pc87413_disable_sw_wd_trg(swc_base_addr); | ||
255 | pc87413_programm_wdto(swc_base_addr, 0); | ||
256 | 244 | ||
257 | spin_unlock(&io_lock); | 245 | spin_unlock(&io_lock); |
258 | } | 246 | } |
@@ -261,20 +249,15 @@ static void pc87413_disable(void) | |||
261 | 249 | ||
262 | static void pc87413_refresh(void) | 250 | static void pc87413_refresh(void) |
263 | { | 251 | { |
264 | unsigned int swc_base_addr; | ||
265 | |||
266 | spin_lock(&io_lock); | 252 | spin_lock(&io_lock); |
267 | 253 | ||
268 | pc87413_select_wdt_out(); | 254 | pc87413_swc_bank3(); |
269 | pc87413_enable_swc(); | 255 | pc87413_disable_sw_wd_tren(); |
270 | swc_base_addr = pc87413_get_swc_base(); | 256 | pc87413_disable_sw_wd_trg(); |
271 | pc87413_swc_bank3(swc_base_addr); | 257 | pc87413_programm_wdto(timeout); |
272 | pc87413_disable_sw_wd_tren(swc_base_addr); | 258 | pc87413_enable_wden(); |
273 | pc87413_disable_sw_wd_trg(swc_base_addr); | 259 | pc87413_enable_sw_wd_tren(); |
274 | pc87413_programm_wdto(swc_base_addr, timeout); | 260 | pc87413_enable_sw_wd_trg(); |
275 | pc87413_enable_wden(swc_base_addr); | ||
276 | pc87413_enable_sw_wd_tren(swc_base_addr); | ||
277 | pc87413_enable_sw_wd_trg(swc_base_addr); | ||
278 | 261 | ||
279 | spin_unlock(&io_lock); | 262 | spin_unlock(&io_lock); |
280 | } | 263 | } |
@@ -528,7 +511,8 @@ static int __init pc87413_init(void) | |||
528 | printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", | 511 | printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", |
529 | WDT_INDEX_IO_PORT); | 512 | WDT_INDEX_IO_PORT); |
530 | 513 | ||
531 | /* request_region(io, 2, "pc87413"); */ | 514 | if (!request_muxed_region(io, 2, MODNAME)) |
515 | return -EBUSY; | ||
532 | 516 | ||
533 | ret = register_reboot_notifier(&pc87413_notifier); | 517 | ret = register_reboot_notifier(&pc87413_notifier); |
534 | if (ret != 0) { | 518 | if (ret != 0) { |
@@ -541,12 +525,32 @@ static int __init pc87413_init(void) | |||
541 | printk(KERN_ERR PFX | 525 | printk(KERN_ERR PFX |
542 | "cannot register miscdev on minor=%d (err=%d)\n", | 526 | "cannot register miscdev on minor=%d (err=%d)\n", |
543 | WATCHDOG_MINOR, ret); | 527 | WATCHDOG_MINOR, ret); |
544 | unregister_reboot_notifier(&pc87413_notifier); | 528 | goto reboot_unreg; |
545 | return ret; | ||
546 | } | 529 | } |
547 | printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); | 530 | printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); |
531 | |||
532 | pc87413_select_wdt_out(); | ||
533 | pc87413_enable_swc(); | ||
534 | pc87413_get_swc_base_addr(); | ||
535 | |||
536 | if (!request_region(swc_base_addr, 0x20, MODNAME)) { | ||
537 | printk(KERN_ERR PFX | ||
538 | "cannot request SWC region at 0x%x\n", swc_base_addr); | ||
539 | ret = -EBUSY; | ||
540 | goto misc_unreg; | ||
541 | } | ||
542 | |||
548 | pc87413_enable(); | 543 | pc87413_enable(); |
544 | |||
545 | release_region(io, 2); | ||
549 | return 0; | 546 | return 0; |
547 | |||
548 | misc_unreg: | ||
549 | misc_deregister(&pc87413_miscdev); | ||
550 | reboot_unreg: | ||
551 | unregister_reboot_notifier(&pc87413_notifier); | ||
552 | release_region(io, 2); | ||
553 | return ret; | ||
550 | } | 554 | } |
551 | 555 | ||
552 | /** | 556 | /** |
@@ -569,7 +573,7 @@ static void __exit pc87413_exit(void) | |||
569 | 573 | ||
570 | misc_deregister(&pc87413_miscdev); | 574 | misc_deregister(&pc87413_miscdev); |
571 | unregister_reboot_notifier(&pc87413_notifier); | 575 | unregister_reboot_notifier(&pc87413_notifier); |
572 | /* release_region(io, 2); */ | 576 | release_region(swc_base_addr, 0x20); |
573 | 577 | ||
574 | printk(KERN_INFO MODNAME " watchdog component driver removed.\n"); | 578 | printk(KERN_INFO MODNAME " watchdog component driver removed.\n"); |
575 | } | 579 | } |
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index f7f5aa00df60..30da88f47cd3 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -589,6 +589,15 @@ static int s3c2410wdt_resume(struct platform_device *dev) | |||
589 | #define s3c2410wdt_resume NULL | 589 | #define s3c2410wdt_resume NULL |
590 | #endif /* CONFIG_PM */ | 590 | #endif /* CONFIG_PM */ |
591 | 591 | ||
592 | #ifdef CONFIG_OF | ||
593 | static const struct of_device_id s3c2410_wdt_match[] = { | ||
594 | { .compatible = "samsung,s3c2410-wdt" }, | ||
595 | {}, | ||
596 | }; | ||
597 | MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); | ||
598 | #else | ||
599 | #define s3c2410_wdt_match NULL | ||
600 | #endif | ||
592 | 601 | ||
593 | static struct platform_driver s3c2410wdt_driver = { | 602 | static struct platform_driver s3c2410wdt_driver = { |
594 | .probe = s3c2410wdt_probe, | 603 | .probe = s3c2410wdt_probe, |
@@ -599,6 +608,7 @@ static struct platform_driver s3c2410wdt_driver = { | |||
599 | .driver = { | 608 | .driver = { |
600 | .owner = THIS_MODULE, | 609 | .owner = THIS_MODULE, |
601 | .name = "s3c2410-wdt", | 610 | .name = "s3c2410-wdt", |
611 | .of_match_table = s3c2410_wdt_match, | ||
602 | }, | 612 | }, |
603 | }; | 613 | }; |
604 | 614 | ||
diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index c7cf4b01f58d..029467e34636 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c | |||
@@ -472,15 +472,10 @@ static void sch311x_wdt_shutdown(struct platform_device *dev) | |||
472 | sch311x_wdt_stop(); | 472 | sch311x_wdt_stop(); |
473 | } | 473 | } |
474 | 474 | ||
475 | #define sch311x_wdt_suspend NULL | ||
476 | #define sch311x_wdt_resume NULL | ||
477 | |||
478 | static struct platform_driver sch311x_wdt_driver = { | 475 | static struct platform_driver sch311x_wdt_driver = { |
479 | .probe = sch311x_wdt_probe, | 476 | .probe = sch311x_wdt_probe, |
480 | .remove = __devexit_p(sch311x_wdt_remove), | 477 | .remove = __devexit_p(sch311x_wdt_remove), |
481 | .shutdown = sch311x_wdt_shutdown, | 478 | .shutdown = sch311x_wdt_shutdown, |
482 | .suspend = sch311x_wdt_suspend, | ||
483 | .resume = sch311x_wdt_resume, | ||
484 | .driver = { | 479 | .driver = { |
485 | .owner = THIS_MODULE, | 480 | .owner = THIS_MODULE, |
486 | .name = DRV_NAME, | 481 | .name = DRV_NAME, |
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 0d80e08b6439..cc2cfbe33b30 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c | |||
@@ -134,6 +134,8 @@ static void wdt_enable(void) | |||
134 | writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); | 134 | writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); |
135 | writel(LOCK, wdt->base + WDTLOCK); | 135 | writel(LOCK, wdt->base + WDTLOCK); |
136 | 136 | ||
137 | /* Flush posted writes. */ | ||
138 | readl(wdt->base + WDTLOCK); | ||
137 | spin_unlock(&wdt->lock); | 139 | spin_unlock(&wdt->lock); |
138 | } | 140 | } |
139 | 141 | ||
@@ -144,9 +146,10 @@ static void wdt_disable(void) | |||
144 | 146 | ||
145 | writel(UNLOCK, wdt->base + WDTLOCK); | 147 | writel(UNLOCK, wdt->base + WDTLOCK); |
146 | writel(0, wdt->base + WDTCONTROL); | 148 | writel(0, wdt->base + WDTCONTROL); |
147 | writel(0, wdt->base + WDTLOAD); | ||
148 | writel(LOCK, wdt->base + WDTLOCK); | 149 | writel(LOCK, wdt->base + WDTLOCK); |
149 | 150 | ||
151 | /* Flush posted writes. */ | ||
152 | readl(wdt->base + WDTLOCK); | ||
150 | spin_unlock(&wdt->lock); | 153 | spin_unlock(&wdt->lock); |
151 | } | 154 | } |
152 | 155 | ||
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c new file mode 100644 index 000000000000..cfa1a1518aad --- /dev/null +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * watchdog_core.c | ||
3 | * | ||
4 | * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
5 | * All Rights Reserved. | ||
6 | * | ||
7 | * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. | ||
8 | * | ||
9 | * This source code is part of the generic code that can be used | ||
10 | * by all the watchdog timer drivers. | ||
11 | * | ||
12 | * Based on source code of the following authors: | ||
13 | * Matt Domsch <Matt_Domsch@dell.com>, | ||
14 | * Rob Radez <rob@osinvestor.com>, | ||
15 | * Rusty Lynch <rusty@linux.co.intel.com> | ||
16 | * Satyam Sharma <satyam@infradead.org> | ||
17 | * Randy Dunlap <randy.dunlap@oracle.com> | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or | ||
20 | * modify it under the terms of the GNU General Public License | ||
21 | * as published by the Free Software Foundation; either version | ||
22 | * 2 of the License, or (at your option) any later version. | ||
23 | * | ||
24 | * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. | ||
25 | * admit liability nor provide warranty for any of this software. | ||
26 | * This material is provided "AS-IS" and at no charge. | ||
27 | */ | ||
28 | |||
29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
30 | |||
31 | #include <linux/module.h> /* For EXPORT_SYMBOL/module stuff/... */ | ||
32 | #include <linux/types.h> /* For standard types */ | ||
33 | #include <linux/errno.h> /* For the -ENODEV/... values */ | ||
34 | #include <linux/kernel.h> /* For printk/panic/... */ | ||
35 | #include <linux/watchdog.h> /* For watchdog specific items */ | ||
36 | #include <linux/init.h> /* For __init/__exit/... */ | ||
37 | |||
38 | #include "watchdog_dev.h" /* For watchdog_dev_register/... */ | ||
39 | |||
40 | /** | ||
41 | * watchdog_register_device() - register a watchdog device | ||
42 | * @wdd: watchdog device | ||
43 | * | ||
44 | * Register a watchdog device with the kernel so that the | ||
45 | * watchdog timer can be accessed from userspace. | ||
46 | * | ||
47 | * A zero is returned on success and a negative errno code for | ||
48 | * failure. | ||
49 | */ | ||
50 | int watchdog_register_device(struct watchdog_device *wdd) | ||
51 | { | ||
52 | int ret; | ||
53 | |||
54 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) | ||
55 | return -EINVAL; | ||
56 | |||
57 | /* Mandatory operations need to be supported */ | ||
58 | if (wdd->ops->start == NULL || wdd->ops->stop == NULL) | ||
59 | return -EINVAL; | ||
60 | |||
61 | /* | ||
62 | * Check that we have valid min and max timeout values, if | ||
63 | * not reset them both to 0 (=not used or unknown) | ||
64 | */ | ||
65 | if (wdd->min_timeout > wdd->max_timeout) { | ||
66 | pr_info("Invalid min and max timeout values, resetting to 0!\n"); | ||
67 | wdd->min_timeout = 0; | ||
68 | wdd->max_timeout = 0; | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * Note: now that all watchdog_device data has been verified, we | ||
73 | * will not check this anymore in other functions. If data gets | ||
74 | * corrupted in a later stage then we expect a kernel panic! | ||
75 | */ | ||
76 | |||
77 | /* We only support 1 watchdog device via the /dev/watchdog interface */ | ||
78 | ret = watchdog_dev_register(wdd); | ||
79 | if (ret) { | ||
80 | pr_err("error registering /dev/watchdog (err=%d).\n", ret); | ||
81 | return ret; | ||
82 | } | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
87 | |||
88 | /** | ||
89 | * watchdog_unregister_device() - unregister a watchdog device | ||
90 | * @wdd: watchdog device to unregister | ||
91 | * | ||
92 | * Unregister a watchdog device that was previously successfully | ||
93 | * registered with watchdog_register_device(). | ||
94 | */ | ||
95 | void watchdog_unregister_device(struct watchdog_device *wdd) | ||
96 | { | ||
97 | int ret; | ||
98 | |||
99 | if (wdd == NULL) | ||
100 | return; | ||
101 | |||
102 | ret = watchdog_dev_unregister(wdd); | ||
103 | if (ret) | ||
104 | pr_err("error unregistering /dev/watchdog (err=%d).\n", ret); | ||
105 | } | ||
106 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | ||
107 | |||
108 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | ||
109 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); | ||
110 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); | ||
111 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c new file mode 100644 index 000000000000..d33520d0b4c9 --- /dev/null +++ b/drivers/watchdog/watchdog_dev.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* | ||
2 | * watchdog_dev.c | ||
3 | * | ||
4 | * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
5 | * All Rights Reserved. | ||
6 | * | ||
7 | * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. | ||
8 | * | ||
9 | * | ||
10 | * This source code is part of the generic code that can be used | ||
11 | * by all the watchdog timer drivers. | ||
12 | * | ||
13 | * This part of the generic code takes care of the following | ||
14 | * misc device: /dev/watchdog. | ||
15 | * | ||
16 | * Based on source code of the following authors: | ||
17 | * Matt Domsch <Matt_Domsch@dell.com>, | ||
18 | * Rob Radez <rob@osinvestor.com>, | ||
19 | * Rusty Lynch <rusty@linux.co.intel.com> | ||
20 | * Satyam Sharma <satyam@infradead.org> | ||
21 | * Randy Dunlap <randy.dunlap@oracle.com> | ||
22 | * | ||
23 | * This program is free software; you can redistribute it and/or | ||
24 | * modify it under the terms of the GNU General Public License | ||
25 | * as published by the Free Software Foundation; either version | ||
26 | * 2 of the License, or (at your option) any later version. | ||
27 | * | ||
28 | * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. | ||
29 | * admit liability nor provide warranty for any of this software. | ||
30 | * This material is provided "AS-IS" and at no charge. | ||
31 | */ | ||
32 | |||
33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
34 | |||
35 | #include <linux/module.h> /* For module stuff/... */ | ||
36 | #include <linux/types.h> /* For standard types (like size_t) */ | ||
37 | #include <linux/errno.h> /* For the -ENODEV/... values */ | ||
38 | #include <linux/kernel.h> /* For printk/panic/... */ | ||
39 | #include <linux/fs.h> /* For file operations */ | ||
40 | #include <linux/watchdog.h> /* For watchdog specific items */ | ||
41 | #include <linux/miscdevice.h> /* For handling misc devices */ | ||
42 | #include <linux/init.h> /* For __init/__exit/... */ | ||
43 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ | ||
44 | |||
45 | /* make sure we only register one /dev/watchdog device */ | ||
46 | static unsigned long watchdog_dev_busy; | ||
47 | /* the watchdog device behind /dev/watchdog */ | ||
48 | static struct watchdog_device *wdd; | ||
49 | |||
50 | /* | ||
51 | * watchdog_ping: ping the watchdog. | ||
52 | * @wddev: the watchdog device to ping | ||
53 | * | ||
54 | * If the watchdog has no own ping operation then it needs to be | ||
55 | * restarted via the start operation. This wrapper function does | ||
56 | * exactly that. | ||
57 | * We only ping when the watchdog device is running. | ||
58 | */ | ||
59 | |||
60 | static int watchdog_ping(struct watchdog_device *wddev) | ||
61 | { | ||
62 | if (test_bit(WDOG_ACTIVE, &wdd->status)) { | ||
63 | if (wddev->ops->ping) | ||
64 | return wddev->ops->ping(wddev); /* ping the watchdog */ | ||
65 | else | ||
66 | return wddev->ops->start(wddev); /* restart watchdog */ | ||
67 | } | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * watchdog_start: wrapper to start the watchdog. | ||
73 | * @wddev: the watchdog device to start | ||
74 | * | ||
75 | * Start the watchdog if it is not active and mark it active. | ||
76 | * This function returns zero on success or a negative errno code for | ||
77 | * failure. | ||
78 | */ | ||
79 | |||
80 | static int watchdog_start(struct watchdog_device *wddev) | ||
81 | { | ||
82 | int err; | ||
83 | |||
84 | if (!test_bit(WDOG_ACTIVE, &wdd->status)) { | ||
85 | err = wddev->ops->start(wddev); | ||
86 | if (err < 0) | ||
87 | return err; | ||
88 | |||
89 | set_bit(WDOG_ACTIVE, &wdd->status); | ||
90 | } | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * watchdog_stop: wrapper to stop the watchdog. | ||
96 | * @wddev: the watchdog device to stop | ||
97 | * | ||
98 | * Stop the watchdog if it is still active and unmark it active. | ||
99 | * This function returns zero on success or a negative errno code for | ||
100 | * failure. | ||
101 | * If the 'nowayout' feature was set, the watchdog cannot be stopped. | ||
102 | */ | ||
103 | |||
104 | static int watchdog_stop(struct watchdog_device *wddev) | ||
105 | { | ||
106 | int err = -EBUSY; | ||
107 | |||
108 | if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { | ||
109 | pr_info("%s: nowayout prevents watchdog to be stopped!\n", | ||
110 | wdd->info->identity); | ||
111 | return err; | ||
112 | } | ||
113 | |||
114 | if (test_bit(WDOG_ACTIVE, &wdd->status)) { | ||
115 | err = wddev->ops->stop(wddev); | ||
116 | if (err < 0) | ||
117 | return err; | ||
118 | |||
119 | clear_bit(WDOG_ACTIVE, &wdd->status); | ||
120 | } | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * watchdog_write: writes to the watchdog. | ||
126 | * @file: file from VFS | ||
127 | * @data: user address of data | ||
128 | * @len: length of data | ||
129 | * @ppos: pointer to the file offset | ||
130 | * | ||
131 | * A write to a watchdog device is defined as a keepalive ping. | ||
132 | * Writing the magic 'V' sequence allows the next close to turn | ||
133 | * off the watchdog (if 'nowayout' is not set). | ||
134 | */ | ||
135 | |||
136 | static ssize_t watchdog_write(struct file *file, const char __user *data, | ||
137 | size_t len, loff_t *ppos) | ||
138 | { | ||
139 | size_t i; | ||
140 | char c; | ||
141 | |||
142 | if (len == 0) | ||
143 | return 0; | ||
144 | |||
145 | /* | ||
146 | * Note: just in case someone wrote the magic character | ||
147 | * five months ago... | ||
148 | */ | ||
149 | clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); | ||
150 | |||
151 | /* scan to see whether or not we got the magic character */ | ||
152 | for (i = 0; i != len; i++) { | ||
153 | if (get_user(c, data + i)) | ||
154 | return -EFAULT; | ||
155 | if (c == 'V') | ||
156 | set_bit(WDOG_ALLOW_RELEASE, &wdd->status); | ||
157 | } | ||
158 | |||
159 | /* someone wrote to us, so we send the watchdog a keepalive ping */ | ||
160 | watchdog_ping(wdd); | ||
161 | |||
162 | return len; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * watchdog_ioctl: handle the different ioctl's for the watchdog device. | ||
167 | * @file: file handle to the device | ||
168 | * @cmd: watchdog command | ||
169 | * @arg: argument pointer | ||
170 | * | ||
171 | * The watchdog API defines a common set of functions for all watchdogs | ||
172 | * according to their available features. | ||
173 | */ | ||
174 | |||
175 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | ||
176 | unsigned long arg) | ||
177 | { | ||
178 | void __user *argp = (void __user *)arg; | ||
179 | int __user *p = argp; | ||
180 | unsigned int val; | ||
181 | int err; | ||
182 | |||
183 | if (wdd->ops->ioctl) { | ||
184 | err = wdd->ops->ioctl(wdd, cmd, arg); | ||
185 | if (err != -ENOIOCTLCMD) | ||
186 | return err; | ||
187 | } | ||
188 | |||
189 | switch (cmd) { | ||
190 | case WDIOC_GETSUPPORT: | ||
191 | return copy_to_user(argp, wdd->info, | ||
192 | sizeof(struct watchdog_info)) ? -EFAULT : 0; | ||
193 | case WDIOC_GETSTATUS: | ||
194 | val = wdd->ops->status ? wdd->ops->status(wdd) : 0; | ||
195 | return put_user(val, p); | ||
196 | case WDIOC_GETBOOTSTATUS: | ||
197 | return put_user(wdd->bootstatus, p); | ||
198 | case WDIOC_SETOPTIONS: | ||
199 | if (get_user(val, p)) | ||
200 | return -EFAULT; | ||
201 | if (val & WDIOS_DISABLECARD) { | ||
202 | err = watchdog_stop(wdd); | ||
203 | if (err < 0) | ||
204 | return err; | ||
205 | } | ||
206 | if (val & WDIOS_ENABLECARD) { | ||
207 | err = watchdog_start(wdd); | ||
208 | if (err < 0) | ||
209 | return err; | ||
210 | } | ||
211 | return 0; | ||
212 | case WDIOC_KEEPALIVE: | ||
213 | if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) | ||
214 | return -EOPNOTSUPP; | ||
215 | watchdog_ping(wdd); | ||
216 | return 0; | ||
217 | case WDIOC_SETTIMEOUT: | ||
218 | if ((wdd->ops->set_timeout == NULL) || | ||
219 | !(wdd->info->options & WDIOF_SETTIMEOUT)) | ||
220 | return -EOPNOTSUPP; | ||
221 | if (get_user(val, p)) | ||
222 | return -EFAULT; | ||
223 | if ((wdd->max_timeout != 0) && | ||
224 | (val < wdd->min_timeout || val > wdd->max_timeout)) | ||
225 | return -EINVAL; | ||
226 | err = wdd->ops->set_timeout(wdd, val); | ||
227 | if (err < 0) | ||
228 | return err; | ||
229 | wdd->timeout = val; | ||
230 | /* If the watchdog is active then we send a keepalive ping | ||
231 | * to make sure that the watchdog keep's running (and if | ||
232 | * possible that it takes the new timeout) */ | ||
233 | watchdog_ping(wdd); | ||
234 | /* Fall */ | ||
235 | case WDIOC_GETTIMEOUT: | ||
236 | /* timeout == 0 means that we don't know the timeout */ | ||
237 | if (wdd->timeout == 0) | ||
238 | return -EOPNOTSUPP; | ||
239 | return put_user(wdd->timeout, p); | ||
240 | default: | ||
241 | return -ENOTTY; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | * watchdog_open: open the /dev/watchdog device. | ||
247 | * @inode: inode of device | ||
248 | * @file: file handle to device | ||
249 | * | ||
250 | * When the /dev/watchdog device gets opened, we start the watchdog. | ||
251 | * Watch out: the /dev/watchdog device is single open, so we make sure | ||
252 | * it can only be opened once. | ||
253 | */ | ||
254 | |||
255 | static int watchdog_open(struct inode *inode, struct file *file) | ||
256 | { | ||
257 | int err = -EBUSY; | ||
258 | |||
259 | /* the watchdog is single open! */ | ||
260 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) | ||
261 | return -EBUSY; | ||
262 | |||
263 | /* | ||
264 | * If the /dev/watchdog device is open, we don't want the module | ||
265 | * to be unloaded. | ||
266 | */ | ||
267 | if (!try_module_get(wdd->ops->owner)) | ||
268 | goto out; | ||
269 | |||
270 | err = watchdog_start(wdd); | ||
271 | if (err < 0) | ||
272 | goto out_mod; | ||
273 | |||
274 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | ||
275 | return nonseekable_open(inode, file); | ||
276 | |||
277 | out_mod: | ||
278 | module_put(wdd->ops->owner); | ||
279 | out: | ||
280 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | ||
281 | return err; | ||
282 | } | ||
283 | |||
284 | /* | ||
285 | * watchdog_release: release the /dev/watchdog device. | ||
286 | * @inode: inode of device | ||
287 | * @file: file handle to device | ||
288 | * | ||
289 | * This is the code for when /dev/watchdog gets closed. We will only | ||
290 | * stop the watchdog when we have received the magic char (and nowayout | ||
291 | * was not set), else the watchdog will keep running. | ||
292 | */ | ||
293 | |||
294 | static int watchdog_release(struct inode *inode, struct file *file) | ||
295 | { | ||
296 | int err = -EBUSY; | ||
297 | |||
298 | /* | ||
299 | * We only stop the watchdog if we received the magic character | ||
300 | * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then | ||
301 | * watchdog_stop will fail. | ||
302 | */ | ||
303 | if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || | ||
304 | !(wdd->info->options & WDIOF_MAGICCLOSE)) | ||
305 | err = watchdog_stop(wdd); | ||
306 | |||
307 | /* If the watchdog was not stopped, send a keepalive ping */ | ||
308 | if (err < 0) { | ||
309 | pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); | ||
310 | watchdog_ping(wdd); | ||
311 | } | ||
312 | |||
313 | /* Allow the owner module to be unloaded again */ | ||
314 | module_put(wdd->ops->owner); | ||
315 | |||
316 | /* make sure that /dev/watchdog can be re-opened */ | ||
317 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static const struct file_operations watchdog_fops = { | ||
323 | .owner = THIS_MODULE, | ||
324 | .write = watchdog_write, | ||
325 | .unlocked_ioctl = watchdog_ioctl, | ||
326 | .open = watchdog_open, | ||
327 | .release = watchdog_release, | ||
328 | }; | ||
329 | |||
330 | static struct miscdevice watchdog_miscdev = { | ||
331 | .minor = WATCHDOG_MINOR, | ||
332 | .name = "watchdog", | ||
333 | .fops = &watchdog_fops, | ||
334 | }; | ||
335 | |||
336 | /* | ||
337 | * watchdog_dev_register: | ||
338 | * @watchdog: watchdog device | ||
339 | * | ||
340 | * Register a watchdog device as /dev/watchdog. /dev/watchdog | ||
341 | * is actually a miscdevice and thus we set it up like that. | ||
342 | */ | ||
343 | |||
344 | int watchdog_dev_register(struct watchdog_device *watchdog) | ||
345 | { | ||
346 | int err; | ||
347 | |||
348 | /* Only one device can register for /dev/watchdog */ | ||
349 | if (test_and_set_bit(0, &watchdog_dev_busy)) { | ||
350 | pr_err("only one watchdog can use /dev/watchdog.\n"); | ||
351 | return -EBUSY; | ||
352 | } | ||
353 | |||
354 | wdd = watchdog; | ||
355 | |||
356 | err = misc_register(&watchdog_miscdev); | ||
357 | if (err != 0) { | ||
358 | pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", | ||
359 | watchdog->info->identity, WATCHDOG_MINOR, err); | ||
360 | goto out; | ||
361 | } | ||
362 | |||
363 | return 0; | ||
364 | |||
365 | out: | ||
366 | wdd = NULL; | ||
367 | clear_bit(0, &watchdog_dev_busy); | ||
368 | return err; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * watchdog_dev_unregister: | ||
373 | * @watchdog: watchdog device | ||
374 | * | ||
375 | * Deregister the /dev/watchdog device. | ||
376 | */ | ||
377 | |||
378 | int watchdog_dev_unregister(struct watchdog_device *watchdog) | ||
379 | { | ||
380 | /* Check that a watchdog device was registered in the past */ | ||
381 | if (!test_bit(0, &watchdog_dev_busy) || !wdd) | ||
382 | return -ENODEV; | ||
383 | |||
384 | /* We can only unregister the watchdog device that was registered */ | ||
385 | if (watchdog != wdd) { | ||
386 | pr_err("%s: watchdog was not registered as /dev/watchdog.\n", | ||
387 | watchdog->info->identity); | ||
388 | return -ENODEV; | ||
389 | } | ||
390 | |||
391 | misc_deregister(&watchdog_miscdev); | ||
392 | wdd = NULL; | ||
393 | clear_bit(0, &watchdog_dev_busy); | ||
394 | return 0; | ||
395 | } | ||
diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h new file mode 100644 index 000000000000..bc7612be25ce --- /dev/null +++ b/drivers/watchdog/watchdog_dev.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * watchdog_core.h | ||
3 | * | ||
4 | * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, | ||
5 | * All Rights Reserved. | ||
6 | * | ||
7 | * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. | ||
8 | * | ||
9 | * This source code is part of the generic code that can be used | ||
10 | * by all the watchdog timer drivers. | ||
11 | * | ||
12 | * Based on source code of the following authors: | ||
13 | * Matt Domsch <Matt_Domsch@dell.com>, | ||
14 | * Rob Radez <rob@osinvestor.com>, | ||
15 | * Rusty Lynch <rusty@linux.co.intel.com> | ||
16 | * Satyam Sharma <satyam@infradead.org> | ||
17 | * Randy Dunlap <randy.dunlap@oracle.com> | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or | ||
20 | * modify it under the terms of the GNU General Public License | ||
21 | * as published by the Free Software Foundation; either version | ||
22 | * 2 of the License, or (at your option) any later version. | ||
23 | * | ||
24 | * Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. | ||
25 | * admit liability nor provide warranty for any of this software. | ||
26 | * This material is provided "AS-IS" and at no charge. | ||
27 | */ | ||
28 | |||
29 | /* | ||
30 | * Functions/procedures to be called by the core | ||
31 | */ | ||
32 | int watchdog_dev_register(struct watchdog_device *); | ||
33 | int watchdog_dev_unregister(struct watchdog_device *); | ||