Merge ../linux-2.6-watchdog-mm
[linux-drm-fsl-dcu.git] / drivers / char / watchdog / eurotechwdt.c
1 /*
2  *      Eurotech CPU-1220/1410 on board WDT driver
3  *
4  *      (c) Copyright 2001 Ascensit <support@ascensit.com>
5  *      (c) Copyright 2001 Rodolfo Giometti <giometti@ascensit.com>
6  *      (c) Copyright 2002 Rob Radez <rob@osinvestor.com>
7  *
8  *      Based on wdt.c.
9  *      Original copyright messages:
10  *
11  *      (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
12  *                              http://www.redhat.com
13  *
14  *      This program is free software; you can redistribute it and/or
15  *      modify it under the terms of the GNU General Public License
16  *      as published by the Free Software Foundation; either version
17  *      2 of the License, or (at your option) any later version.
18  *
19  *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
20  *      warranty for any of this software. This material is provided
21  *      "AS-IS" and at no charge.
22  *
23  *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>*
24  */
25
26 /* Changelog:
27  *
28  * 2002/04/25 - Rob Radez
29  *      clean up #includes
30  *      clean up locking
31  *      make __setup param unique
32  *      proper options in watchdog_info
33  *      add WDIOC_GETSTATUS and WDIOC_SETOPTIONS ioctls
34  *      add expect_close support
35  *
36  * 2001 - Rodolfo Giometti
37  *      Initial release
38  *
39  * 2002.05.30 - Joel Becker <joel.becker@oracle.com>
40  *      Added Matt Domsch's nowayout module option.
41  */
42
43 #include <linux/interrupt.h>
44 #include <linux/module.h>
45 #include <linux/moduleparam.h>
46 #include <linux/types.h>
47 #include <linux/miscdevice.h>
48 #include <linux/watchdog.h>
49 #include <linux/fs.h>
50 #include <linux/ioport.h>
51 #include <linux/notifier.h>
52 #include <linux/reboot.h>
53 #include <linux/init.h>
54
55 #include <asm/io.h>
56 #include <asm/uaccess.h>
57 #include <asm/system.h>
58
59 static unsigned long eurwdt_is_open;
60 static int eurwdt_timeout;
61 static char eur_expect_close;
62
63 /*
64  * You must set these - there is no sane way to probe for this board.
65  * You can use eurwdt=x,y to set these now.
66  */
67
68 static int io = 0x3f0;
69 static int irq = 10;
70 static char *ev = "int";
71
72 #define WDT_TIMEOUT             60                /* 1 minute */
73
74 static int nowayout = WATCHDOG_NOWAYOUT;
75 module_param(nowayout, int, 0);
76 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
77
78 /*
79  * Some symbolic names
80  */
81
82 #define WDT_CTRL_REG            0x30
83 #define WDT_OUTPIN_CFG          0xe2
84 #define WDT_EVENT_INT           0x00
85 #define WDT_EVENT_REBOOT        0x08
86 #define WDT_UNIT_SEL            0xf1
87 #define WDT_UNIT_SECS           0x80
88 #define WDT_TIMEOUT_VAL         0xf2
89 #define WDT_TIMER_CFG           0xf3
90
91
92 module_param(io, int, 0);
93 MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)");
94 module_param(irq, int, 0);
95 MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)");
96 module_param(ev, charp, 0);
97 MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')");
98
99
100 /*
101  * Programming support
102  */
103
104 static inline void eurwdt_write_reg(u8 index, u8 data)
105 {
106         outb(index, io);
107         outb(data, io+1);
108 }
109
110 static inline void eurwdt_lock_chip(void)
111 {
112         outb(0xaa, io);
113 }
114
115 static inline void eurwdt_unlock_chip(void)
116 {
117         outb(0x55, io);
118         eurwdt_write_reg(0x07, 0x08);   /* set the logical device */
119 }
120
121 static inline void eurwdt_set_timeout(int timeout)
122 {
123         eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
124 }
125
126 static inline void eurwdt_disable_timer(void)
127 {
128         eurwdt_set_timeout(0);
129 }
130
131 static void eurwdt_activate_timer(void)
132 {
133         eurwdt_disable_timer();
134         eurwdt_write_reg(WDT_CTRL_REG, 0x01);   /* activate the WDT */
135         eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
136
137         /* Setting interrupt line */
138         if (irq == 2 || irq > 15 || irq < 0) {
139                 printk(KERN_ERR ": invalid irq number\n");
140                 irq = 0;        /* if invalid we disable interrupt */
141         }
142         if (irq == 0)
143                 printk(KERN_INFO ": interrupt disabled\n");
144
145         eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
146
147         eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS);  /* we use seconds */
148         eurwdt_set_timeout(0);  /* the default timeout */
149 }
150
151
152 /*
153  * Kernel methods.
154  */
155
156 static irqreturn_t eurwdt_interrupt(int irq, void *dev_id)
157 {
158         printk(KERN_CRIT "timeout WDT timeout\n");
159
160 #ifdef ONLY_TESTING
161         printk(KERN_CRIT "Would Reboot.\n");
162 #else
163         printk(KERN_CRIT "Initiating system reboot.\n");
164         emergency_restart();
165 #endif
166         return IRQ_HANDLED;
167 }
168
169
170 /**
171  * eurwdt_ping:
172  *
173  * Reload counter one with the watchdog timeout.
174  */
175
176 static void eurwdt_ping(void)
177 {
178         /* Write the watchdog default value */
179         eurwdt_set_timeout(eurwdt_timeout);
180 }
181
182 /**
183  * eurwdt_write:
184  * @file: file handle to the watchdog
185  * @buf: buffer to write (unused as data does not matter here
186  * @count: count of bytes
187  * @ppos: pointer to the position to write. No seeks allowed
188  *
189  * A write to a watchdog device is defined as a keepalive signal. Any
190  * write of data will do, as we we don't define content meaning.
191  */
192
193 static ssize_t eurwdt_write(struct file *file, const char __user *buf,
194 size_t count, loff_t *ppos)
195 {
196         if (count)      {
197                 if (!nowayout) {
198                         size_t i;
199
200                         eur_expect_close = 0;
201
202                         for (i = 0; i != count; i++) {
203                                 char c;
204                                 if(get_user(c, buf+i))
205                                         return -EFAULT;
206                                 if (c == 'V')
207                                         eur_expect_close = 42;
208                         }
209                 }
210                 eurwdt_ping();  /* the default timeout */
211         }
212
213         return count;
214 }
215
216 /**
217  * eurwdt_ioctl:
218  * @inode: inode of the device
219  * @file: file handle to the device
220  * @cmd: watchdog command
221  * @arg: argument pointer
222  *
223  * The watchdog API defines a common set of functions for all watchdogs
224  * according to their available features.
225  */
226
227 static int eurwdt_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                 .options          = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
234                 .firmware_version = 1,
235                 .identity         = "WDT Eurotech CPU-1220/1410",
236         };
237
238         int time;
239         int options, retval = -EINVAL;
240
241         switch(cmd) {
242         default:
243                 return -ENOTTY;
244
245         case WDIOC_GETSUPPORT:
246                 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
247
248         case WDIOC_GETSTATUS:
249         case WDIOC_GETBOOTSTATUS:
250                 return put_user(0, p);
251
252         case WDIOC_KEEPALIVE:
253                 eurwdt_ping();
254                 return 0;
255
256         case WDIOC_SETTIMEOUT:
257                 if (copy_from_user(&time, p, sizeof(int)))
258                         return -EFAULT;
259
260                 /* Sanity check */
261                 if (time < 0 || time > 255)
262                         return -EINVAL;
263
264                 eurwdt_timeout = time;
265                 eurwdt_set_timeout(time);
266                 /* Fall */
267
268         case WDIOC_GETTIMEOUT:
269                 return put_user(eurwdt_timeout, p);
270
271         case WDIOC_SETOPTIONS:
272                 if (get_user(options, p))
273                         return -EFAULT;
274                 if (options & WDIOS_DISABLECARD) {
275                         eurwdt_disable_timer();
276                         retval = 0;
277                 }
278                 if (options & WDIOS_ENABLECARD) {
279                         eurwdt_activate_timer();
280                         eurwdt_ping();
281                         retval = 0;
282                 }
283                 return retval;
284         }
285 }
286
287 /**
288  * eurwdt_open:
289  * @inode: inode of device
290  * @file: file handle to device
291  *
292  * The misc device has been opened. The watchdog device is single
293  * open and on opening we load the counter.
294  */
295
296 static int eurwdt_open(struct inode *inode, struct file *file)
297 {
298         if (test_and_set_bit(0, &eurwdt_is_open))
299                 return -EBUSY;
300         eurwdt_timeout = WDT_TIMEOUT;   /* initial timeout */
301         /* Activate the WDT */
302         eurwdt_activate_timer();
303         return nonseekable_open(inode, file);
304 }
305
306 /**
307  * eurwdt_release:
308  * @inode: inode to board
309  * @file: file handle to board
310  *
311  * The watchdog has a configurable API. There is a religious dispute
312  * between people who want their watchdog to be able to shut down and
313  * those who want to be sure if the watchdog manager dies the machine
314  * reboots. In the former case we disable the counters, in the latter
315  * case you have to open it again very soon.
316  */
317
318 static int eurwdt_release(struct inode *inode, struct file *file)
319 {
320         if (eur_expect_close == 42) {
321                 eurwdt_disable_timer();
322         } else {
323                 printk(KERN_CRIT "eurwdt: Unexpected close, not stopping watchdog!\n");
324                 eurwdt_ping();
325         }
326         clear_bit(0, &eurwdt_is_open);
327         eur_expect_close = 0;
328         return 0;
329 }
330
331 /**
332  * eurwdt_notify_sys:
333  * @this: our notifier block
334  * @code: the event being reported
335  * @unused: unused
336  *
337  * Our notifier is called on system shutdowns. We want to turn the card
338  * off at reboot otherwise the machine will reboot again during memory
339  * test or worse yet during the following fsck. This would suck, in fact
340  * trust me - if it happens it does suck.
341  */
342
343 static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
344         void *unused)
345 {
346         if (code == SYS_DOWN || code == SYS_HALT) {
347                 /* Turn the card off */
348                 eurwdt_disable_timer();
349         }
350
351         return NOTIFY_DONE;
352 }
353
354 /*
355  * Kernel Interfaces
356  */
357
358
359 static const struct file_operations eurwdt_fops = {
360         .owner  = THIS_MODULE,
361         .llseek = no_llseek,
362         .write  = eurwdt_write,
363         .ioctl  = eurwdt_ioctl,
364         .open   = eurwdt_open,
365         .release        = eurwdt_release,
366 };
367
368 static struct miscdevice eurwdt_miscdev = {
369         .minor  = WATCHDOG_MINOR,
370         .name   = "watchdog",
371         .fops   = &eurwdt_fops,
372 };
373
374 /*
375  * The WDT card needs to learn about soft shutdowns in order to
376  * turn the timebomb registers off.
377  */
378
379 static struct notifier_block eurwdt_notifier = {
380         .notifier_call = eurwdt_notify_sys,
381 };
382
383 /**
384  * cleanup_module:
385  *
386  * Unload the watchdog. You cannot do this with any file handles open.
387  * If your watchdog is set to continue ticking on close and you unload
388  * it, well it keeps ticking. We won't get the interrupt but the board
389  * will not touch PC memory so all is fine. You just have to load a new
390  * module in 60 seconds or reboot.
391  */
392
393 static void __exit eurwdt_exit(void)
394 {
395         eurwdt_lock_chip();
396
397         misc_deregister(&eurwdt_miscdev);
398
399         unregister_reboot_notifier(&eurwdt_notifier);
400         release_region(io, 2);
401         free_irq(irq, NULL);
402 }
403
404 /**
405  * eurwdt_init:
406  *
407  * Set up the WDT watchdog board. After grabbing the resources
408  * we require we need also to unlock the device.
409  * The open() function will actually kick the board off.
410  */
411
412 static int __init eurwdt_init(void)
413 {
414         int ret;
415
416         ret = misc_register(&eurwdt_miscdev);
417         if (ret) {
418                 printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
419                 WATCHDOG_MINOR);
420                 goto out;
421         }
422
423         ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL);
424         if(ret) {
425                 printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
426                 goto outmisc;
427         }
428
429         if (!request_region(io, 2, "eurwdt")) {
430                 printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
431                 ret = -EBUSY;
432                 goto outirq;
433         }
434
435         ret = register_reboot_notifier(&eurwdt_notifier);
436         if (ret) {
437                 printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret);
438                 goto outreg;
439         }
440
441         eurwdt_unlock_chip();
442
443         ret = 0;
444         printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
445                 " - timeout event: %s\n",
446                 io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
447
448 out:
449         return ret;
450
451 outreg:
452         release_region(io, 2);
453
454 outirq:
455         free_irq(irq, NULL);
456
457 outmisc:
458         misc_deregister(&eurwdt_miscdev);
459         goto out;
460 }
461
462 module_init(eurwdt_init);
463 module_exit(eurwdt_exit);
464
465 MODULE_AUTHOR("Rodolfo Giometti");
466 MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
467 MODULE_LICENSE("GPL");
468 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);