[PATCH] watchdog nowayout for ib700wdt
[opensuse:kernel.git] / drivers / char / ib700wdt.c
1 /*
2  *      IB700 Single Board Computer WDT driver for Linux 2.4.x
3  *
4  *      (c) Copyright 2001 Charles Howes <chowes@vsol.net>
5  *
6  *      Based on advantechwdt.c which is based on acquirewdt.c which
7  *       is based on wdt.c.
8  *
9  *      (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
10  *
11  *      Based on acquirewdt.c which is based on wdt.c.
12  *      Original copyright messages:
13  *
14  *      (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
15  *                              http://www.redhat.com
16  *
17  *      This program is free software; you can redistribute it and/or
18  *      modify it under the terms of the GNU General Public License
19  *      as published by the Free Software Foundation; either version
20  *      2 of the License, or (at your option) any later version.
21  *
22  *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
23  *      warranty for any of this software. This material is provided
24  *      "AS-IS" and at no charge.
25  *
26  *      (c) Copyright 1995    Alan Cox <alan@redhat.com>
27  *
28  *      14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
29  *           Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
30  *           Added timeout module option to override default
31  * 
32  */
33
34 #include <linux/config.h>
35 #include <linux/module.h>
36 #include <linux/version.h>
37 #include <linux/types.h>
38 #include <linux/errno.h>
39 #include <linux/kernel.h>
40 #include <linux/sched.h>
41 #include <linux/miscdevice.h>
42 #include <linux/watchdog.h>
43 #include <linux/slab.h>
44 #include <linux/ioport.h>
45 #include <linux/fcntl.h>
46 #include <asm/io.h>
47 #include <asm/uaccess.h>
48 #include <asm/system.h>
49 #include <linux/notifier.h>
50 #include <linux/reboot.h>
51 #include <linux/init.h>
52 #include <linux/spinlock.h>
53 #include <linux/smp_lock.h>
54
55 static int ibwdt_is_open;
56 static spinlock_t ibwdt_lock;
57
58 /*
59  *
60  * Watchdog Timer Configuration
61  *
62  * The function of the watchdog timer is to reset the system
63  * automatically and is defined at I/O port 0443H.  To enable the
64  * watchdog timer and allow the system to reset, write I/O port 0443H.
65  * To disable the timer, write I/O port 0441H for the system to stop the
66  * watchdog function.  The timer has a tolerance of 20% for its
67  * intervals.
68  *
69  * The following describes how the timer should be programmed.
70  *
71  * Enabling Watchdog:
72  * MOV AX,000FH (Choose the values from 0 to F)
73  * MOV DX,0443H
74  * OUT DX,AX
75  *
76  * Disabling Watchdog:
77  * MOV AX,000FH (Any value is fine.)
78  * MOV DX,0441H
79  * OUT DX,AX
80  *
81  * Watchdog timer control table:
82  * Level   Value  Time/sec | Level Value Time/sec
83  *   1       F       0     |   9     7      16
84  *   2       E       2     |   10    6      18
85  *   3       D       4     |   11    5      20
86  *   4       C       6     |   12    4      22
87  *   5       B       8     |   13    3      24
88  *   6       A       10    |   14    2      26
89  *   7       9       12    |   15    1      28
90  *   8       8       14    |   16    0      30
91  *
92  */
93
94 #define WDT_STOP 0x441
95 #define WDT_START 0x443
96
97 #define WD_TIMO 0               /* 30 seconds +/- 20%, from table */
98
99 static int timeout_val = WD_TIMO;       /* value in table */
100 static int timeout = 30;                /* in seconds */
101 MODULE_PARM(timeout,"i");
102 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, 0 < n < 30, must be even (default=30)");
103
104 #ifdef CONFIG_WATCHDOG_NOWAYOUT
105 static int nowayout = 1;
106 #else
107 static int nowayout = 0;
108 #endif
109
110 MODULE_PARM(nowayout,"i");
111 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
112
113 /*
114  *      Kernel methods.
115  */
116
117 static void __init
118 ibwdt_validate_timeout(void)
119 {
120         timeout_val = (30 - timeout) / 2;
121         if (timeout_val < 0 || timeout_val > 0xF) timeout_val = WD_TIMO;
122 }
123
124 static void
125 ibwdt_ping(void)
126 {
127         /* Write a watchdog value */
128         outb_p(timeout_val, WDT_START);
129 }
130
131 static ssize_t
132 ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
133 {
134         /*  Can't seek (pwrite) on this device  */
135         if (ppos != &file->f_pos)
136                 return -ESPIPE;
137
138         if (count) {
139                 ibwdt_ping();
140                 return 1;
141         }
142         return 0;
143 }
144
145 static ssize_t
146 ibwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos)
147 {
148         return -EINVAL;
149 }
150
151 static int
152 ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
153           unsigned long arg)
154 {
155         static struct watchdog_info ident = {
156                 WDIOF_KEEPALIVEPING, 1, "IB700 WDT"
157         };
158
159         switch (cmd) {
160         case WDIOC_GETSUPPORT:
161           if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
162             return -EFAULT;
163           break;
164
165         case WDIOC_GETSTATUS:
166           if (copy_to_user((int *)arg, &ibwdt_is_open,  sizeof(int)))
167             return -EFAULT;
168           break;
169
170         case WDIOC_KEEPALIVE:
171           ibwdt_ping();
172           break;
173
174         default:
175           return -ENOTTY;
176         }
177         return 0;
178 }
179
180 static int
181 ibwdt_open(struct inode *inode, struct file *file)
182 {
183         switch (minor(inode->i_rdev)) {
184                 case WATCHDOG_MINOR:
185                         spin_lock(&ibwdt_lock);
186                         if (ibwdt_is_open) {
187                                 spin_unlock(&ibwdt_lock);
188                                 return -EBUSY;
189                         }
190                         if (nowayout) {
191                                 MOD_INC_USE_COUNT;
192                         }
193                         /*
194                          *      Activate
195                          */
196
197                         ibwdt_is_open = 1;
198                         ibwdt_ping();
199                         spin_unlock(&ibwdt_lock);
200                         return 0;
201                 default:
202                         return -ENODEV;
203         }
204 }
205
206 static int
207 ibwdt_close(struct inode *inode, struct file *file)
208 {
209         lock_kernel();
210         if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
211                 spin_lock(&ibwdt_lock);
212                 if (!nowayout) {
213                         outb_p(timeout_val, WDT_STOP);
214                 }
215                 ibwdt_is_open = 0;
216                 spin_unlock(&ibwdt_lock);
217         }
218         unlock_kernel();
219         return 0;
220 }
221
222 /*
223  *      Notifier for system down
224  */
225
226 static int
227 ibwdt_notify_sys(struct notifier_block *this, unsigned long code,
228         void *unused)
229 {
230         if (code == SYS_DOWN || code == SYS_HALT) {
231                 /* Turn the WDT off */
232                 outb_p(timeout_val, WDT_STOP);
233         }
234         return NOTIFY_DONE;
235 }
236
237 /*
238  *      Kernel Interfaces
239  */
240
241 static struct file_operations ibwdt_fops = {
242         owner:          THIS_MODULE,
243         read:           ibwdt_read,
244         write:          ibwdt_write,
245         ioctl:          ibwdt_ioctl,
246         open:           ibwdt_open,
247         release:        ibwdt_close,
248 };
249
250 static struct miscdevice ibwdt_miscdev = {
251         WATCHDOG_MINOR,
252         "watchdog",
253         &ibwdt_fops
254 };
255
256 /*
257  *      The WDT needs to learn about soft shutdowns in order to
258  *      turn the timebomb registers off.
259  */
260
261 static struct notifier_block ibwdt_notifier = {
262         ibwdt_notify_sys,
263         NULL,
264         0
265 };
266
267 static int __init
268 ibwdt_init(void)
269 {
270         printk("WDT driver for IB700 single board computer initialising.\n");
271
272         ibwdt_validate_timeout();
273         spin_lock_init(&ibwdt_lock);
274         misc_register(&ibwdt_miscdev);
275 #if WDT_START != WDT_STOP
276         request_region(WDT_STOP, 1, "IB700 WDT");
277 #endif
278         request_region(WDT_START, 1, "IB700 WDT");
279         register_reboot_notifier(&ibwdt_notifier);
280         return 0;
281 }
282
283 static void __exit
284 ibwdt_exit(void)
285 {
286         misc_deregister(&ibwdt_miscdev);
287         unregister_reboot_notifier(&ibwdt_notifier);
288 #if WDT_START != WDT_STOP
289         release_region(WDT_STOP,1);
290 #endif
291         release_region(WDT_START,1);
292 }
293
294 module_init(ibwdt_init);
295 module_exit(ibwdt_exit);
296
297 MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
298 MODULE_DESCRIPTION("IB700 SBC watchdog driver");
299 MODULE_LICENSE("GPL");
300
301 /* end of ib700wdt.c */