aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/sbc7240_wdt.c324
3 files changed, 338 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 126b554810c8..52dff40ec192 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -466,6 +466,19 @@ config SBC8360_WDT
466 466
467 Most people will say N. 467 Most people will say N.
468 468
469config 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
469config CPU5_WDT 482config CPU5_WDT
470 tristate "SMA CPU5 Watchdog" 483 tristate "SMA CPU5 Watchdog"
471 depends on X86 484 depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e4779f76b1ed..87483cc63252 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
72obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o 72obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
73obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o 73obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
74obj-$(CONFIG_SBC8360_WDT) += sbc8360.o 74obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
75obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
75obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o 76obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
76obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o 77obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
77obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o 78obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
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
44static int timeout = SBC7240_TIMEOUT; /* in seconds */
45module_param(timeout, int, 0);
46MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<="
47 __MODULE_STRING(SBC7240_MAX_TIMEOUT) ", default="
48 __MODULE_STRING(SBC7240_TIMEOUT) ")");
49
50static int nowayout = WATCHDOG_NOWAYOUT;
51module_param(nowayout, int, 0);
52MODULE_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
57static unsigned long wdt_status;
58
59/*
60 * Utility routines
61 */
62
63static 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
73static 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
83static 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 */
99static 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 */
108static 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
137static 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
147static 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
162static struct watchdog_info ident = {
163 .options = WDIOF_KEEPALIVEPING|
164 WDIOF_SETTIMEOUT|
165 WDIOF_MAGICCLOSE,
166 .firmware_version = 1,
167 .identity = "SBC7240",
168};
169
170
171static 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
222static 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
231static 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
241static 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
249static struct notifier_block wdt_notifier = {
250 .notifier_call = wdt_notify_sys,
251};
252
253static 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
262static 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
308err_out_reboot_notifier:
309 unregister_reboot_notifier(&wdt_notifier);
310err_out_region:
311 release_region(SBC7240_ENABLE_PORT, 1);
312err_out:
313 return rc;
314}
315
316module_init(sbc7240_wdt_init);
317module_exit(sbc7240_wdt_unload);
318
319MODULE_AUTHOR("Gilles Gigan");
320MODULE_DESCRIPTION("Watchdog device driver for single board"
321 " computers EPIC Nano 7240 from iEi");
322MODULE_LICENSE("GPL");
323MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
324