diff options
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 2 | ||||
-rw-r--r-- | drivers/sbus/char/cpwatchdog.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 23 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/at32ap700x_wdt.c | 69 | ||||
-rw-r--r-- | drivers/watchdog/bfin_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/it8712f_wdt.c | 400 | ||||
-rw-r--r-- | drivers/watchdog/sbc7240_wdt.c | 324 | ||||
-rw-r--r-- | drivers/watchdog/w83697hf_wdt.c | 4 |
9 files changed, 821 insertions, 7 deletions
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index e686fc925168..8f45ca9235ad 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c | |||
@@ -669,6 +669,7 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, | |||
669 | return 0; | 669 | return 0; |
670 | 670 | ||
671 | case WDIOC_SET_PRETIMEOUT: | 671 | case WDIOC_SET_PRETIMEOUT: |
672 | case WDIOC_SETPRETIMEOUT: | ||
672 | i = copy_from_user(&val, argp, sizeof(int)); | 673 | i = copy_from_user(&val, argp, sizeof(int)); |
673 | if (i) | 674 | if (i) |
674 | return -EFAULT; | 675 | return -EFAULT; |
@@ -676,6 +677,7 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, | |||
676 | return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); | 677 | return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); |
677 | 678 | ||
678 | case WDIOC_GET_PRETIMEOUT: | 679 | case WDIOC_GET_PRETIMEOUT: |
680 | case WDIOC_GETPRETIMEOUT: | ||
679 | i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); | 681 | i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); |
680 | if (i) | 682 | if (i) |
681 | return -EFAULT; | 683 | return -EFAULT; |
diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c index 7b5773d88212..a4e758143665 100644 --- a/drivers/sbus/char/cpwatchdog.c +++ b/drivers/sbus/char/cpwatchdog.c | |||
@@ -154,7 +154,7 @@ struct wd_device { | |||
154 | }; | 154 | }; |
155 | 155 | ||
156 | static struct wd_device wd_dev = { | 156 | static struct wd_device wd_dev = { |
157 | 0, SPIN_LOCK_UNLOCKED, 0, 0, 0, 0, | 157 | 0, __SPIN_LOCK_UNLOCKED(wd_dev.lock), 0, 0, 0, 0, |
158 | }; | 158 | }; |
159 | 159 | ||
160 | static struct timer_list wd_timer; | 160 | static struct timer_list wd_timer; |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2792bc1a7269..52dff40ec192 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -392,6 +392,16 @@ config ITCO_VENDOR_SUPPORT | |||
392 | devices. At this moment we only have additional support for some | 392 | devices. At this moment we only have additional support for some |
393 | SuperMicro Inc. motherboards. | 393 | SuperMicro Inc. motherboards. |
394 | 394 | ||
395 | config IT8712F_WDT | ||
396 | tristate "IT8712F (Smart Guardian) Watchdog Timer" | ||
397 | depends on X86 | ||
398 | ---help--- | ||
399 | This is the driver for the built-in watchdog timer on the IT8712F | ||
400 | Super I/0 chipset used on many motherboards. | ||
401 | |||
402 | To compile this driver as a module, choose M here: the | ||
403 | module will be called it8712f_wdt. | ||
404 | |||
395 | config SC1200_WDT | 405 | config SC1200_WDT |
396 | tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" | 406 | tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" |
397 | depends on X86 | 407 | depends on X86 |
@@ -456,6 +466,19 @@ config SBC8360_WDT | |||
456 | 466 | ||
457 | Most people will say N. | 467 | Most people will say N. |
458 | 468 | ||
469 | config SBC7240_WDT | ||
470 | tristate "SBC Nano 7240 Watchdog Timer" | ||
471 | depends on X86_32 | ||
472 | ---help--- | ||
473 | This is the driver for the hardware watchdog found on the IEI | ||
474 | single board computers EPIC Nano 7240 (and likely others). This | ||
475 | watchdog simply watches your kernel to make sure it doesn't freeze, | ||
476 | and if it does, it reboots your computer after a certain amount of | ||
477 | time. | ||
478 | |||
479 | To compile this driver as a module, choose M here: the | ||
480 | module will be called sbc7240_wdt. | ||
481 | |||
459 | config CPU5_WDT | 482 | config CPU5_WDT |
460 | tristate "SMA CPU5 Watchdog" | 483 | tristate "SMA CPU5 Watchdog" |
461 | depends on X86 | 484 | depends on X86 |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7d9e5734f8bb..87483cc63252 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -66,11 +66,13 @@ obj-$(CONFIG_IBMASR) += ibmasr.o | |||
66 | obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o | 66 | obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o |
67 | obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o | 67 | obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o |
68 | obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o iTCO_vendor_support.o | 68 | obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o iTCO_vendor_support.o |
69 | obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o | ||
69 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o | 70 | obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o |
70 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o | 71 | obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o |
71 | obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o | 72 | obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o |
72 | obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o | 73 | obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o |
73 | obj-$(CONFIG_SBC8360_WDT) += sbc8360.o | 74 | obj-$(CONFIG_SBC8360_WDT) += sbc8360.o |
75 | obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o | ||
74 | obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o | 76 | obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o |
75 | obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o | 77 | obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o |
76 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o | 78 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o |
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c index 54a516169d07..fb5ed6478f78 100644 --- a/drivers/watchdog/at32ap700x_wdt.c +++ b/drivers/watchdog/at32ap700x_wdt.c | |||
@@ -6,6 +6,19 @@ | |||
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | * | ||
10 | * | ||
11 | * Errata: WDT Clear is blocked after WDT Reset | ||
12 | * | ||
13 | * A watchdog timer event will, after reset, block writes to the WDT_CLEAR | ||
14 | * register, preventing the program to clear the next Watchdog Timer Reset. | ||
15 | * | ||
16 | * If you still want to use the WDT after a WDT reset a small code can be | ||
17 | * insterted at the startup checking the AVR32_PM.rcause register for WDT reset | ||
18 | * and use a GPIO pin to reset the system. This method requires that one of the | ||
19 | * GPIO pins are available and connected externally to the RESET_N pin. After | ||
20 | * the GPIO pin has pulled down the reset line the GPIO will be reset and leave | ||
21 | * the pin tristated with pullup. | ||
9 | */ | 22 | */ |
10 | 23 | ||
11 | #include <linux/init.h> | 24 | #include <linux/init.h> |
@@ -44,6 +57,13 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | |||
44 | 57 | ||
45 | #define WDT_CLR 0x04 | 58 | #define WDT_CLR 0x04 |
46 | 59 | ||
60 | #define WDT_RCAUSE 0x10 | ||
61 | #define WDT_RCAUSE_POR 0 | ||
62 | #define WDT_RCAUSE_EXT 2 | ||
63 | #define WDT_RCAUSE_WDT 3 | ||
64 | #define WDT_RCAUSE_JTAG 4 | ||
65 | #define WDT_RCAUSE_SERP 5 | ||
66 | |||
47 | #define WDT_BIT(name) (1 << WDT_##name) | 67 | #define WDT_BIT(name) (1 << WDT_##name) |
48 | #define WDT_BF(name, value) ((value) << WDT_##name) | 68 | #define WDT_BF(name, value) ((value) << WDT_##name) |
49 | 69 | ||
@@ -56,6 +76,7 @@ struct wdt_at32ap700x { | |||
56 | void __iomem *regs; | 76 | void __iomem *regs; |
57 | spinlock_t io_lock; | 77 | spinlock_t io_lock; |
58 | int timeout; | 78 | int timeout; |
79 | int boot_status; | ||
59 | unsigned long users; | 80 | unsigned long users; |
60 | struct miscdevice miscdev; | 81 | struct miscdevice miscdev; |
61 | }; | 82 | }; |
@@ -126,7 +147,7 @@ static int at32_wdt_close(struct inode *inode, struct file *file) | |||
126 | at32_wdt_stop(); | 147 | at32_wdt_stop(); |
127 | } else { | 148 | } else { |
128 | dev_dbg(wdt->miscdev.parent, | 149 | dev_dbg(wdt->miscdev.parent, |
129 | "Unexpected close, not stopping watchdog!\n"); | 150 | "unexpected close, not stopping watchdog!\n"); |
130 | at32_wdt_pat(); | 151 | at32_wdt_pat(); |
131 | } | 152 | } |
132 | clear_bit(1, &wdt->users); | 153 | clear_bit(1, &wdt->users); |
@@ -154,6 +175,33 @@ static int at32_wdt_settimeout(int time) | |||
154 | return 0; | 175 | return 0; |
155 | } | 176 | } |
156 | 177 | ||
178 | /* | ||
179 | * Get the watchdog status. | ||
180 | */ | ||
181 | static int at32_wdt_get_status(void) | ||
182 | { | ||
183 | int rcause; | ||
184 | int status = 0; | ||
185 | |||
186 | rcause = wdt_readl(wdt, RCAUSE); | ||
187 | |||
188 | switch (rcause) { | ||
189 | case WDT_BIT(RCAUSE_EXT): | ||
190 | status = WDIOF_EXTERN1; | ||
191 | break; | ||
192 | case WDT_BIT(RCAUSE_WDT): | ||
193 | status = WDIOF_CARDRESET; | ||
194 | break; | ||
195 | case WDT_BIT(RCAUSE_POR): /* fall through */ | ||
196 | case WDT_BIT(RCAUSE_JTAG): /* fall through */ | ||
197 | case WDT_BIT(RCAUSE_SERP): /* fall through */ | ||
198 | default: | ||
199 | break; | ||
200 | } | ||
201 | |||
202 | return status; | ||
203 | } | ||
204 | |||
157 | static struct watchdog_info at32_wdt_info = { | 205 | static struct watchdog_info at32_wdt_info = { |
158 | .identity = "at32ap700x watchdog", | 206 | .identity = "at32ap700x watchdog", |
159 | .options = WDIOF_SETTIMEOUT | | 207 | .options = WDIOF_SETTIMEOUT | |
@@ -194,10 +242,12 @@ static int at32_wdt_ioctl(struct inode *inode, struct file *file, | |||
194 | case WDIOC_GETTIMEOUT: | 242 | case WDIOC_GETTIMEOUT: |
195 | ret = put_user(wdt->timeout, p); | 243 | ret = put_user(wdt->timeout, p); |
196 | break; | 244 | break; |
197 | case WDIOC_GETSTATUS: /* fall through */ | 245 | case WDIOC_GETSTATUS: |
198 | case WDIOC_GETBOOTSTATUS: | ||
199 | ret = put_user(0, p); | 246 | ret = put_user(0, p); |
200 | break; | 247 | break; |
248 | case WDIOC_GETBOOTSTATUS: | ||
249 | ret = put_user(wdt->boot_status, p); | ||
250 | break; | ||
201 | case WDIOC_SETOPTIONS: | 251 | case WDIOC_SETOPTIONS: |
202 | ret = get_user(time, p); | 252 | ret = get_user(time, p); |
203 | if (ret) | 253 | if (ret) |
@@ -282,8 +332,19 @@ static int __init at32_wdt_probe(struct platform_device *pdev) | |||
282 | dev_dbg(&pdev->dev, "could not map I/O memory\n"); | 332 | dev_dbg(&pdev->dev, "could not map I/O memory\n"); |
283 | goto err_free; | 333 | goto err_free; |
284 | } | 334 | } |
335 | |||
285 | spin_lock_init(&wdt->io_lock); | 336 | spin_lock_init(&wdt->io_lock); |
286 | wdt->users = 0; | 337 | wdt->boot_status = at32_wdt_get_status(); |
338 | |||
339 | /* Work-around for watchdog silicon errata. */ | ||
340 | if (wdt->boot_status & WDIOF_CARDRESET) { | ||
341 | dev_info(&pdev->dev, "CPU must be reset with external " | ||
342 | "reset or POR due to silicon errata.\n"); | ||
343 | ret = -EIO; | ||
344 | goto err_iounmap; | ||
345 | } else { | ||
346 | wdt->users = 0; | ||
347 | } | ||
287 | wdt->miscdev.minor = WATCHDOG_MINOR; | 348 | wdt->miscdev.minor = WATCHDOG_MINOR; |
288 | wdt->miscdev.name = "watchdog"; | 349 | wdt->miscdev.name = "watchdog"; |
289 | wdt->miscdev.fops = &at32_wdt_fops; | 350 | wdt->miscdev.fops = &at32_wdt_fops; |
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c index 309d27913fc1..31dc7a69e90c 100644 --- a/drivers/watchdog/bfin_wdt.c +++ b/drivers/watchdog/bfin_wdt.c | |||
@@ -71,7 +71,7 @@ static int nowayout = WATCHDOG_NOWAYOUT; | |||
71 | static struct watchdog_info bfin_wdt_info; | 71 | static struct watchdog_info bfin_wdt_info; |
72 | static unsigned long open_check; | 72 | static unsigned long open_check; |
73 | static char expect_close; | 73 | static char expect_close; |
74 | static spinlock_t bfin_wdt_spinlock = SPIN_LOCK_UNLOCKED; | 74 | static DEFINE_SPINLOCK(bfin_wdt_spinlock); |
75 | 75 | ||
76 | /** | 76 | /** |
77 | * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive | 77 | * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive |
diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c new file mode 100644 index 000000000000..6330fc02464e --- /dev/null +++ b/drivers/watchdog/it8712f_wdt.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * IT8712F "Smart Guardian" Watchdog support | ||
3 | * | ||
4 | * Copyright (c) 2006-2007 Jorge Boncompte - DTI2 <jorge@dti2.net> | ||
5 | * | ||
6 | * Based on info and code taken from: | ||
7 | * | ||
8 | * drivers/char/watchdog/scx200_wdt.c | ||
9 | * drivers/hwmon/it87.c | ||
10 | * IT8712F EC-LPC I/O Preliminary Specification 0.9.2.pdf | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License as | ||
14 | * published by the Free Software Foundation; either version 2 of the | ||
15 | * License, or (at your option) any later version. | ||
16 | * | ||
17 | * The author(s) of this software shall not be held liable for damages | ||
18 | * of any nature resulting due to the use of this software. This | ||
19 | * software is provided AS-IS with no warranties. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/watchdog.h> | ||
27 | #include <linux/notifier.h> | ||
28 | #include <linux/reboot.h> | ||
29 | #include <linux/fs.h> | ||
30 | #include <linux/pci.h> | ||
31 | #include <linux/spinlock.h> | ||
32 | |||
33 | #include <asm/uaccess.h> | ||
34 | #include <asm/io.h> | ||
35 | |||
36 | #define NAME "it8712f_wdt" | ||
37 | |||
38 | MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>"); | ||
39 | MODULE_DESCRIPTION("IT8712F Watchdog Driver"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
42 | |||
43 | static int margin = 60; /* in seconds */ | ||
44 | module_param(margin, int, 0); | ||
45 | MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); | ||
46 | |||
47 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
48 | module_param(nowayout, int, 0); | ||
49 | MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); | ||
50 | |||
51 | static struct semaphore it8712f_wdt_sem; | ||
52 | static unsigned expect_close; | ||
53 | static spinlock_t io_lock; | ||
54 | |||
55 | /* Dog Food address - We use the game port address */ | ||
56 | static unsigned short address; | ||
57 | |||
58 | #define REG 0x2e /* The register to read/write */ | ||
59 | #define VAL 0x2f /* The value to read/write */ | ||
60 | |||
61 | #define LDN 0x07 /* Register: Logical device select */ | ||
62 | #define DEVID 0x20 /* Register: Device ID */ | ||
63 | #define DEVREV 0x22 /* Register: Device Revision */ | ||
64 | #define ACT_REG 0x30 /* LDN Register: Activation */ | ||
65 | #define BASE_REG 0x60 /* LDN Register: Base address */ | ||
66 | |||
67 | #define IT8712F_DEVID 0x8712 | ||
68 | |||
69 | #define LDN_GPIO 0x07 /* GPIO and Watch Dog Timer */ | ||
70 | #define LDN_GAME 0x09 /* Game Port */ | ||
71 | |||
72 | #define WDT_CONTROL 0x71 /* WDT Register: Control */ | ||
73 | #define WDT_CONFIG 0x72 /* WDT Register: Configuration */ | ||
74 | #define WDT_TIMEOUT 0x73 /* WDT Register: Timeout Value */ | ||
75 | |||
76 | #define WDT_RESET_GAME 0x10 | ||
77 | #define WDT_RESET_KBD 0x20 | ||
78 | #define WDT_RESET_MOUSE 0x40 | ||
79 | #define WDT_RESET_CIR 0x80 | ||
80 | |||
81 | #define WDT_UNIT_SEC 0x80 /* If 0 in MINUTES */ | ||
82 | |||
83 | #define WDT_OUT_PWROK 0x10 | ||
84 | #define WDT_OUT_KRST 0x40 | ||
85 | |||
86 | static int | ||
87 | superio_inb(int reg) | ||
88 | { | ||
89 | outb(reg, REG); | ||
90 | return inb(VAL); | ||
91 | } | ||
92 | |||
93 | static void | ||
94 | superio_outb(int val, int reg) | ||
95 | { | ||
96 | outb(reg, REG); | ||
97 | outb(val, VAL); | ||
98 | } | ||
99 | |||
100 | static int | ||
101 | superio_inw(int reg) | ||
102 | { | ||
103 | int val; | ||
104 | outb(reg++, REG); | ||
105 | val = inb(VAL) << 8; | ||
106 | outb(reg, REG); | ||
107 | val |= inb(VAL); | ||
108 | return val; | ||
109 | } | ||
110 | |||
111 | static inline void | ||
112 | superio_select(int ldn) | ||
113 | { | ||
114 | outb(LDN, REG); | ||
115 | outb(ldn, VAL); | ||
116 | } | ||
117 | |||
118 | static inline void | ||
119 | superio_enter(void) | ||
120 | { | ||
121 | spin_lock(&io_lock); | ||
122 | outb(0x87, REG); | ||
123 | outb(0x01, REG); | ||
124 | outb(0x55, REG); | ||
125 | outb(0x55, REG); | ||
126 | } | ||
127 | |||
128 | static inline void | ||
129 | superio_exit(void) | ||
130 | { | ||
131 | outb(0x02, REG); | ||
132 | outb(0x02, VAL); | ||
133 | spin_unlock(&io_lock); | ||
134 | } | ||
135 | |||
136 | static inline void | ||
137 | it8712f_wdt_ping(void) | ||
138 | { | ||
139 | inb(address); | ||
140 | } | ||
141 | |||
142 | static void | ||
143 | it8712f_wdt_update_margin(void) | ||
144 | { | ||
145 | int config = WDT_OUT_KRST | WDT_OUT_PWROK; | ||
146 | |||
147 | printk(KERN_INFO NAME ": timer margin %d seconds\n", margin); | ||
148 | |||
149 | /* The timeout register only has 8bits wide */ | ||
150 | if (margin < 256) | ||
151 | config |= WDT_UNIT_SEC; /* else UNIT are MINUTES */ | ||
152 | superio_outb(config, WDT_CONFIG); | ||
153 | |||
154 | superio_outb((margin > 255) ? (margin / 60) : margin, WDT_TIMEOUT); | ||
155 | } | ||
156 | |||
157 | static void | ||
158 | it8712f_wdt_enable(void) | ||
159 | { | ||
160 | printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); | ||
161 | superio_enter(); | ||
162 | superio_select(LDN_GPIO); | ||
163 | |||
164 | superio_outb(WDT_RESET_GAME, WDT_CONTROL); | ||
165 | |||
166 | it8712f_wdt_update_margin(); | ||
167 | |||
168 | superio_exit(); | ||
169 | |||
170 | it8712f_wdt_ping(); | ||
171 | } | ||
172 | |||
173 | static void | ||
174 | it8712f_wdt_disable(void) | ||
175 | { | ||
176 | printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); | ||
177 | |||
178 | superio_enter(); | ||
179 | superio_select(LDN_GPIO); | ||
180 | |||
181 | superio_outb(0, WDT_CONFIG); | ||
182 | superio_outb(0, WDT_CONTROL); | ||
183 | superio_outb(0, WDT_TIMEOUT); | ||
184 | |||
185 | superio_exit(); | ||
186 | } | ||
187 | |||
188 | static int | ||
189 | it8712f_wdt_notify(struct notifier_block *this, | ||
190 | unsigned long code, void *unused) | ||
191 | { | ||
192 | if (code == SYS_HALT || code == SYS_POWER_OFF) | ||
193 | if (!nowayout) | ||
194 | it8712f_wdt_disable(); | ||
195 | |||
196 | return NOTIFY_DONE; | ||
197 | } | ||
198 | |||
199 | static struct notifier_block it8712f_wdt_notifier = { | ||
200 | .notifier_call = it8712f_wdt_notify, | ||
201 | }; | ||
202 | |||
203 | static ssize_t | ||
204 | it8712f_wdt_write(struct file *file, const char __user *data, | ||
205 | size_t len, loff_t *ppos) | ||
206 | { | ||
207 | /* check for a magic close character */ | ||
208 | if (len) { | ||
209 | size_t i; | ||
210 | |||
211 | it8712f_wdt_ping(); | ||
212 | |||
213 | expect_close = 0; | ||
214 | for (i = 0; i < len; ++i) { | ||
215 | char c; | ||
216 | if (get_user(c, data+i)) | ||
217 | return -EFAULT; | ||
218 | if (c == 'V') | ||
219 | expect_close = 42; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | return len; | ||
224 | } | ||
225 | |||
226 | static int | ||
227 | it8712f_wdt_ioctl(struct inode *inode, struct file *file, | ||
228 | unsigned int cmd, unsigned long arg) | ||
229 | { | ||
230 | void __user *argp = (void __user *)arg; | ||
231 | int __user *p = argp; | ||
232 | static struct watchdog_info ident = { | ||
233 | .identity = "IT8712F Watchdog", | ||
234 | .firmware_version = 1, | ||
235 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
236 | }; | ||
237 | int new_margin; | ||
238 | |||
239 | switch (cmd) { | ||
240 | default: | ||
241 | return -ENOTTY; | ||
242 | case WDIOC_GETSUPPORT: | ||
243 | if (copy_to_user(argp, &ident, sizeof(ident))) | ||
244 | return -EFAULT; | ||
245 | return 0; | ||
246 | case WDIOC_GETSTATUS: | ||
247 | case WDIOC_GETBOOTSTATUS: | ||
248 | return put_user(0, p); | ||
249 | case WDIOC_KEEPALIVE: | ||
250 | it8712f_wdt_ping(); | ||
251 | return 0; | ||
252 | case WDIOC_SETTIMEOUT: | ||
253 | if (get_user(new_margin, p)) | ||
254 | return -EFAULT; | ||
255 | if (new_margin < 1) | ||
256 | return -EINVAL; | ||
257 | margin = new_margin; | ||
258 | superio_enter(); | ||
259 | superio_select(LDN_GPIO); | ||
260 | |||
261 | it8712f_wdt_update_margin(); | ||
262 | |||
263 | superio_exit(); | ||
264 | it8712f_wdt_ping(); | ||
265 | case WDIOC_GETTIMEOUT: | ||
266 | if (put_user(margin, p)) | ||
267 | return -EFAULT; | ||
268 | return 0; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | static int | ||
273 | it8712f_wdt_open(struct inode *inode, struct file *file) | ||
274 | { | ||
275 | /* only allow one at a time */ | ||
276 | if (down_trylock(&it8712f_wdt_sem)) | ||
277 | return -EBUSY; | ||
278 | it8712f_wdt_enable(); | ||
279 | |||
280 | return nonseekable_open(inode, file); | ||
281 | } | ||
282 | |||
283 | static int | ||
284 | it8712f_wdt_release(struct inode *inode, struct file *file) | ||
285 | { | ||
286 | if (expect_close != 42) { | ||
287 | printk(KERN_WARNING NAME | ||
288 | ": watchdog device closed unexpectedly, will not" | ||
289 | " disable the watchdog timer\n"); | ||
290 | } else if (!nowayout) { | ||
291 | it8712f_wdt_disable(); | ||
292 | } | ||
293 | expect_close = 0; | ||
294 | up(&it8712f_wdt_sem); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static struct file_operations it8712f_wdt_fops = { | ||
300 | .owner = THIS_MODULE, | ||
301 | .llseek = no_llseek, | ||
302 | .write = it8712f_wdt_write, | ||
303 | .ioctl = it8712f_wdt_ioctl, | ||
304 | .open = it8712f_wdt_open, | ||
305 | .release = it8712f_wdt_release, | ||
306 | }; | ||
307 | |||
308 | static struct miscdevice it8712f_wdt_miscdev = { | ||
309 | .minor = WATCHDOG_MINOR, | ||
310 | .name = "watchdog", | ||
311 | .fops = &it8712f_wdt_fops, | ||
312 | }; | ||
313 | |||
314 | static int __init | ||
315 | it8712f_wdt_find(unsigned short *address) | ||
316 | { | ||
317 | int err = -ENODEV; | ||
318 | int chip_type; | ||
319 | |||
320 | superio_enter(); | ||
321 | chip_type = superio_inw(DEVID); | ||
322 | if (chip_type != IT8712F_DEVID) | ||
323 | goto exit; | ||
324 | |||
325 | superio_select(LDN_GAME); | ||
326 | superio_outb(1, ACT_REG); | ||
327 | if (!(superio_inb(ACT_REG) & 0x01)) { | ||
328 | printk(KERN_ERR NAME ": Device not activated, skipping\n"); | ||
329 | goto exit; | ||
330 | } | ||
331 | |||
332 | *address = superio_inw(BASE_REG); | ||
333 | if (*address == 0) { | ||
334 | printk(KERN_ERR NAME ": Base address not set, skipping\n"); | ||
335 | goto exit; | ||
336 | } | ||
337 | |||
338 | err = 0; | ||
339 | printk(KERN_DEBUG NAME ": Found IT%04xF chip revision %d - " | ||
340 | "using DogFood address 0x%x\n", | ||
341 | chip_type, superio_inb(DEVREV) & 0x0f, *address); | ||
342 | |||
343 | exit: | ||
344 | superio_exit(); | ||
345 | return err; | ||
346 | } | ||
347 | |||
348 | static int __init | ||
349 | it8712f_wdt_init(void) | ||
350 | { | ||
351 | int err = 0; | ||
352 | |||
353 | spin_lock_init(&io_lock); | ||
354 | |||
355 | if (it8712f_wdt_find(&address)) | ||
356 | return -ENODEV; | ||
357 | |||
358 | if (!request_region(address, 1, "IT8712F Watchdog")) { | ||
359 | printk(KERN_WARNING NAME ": watchdog I/O region busy\n"); | ||
360 | return -EBUSY; | ||
361 | } | ||
362 | |||
363 | it8712f_wdt_disable(); | ||
364 | |||
365 | sema_init(&it8712f_wdt_sem, 1); | ||
366 | |||
367 | err = register_reboot_notifier(&it8712f_wdt_notifier); | ||
368 | if (err) { | ||
369 | printk(KERN_ERR NAME ": unable to register reboot notifier\n"); | ||
370 | goto out; | ||
371 | } | ||
372 | |||
373 | err = misc_register(&it8712f_wdt_miscdev); | ||
374 | if (err) { | ||
375 | printk(KERN_ERR NAME | ||
376 | ": cannot register miscdev on minor=%d (err=%d)\n", | ||
377 | WATCHDOG_MINOR, err); | ||
378 | goto reboot_out; | ||
379 | } | ||
380 | |||
381 | return 0; | ||
382 | |||
383 | |||
384 | reboot_out: | ||
385 | unregister_reboot_notifier(&it8712f_wdt_notifier); | ||
386 | out: | ||
387 | release_region(address, 1); | ||
388 | return err; | ||
389 | } | ||
390 | |||
391 | static void __exit | ||
392 | it8712f_wdt_exit(void) | ||
393 | { | ||
394 | misc_deregister(&it8712f_wdt_miscdev); | ||
395 | unregister_reboot_notifier(&it8712f_wdt_notifier); | ||
396 | release_region(address, 1); | ||
397 | } | ||
398 | |||
399 | module_init(it8712f_wdt_init); | ||
400 | module_exit(it8712f_wdt_exit); | ||
diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c new file mode 100644 index 000000000000..4c8cefbd8627 --- /dev/null +++ b/drivers/watchdog/sbc7240_wdt.c | |||
@@ -0,0 +1,324 @@ | |||
1 | /* | ||
2 | * NANO7240 SBC Watchdog device driver | ||
3 | * | ||
4 | * Based on w83877f.c by Scott Jennings, | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation; | ||
9 | * | ||
10 | * Software distributed under the License is distributed on an "AS IS" | ||
11 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or | ||
12 | * implied. See the License for the specific language governing | ||
13 | * rights and limitations under the License. | ||
14 | * | ||
15 | * (c) Copyright 2007 Gilles GIGAN <gilles.gigan@jcu.edu.au> | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/fs.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/ioport.h> | ||
22 | #include <linux/jiffies.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include <linux/notifier.h> | ||
27 | #include <linux/reboot.h> | ||
28 | #include <linux/types.h> | ||
29 | #include <linux/watchdog.h> | ||
30 | #include <asm/atomic.h> | ||
31 | #include <asm/io.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/uaccess.h> | ||
34 | |||
35 | #define SBC7240_PREFIX "sbc7240_wdt: " | ||
36 | |||
37 | #define SBC7240_ENABLE_PORT 0x443 | ||
38 | #define SBC7240_DISABLE_PORT 0x043 | ||
39 | #define SBC7240_SET_TIMEOUT_PORT SBC7240_ENABLE_PORT | ||
40 | #define SBC7240_MAGIC_CHAR 'V' | ||
41 | |||
42 | #define SBC7240_TIMEOUT 30 | ||
43 | #define SBC7240_MAX_TIMEOUT 255 | ||
44 | static int timeout = SBC7240_TIMEOUT; /* in seconds */ | ||
45 | module_param(timeout, int, 0); | ||
46 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=" | ||
47 | __MODULE_STRING(SBC7240_MAX_TIMEOUT) ", default=" | ||
48 | __MODULE_STRING(SBC7240_TIMEOUT) ")"); | ||
49 | |||
50 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
51 | module_param(nowayout, int, 0); | ||
52 | MODULE_PARM_DESC(nowayout, "Disable watchdog when closing device file"); | ||
53 | |||
54 | #define SBC7240_OPEN_STATUS_BIT 0 | ||
55 | #define SBC7240_ENABLED_STATUS_BIT 1 | ||
56 | #define SBC7240_EXPECT_CLOSE_STATUS_BIT 2 | ||
57 | static unsigned long wdt_status; | ||
58 | |||
59 | /* | ||
60 | * Utility routines | ||
61 | */ | ||
62 | |||
63 | static void wdt_disable(void) | ||
64 | { | ||
65 | /* disable the watchdog */ | ||
66 | if (test_and_clear_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) { | ||
67 | inb_p(SBC7240_DISABLE_PORT); | ||
68 | printk(KERN_INFO SBC7240_PREFIX | ||
69 | "Watchdog timer is now disabled.\n"); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static void wdt_enable(void) | ||
74 | { | ||
75 | /* enable the watchdog */ | ||
76 | if (!test_and_set_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) { | ||
77 | inb_p(SBC7240_ENABLE_PORT); | ||
78 | printk(KERN_INFO SBC7240_PREFIX | ||
79 | "Watchdog timer is now enabled.\n"); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | static int wdt_set_timeout(int t) | ||
84 | { | ||
85 | if (t < 1 || t > SBC7240_MAX_TIMEOUT) { | ||
86 | printk(KERN_ERR SBC7240_PREFIX | ||
87 | "timeout value must be 1<=x<=%d\n", | ||
88 | SBC7240_MAX_TIMEOUT); | ||
89 | return -1; | ||
90 | } | ||
91 | /* set the timeout */ | ||
92 | outb_p((unsigned)t, SBC7240_SET_TIMEOUT_PORT); | ||
93 | timeout = t; | ||
94 | printk(KERN_INFO SBC7240_PREFIX "timeout set to %d seconds\n", t); | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | /* Whack the dog */ | ||
99 | static inline void wdt_keepalive(void) | ||
100 | { | ||
101 | if (test_bit(SBC7240_ENABLED_STATUS_BIT, &wdt_status)) | ||
102 | inb_p(SBC7240_ENABLE_PORT); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * /dev/watchdog handling | ||
107 | */ | ||
108 | static ssize_t fop_write(struct file *file, const char __user *buf, | ||
109 | size_t count, loff_t *ppos) | ||
110 | { | ||
111 | size_t i; | ||
112 | char c; | ||
113 | |||
114 | if (count) { | ||
115 | if (!nowayout) { | ||
116 | clear_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, | ||
117 | &wdt_status); | ||
118 | |||
119 | /* is there a magic char ? */ | ||
120 | for (i = 0; i != count; i++) { | ||
121 | if (get_user(c, buf + i)) | ||
122 | return -EFAULT; | ||
123 | if (c == SBC7240_MAGIC_CHAR) { | ||
124 | set_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, | ||
125 | &wdt_status); | ||
126 | break; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | wdt_keepalive(); | ||
132 | } | ||
133 | |||
134 | return count; | ||
135 | } | ||
136 | |||
137 | static int fop_open(struct inode *inode, struct file *file) | ||
138 | { | ||
139 | if (test_and_set_bit(SBC7240_OPEN_STATUS_BIT, &wdt_status)) | ||
140 | return -EBUSY; | ||
141 | |||
142 | wdt_enable(); | ||
143 | |||
144 | return nonseekable_open(inode, file); | ||
145 | } | ||
146 | |||
147 | static int fop_close(struct inode *inode, struct file *file) | ||
148 | { | ||
149 | if (test_and_clear_bit(SBC7240_EXPECT_CLOSE_STATUS_BIT, &wdt_status) | ||
150 | || !nowayout) { | ||
151 | wdt_disable(); | ||
152 | } else { | ||
153 | printk(KERN_CRIT SBC7240_PREFIX | ||
154 | "Unexpected close, not stopping watchdog!\n"); | ||
155 | wdt_keepalive(); | ||
156 | } | ||
157 | |||
158 | clear_bit(SBC7240_OPEN_STATUS_BIT, &wdt_status); | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static struct watchdog_info ident = { | ||
163 | .options = WDIOF_KEEPALIVEPING| | ||
164 | WDIOF_SETTIMEOUT| | ||
165 | WDIOF_MAGICCLOSE, | ||
166 | .firmware_version = 1, | ||
167 | .identity = "SBC7240", | ||
168 | }; | ||
169 | |||
170 | |||
171 | static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
172 | unsigned long arg) | ||
173 | { | ||
174 | switch (cmd) { | ||
175 | case WDIOC_GETSUPPORT: | ||
176 | return copy_to_user | ||
177 | ((void __user *)arg, &ident, sizeof(ident)) | ||
178 | ? -EFAULT : 0; | ||
179 | case WDIOC_GETSTATUS: | ||
180 | case WDIOC_GETBOOTSTATUS: | ||
181 | return put_user(0, (int __user *)arg); | ||
182 | case WDIOC_KEEPALIVE: | ||
183 | wdt_keepalive(); | ||
184 | return 0; | ||
185 | case WDIOC_SETOPTIONS:{ | ||
186 | int options; | ||
187 | int retval = -EINVAL; | ||
188 | |||
189 | if (get_user(options, (int __user *)arg)) | ||
190 | return -EFAULT; | ||
191 | |||
192 | if (options & WDIOS_DISABLECARD) { | ||
193 | wdt_disable(); | ||
194 | retval = 0; | ||
195 | } | ||
196 | |||
197 | if (options & WDIOS_ENABLECARD) { | ||
198 | wdt_enable(); | ||
199 | retval = 0; | ||
200 | } | ||
201 | |||
202 | return retval; | ||
203 | } | ||
204 | case WDIOC_SETTIMEOUT:{ | ||
205 | int new_timeout; | ||
206 | |||
207 | if (get_user(new_timeout, (int __user *)arg)) | ||
208 | return -EFAULT; | ||
209 | |||
210 | if (wdt_set_timeout(new_timeout)) | ||
211 | return -EINVAL; | ||
212 | |||
213 | /* Fall through */ | ||
214 | } | ||
215 | case WDIOC_GETTIMEOUT: | ||
216 | return put_user(timeout, (int __user *)arg); | ||
217 | default: | ||
218 | return -ENOTTY; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | static const struct file_operations wdt_fops = { | ||
223 | .owner = THIS_MODULE, | ||
224 | .llseek = no_llseek, | ||
225 | .write = fop_write, | ||
226 | .open = fop_open, | ||
227 | .release = fop_close, | ||
228 | .ioctl = fop_ioctl, | ||
229 | }; | ||
230 | |||
231 | static struct miscdevice wdt_miscdev = { | ||
232 | .minor = WATCHDOG_MINOR, | ||
233 | .name = "watchdog", | ||
234 | .fops = &wdt_fops, | ||
235 | }; | ||
236 | |||
237 | /* | ||
238 | * Notifier for system down | ||
239 | */ | ||
240 | |||
241 | static int wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
242 | void *unused) | ||
243 | { | ||
244 | if (code == SYS_DOWN || code == SYS_HALT) | ||
245 | wdt_disable(); | ||
246 | return NOTIFY_DONE; | ||
247 | } | ||
248 | |||
249 | static struct notifier_block wdt_notifier = { | ||
250 | .notifier_call = wdt_notify_sys, | ||
251 | }; | ||
252 | |||
253 | static void __exit sbc7240_wdt_unload(void) | ||
254 | { | ||
255 | printk(KERN_INFO SBC7240_PREFIX "Removing watchdog\n"); | ||
256 | misc_deregister(&wdt_miscdev); | ||
257 | |||
258 | unregister_reboot_notifier(&wdt_notifier); | ||
259 | release_region(SBC7240_ENABLE_PORT, 1); | ||
260 | } | ||
261 | |||
262 | static int __init sbc7240_wdt_init(void) | ||
263 | { | ||
264 | int rc = -EBUSY; | ||
265 | |||
266 | if (!request_region(SBC7240_ENABLE_PORT, 1, "SBC7240 WDT")) { | ||
267 | printk(KERN_ERR SBC7240_PREFIX | ||
268 | "I/O address 0x%04x already in use\n", | ||
269 | SBC7240_ENABLE_PORT); | ||
270 | rc = -EIO; | ||
271 | goto err_out; | ||
272 | } | ||
273 | |||
274 | /* The IO port 0x043 used to disable the watchdog | ||
275 | * is already claimed by the system timer, so we | ||
276 | * cant request_region() it ...*/ | ||
277 | |||
278 | if (timeout < 1 || timeout > SBC7240_MAX_TIMEOUT) { | ||
279 | timeout = SBC7240_TIMEOUT; | ||
280 | printk(KERN_INFO SBC7240_PREFIX | ||
281 | "timeout value must be 1<=x<=%d, using %d\n", | ||
282 | SBC7240_MAX_TIMEOUT, timeout); | ||
283 | } | ||
284 | wdt_set_timeout(timeout); | ||
285 | wdt_disable(); | ||
286 | |||
287 | rc = register_reboot_notifier(&wdt_notifier); | ||
288 | if (rc) { | ||
289 | printk(KERN_ERR SBC7240_PREFIX | ||
290 | "cannot register reboot notifier (err=%d)\n", rc); | ||
291 | goto err_out_region; | ||
292 | } | ||
293 | |||
294 | rc = misc_register(&wdt_miscdev); | ||
295 | if (rc) { | ||
296 | printk(KERN_ERR SBC7240_PREFIX | ||
297 | "cannot register miscdev on minor=%d (err=%d)\n", | ||
298 | wdt_miscdev.minor, rc); | ||
299 | goto err_out_reboot_notifier; | ||
300 | } | ||
301 | |||
302 | printk(KERN_INFO SBC7240_PREFIX | ||
303 | "Watchdog driver for SBC7240 initialised (nowayout=%d)\n", | ||
304 | nowayout); | ||
305 | |||
306 | return 0; | ||
307 | |||
308 | err_out_reboot_notifier: | ||
309 | unregister_reboot_notifier(&wdt_notifier); | ||
310 | err_out_region: | ||
311 | release_region(SBC7240_ENABLE_PORT, 1); | ||
312 | err_out: | ||
313 | return rc; | ||
314 | } | ||
315 | |||
316 | module_init(sbc7240_wdt_init); | ||
317 | module_exit(sbc7240_wdt_unload); | ||
318 | |||
319 | MODULE_AUTHOR("Gilles Gigan"); | ||
320 | MODULE_DESCRIPTION("Watchdog device driver for single board" | ||
321 | " computers EPIC Nano 7240 from iEi"); | ||
322 | MODULE_LICENSE("GPL"); | ||
323 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | ||
324 | |||
diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c index c622a0e6c9ae..6ea125eabeaa 100644 --- a/drivers/watchdog/w83697hf_wdt.c +++ b/drivers/watchdog/w83697hf_wdt.c | |||
@@ -382,8 +382,10 @@ wdt_init(void) | |||
382 | /* we will autodetect the W83697HF/HG watchdog */ | 382 | /* we will autodetect the W83697HF/HG watchdog */ |
383 | for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) { | 383 | for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) { |
384 | wdt_io = w83697hf_ioports[i]; | 384 | wdt_io = w83697hf_ioports[i]; |
385 | if (!w83697hf_check_wdt()) | 385 | if (!w83697hf_check_wdt()) { |
386 | found++; | 386 | found++; |
387 | break; | ||
388 | } | ||
387 | } | 389 | } |
388 | } else { | 390 | } else { |
389 | if (!w83697hf_check_wdt()) | 391 | if (!w83697hf_check_wdt()) |