1 /*
2 * Acquire Single Board Computer Watchdog Timer driver for Linux 2.1.x
3 *
4 * Based on wdt.c. Original copyright messages:
5 *
6 * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
7 * http://www.redhat.com
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 *
14 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
15 * warranty for any of this software. This material is provided
16 * "AS-IS" and at no charge.
17 *
18 * (c) Copyright 1995 Alan Cox <alan@redhat.com>
19 *
20 * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
21 * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
22 * Can't add timeout - driver doesn't allow changing value
23 */
24
25 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/version.h>
28 #include <linux/types.h>
29 #include <linux/errno.h>
30 #include <linux/kernel.h>
31 #include <linux/sched.h>
32 #include <linux/miscdevice.h>
33 #include <linux/watchdog.h>
34 #include <linux/slab.h>
35 #include <linux/ioport.h>
36 #include <linux/fcntl.h>
37 #include <asm/io.h>
38 #include <asm/uaccess.h>
39 #include <asm/system.h>
40 #include <linux/notifier.h>
41 #include <linux/reboot.h>
42 #include <linux/init.h>
43 #include <linux/spinlock.h>
44 #include <linux/smp_lock.h>
45
46 static int acq_is_open;
47 static spinlock_t acq_lock;
48 static int expect_close = 0;
49
50 /*
51 * You must set these - there is no sane way to probe for this board.
52 */
53
54 #define WDT_STOP 0x43
55 #define WDT_START 0x443
56
57 #ifdef CONFIG_WATCHDOG_NOWAYOUT
58 static int nowayout = 1;
59 #else
60 static int nowayout = 0;
61 #endif
62
63 MODULE_PARM(nowayout,"i");
64 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
65
66 /*
67 * Kernel methods.
68 */
69
70
acq_ping(void)71 static void acq_ping(void)
72 {
73 /* Write a watchdog value */
74 inb_p(WDT_START);
75 }
76
acq_write(struct file * file,const char * buf,size_t count,loff_t * ppos)77 static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
78 {
79 /* Can't seek (pwrite) on this device */
80 if (ppos != &file->f_pos)
81 return -ESPIPE;
82
83 if(count)
84 {
85 if (!nowayout)
86 {
87 size_t i;
88
89 expect_close = 0;
90
91 for (i = 0; i != count; i++) {
92 char c;
93 if (get_user(c, buf + i))
94 return -EFAULT;
95 if (c == 'V')
96 expect_close = 1;
97 }
98 }
99
100 acq_ping();
101 return 1;
102 }
103 return 0;
104 }
105
acq_read(struct file * file,char * buf,size_t count,loff_t * ppos)106 static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos)
107 {
108 return -EINVAL;
109 }
110
111
112
acq_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)113 static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
114 unsigned long arg)
115 {
116 static struct watchdog_info ident=
117 {
118 WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 1, "Acquire WDT"
119 };
120
121 switch(cmd)
122 {
123 case WDIOC_GETSUPPORT:
124 if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
125 return -EFAULT;
126 break;
127
128 case WDIOC_GETSTATUS:
129 if (copy_to_user((int *)arg, &acq_is_open, sizeof(int)))
130 return -EFAULT;
131 break;
132
133 case WDIOC_KEEPALIVE:
134 acq_ping();
135 break;
136
137 default:
138 return -ENOTTY;
139 }
140 return 0;
141 }
142
acq_open(struct inode * inode,struct file * file)143 static int acq_open(struct inode *inode, struct file *file)
144 {
145 switch(MINOR(inode->i_rdev))
146 {
147 case WATCHDOG_MINOR:
148 spin_lock(&acq_lock);
149 if(acq_is_open)
150 {
151 spin_unlock(&acq_lock);
152 return -EBUSY;
153 }
154 if (nowayout) {
155 MOD_INC_USE_COUNT;
156 }
157 /*
158 * Activate
159 */
160 acq_is_open=1;
161 inb_p(WDT_START);
162 spin_unlock(&acq_lock);
163 return 0;
164 default:
165 return -ENODEV;
166 }
167 }
168
acq_close(struct inode * inode,struct file * file)169 static int acq_close(struct inode *inode, struct file *file)
170 {
171 lock_kernel();
172 if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
173 {
174 spin_lock(&acq_lock);
175 if (expect_close)
176 {
177 inb_p(WDT_STOP);
178 }
179 else
180 {
181 printk(KERN_CRIT "WDT closed unexpectedly. WDT will not stop!\n");
182 }
183 acq_is_open=0;
184 spin_unlock(&acq_lock);
185 }
186 unlock_kernel();
187 return 0;
188 }
189
190 /*
191 * Notifier for system down
192 */
193
acq_notify_sys(struct notifier_block * this,unsigned long code,void * unused)194 static int acq_notify_sys(struct notifier_block *this, unsigned long code,
195 void *unused)
196 {
197 if(code==SYS_DOWN || code==SYS_HALT)
198 {
199 /* Turn the card off */
200 inb_p(WDT_STOP);
201 }
202 return NOTIFY_DONE;
203 }
204
205 /*
206 * Kernel Interfaces
207 */
208
209
210 static struct file_operations acq_fops = {
211 owner: THIS_MODULE,
212 read: acq_read,
213 write: acq_write,
214 ioctl: acq_ioctl,
215 open: acq_open,
216 release: acq_close,
217 };
218
219 static struct miscdevice acq_miscdev=
220 {
221 WATCHDOG_MINOR,
222 "watchdog",
223 &acq_fops
224 };
225
226
227 /*
228 * The WDT card needs to learn about soft shutdowns in order to
229 * turn the timebomb registers off.
230 */
231
232 static struct notifier_block acq_notifier=
233 {
234 acq_notify_sys,
235 NULL,
236 0
237 };
238
acq_init(void)239 static int __init acq_init(void)
240 {
241 printk("WDT driver for Acquire single board computer initialising.\n");
242
243 spin_lock_init(&acq_lock);
244 if (misc_register(&acq_miscdev))
245 return -ENODEV;
246 request_region(WDT_STOP, 1, "Acquire WDT");
247 request_region(WDT_START, 1, "Acquire WDT");
248 register_reboot_notifier(&acq_notifier);
249 return 0;
250 }
251
acq_exit(void)252 static void __exit acq_exit(void)
253 {
254 misc_deregister(&acq_miscdev);
255 unregister_reboot_notifier(&acq_notifier);
256 release_region(WDT_STOP,1);
257 release_region(WDT_START,1);
258 }
259
260 module_init(acq_init);
261 module_exit(acq_exit);
262
263 MODULE_LICENSE("GPL");
264 EXPORT_NO_SYMBOLS;
265