1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
35
36#include <linux/module.h>
37#include <linux/types.h>
38#include <linux/miscdevice.h>
39#include <linux/watchdog.h>
40#include <linux/ioport.h>
41#include <linux/fs.h>
42#include <linux/init.h>
43#include <linux/spinlock.h>
44#include <linux/moduleparam.h>
45#include <linux/platform_device.h>
46#include <linux/io.h>
47#include <linux/uaccess.h>
48
49
50static struct platform_device *ibwdt_platform_device;
51static unsigned long ibwdt_is_open;
52static DEFINE_SPINLOCK(ibwdt_lock);
53static char expect_close;
54
55
56#define DRV_NAME "ib700wdt"
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94#define WDT_STOP 0x441
95#define WDT_START 0x443
96
97
98#define WATCHDOG_TIMEOUT 30
99static int timeout = WATCHDOG_TIMEOUT;
100module_param(timeout, int, 0);
101MODULE_PARM_DESC(timeout,
102 "Watchdog timeout in seconds. 0<= timeout <=30, default="
103 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
104
105static bool nowayout = WATCHDOG_NOWAYOUT;
106module_param(nowayout, bool, 0);
107MODULE_PARM_DESC(nowayout,
108 "Watchdog cannot be stopped once started (default="
109 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
110
111
112
113
114
115
116static void ibwdt_ping(void)
117{
118 int wd_margin = 15 - ((timeout + 1) / 2);
119
120 spin_lock(&ibwdt_lock);
121
122
123 outb_p(wd_margin, WDT_START);
124
125 spin_unlock(&ibwdt_lock);
126}
127
128static void ibwdt_disable(void)
129{
130 spin_lock(&ibwdt_lock);
131 outb_p(0, WDT_STOP);
132 spin_unlock(&ibwdt_lock);
133}
134
135static int ibwdt_set_heartbeat(int t)
136{
137 if (t < 0 || t > 30)
138 return -EINVAL;
139
140 timeout = t;
141 return 0;
142}
143
144
145
146
147
148static ssize_t ibwdt_write(struct file *file, const char __user *buf,
149 size_t count, loff_t *ppos)
150{
151 if (count) {
152 if (!nowayout) {
153 size_t i;
154
155
156 expect_close = 0;
157
158 for (i = 0; i != count; i++) {
159 char c;
160 if (get_user(c, buf + i))
161 return -EFAULT;
162 if (c == 'V')
163 expect_close = 42;
164 }
165 }
166 ibwdt_ping();
167 }
168 return count;
169}
170
171static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
172{
173 int new_margin;
174 void __user *argp = (void __user *)arg;
175 int __user *p = argp;
176
177 static const struct watchdog_info ident = {
178 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
179 | WDIOF_MAGICCLOSE,
180 .firmware_version = 1,
181 .identity = "IB700 WDT",
182 };
183
184 switch (cmd) {
185 case WDIOC_GETSUPPORT:
186 if (copy_to_user(argp, &ident, sizeof(ident)))
187 return -EFAULT;
188 break;
189
190 case WDIOC_GETSTATUS:
191 case WDIOC_GETBOOTSTATUS:
192 return put_user(0, p);
193
194 case WDIOC_SETOPTIONS:
195 {
196 int options, retval = -EINVAL;
197
198 if (get_user(options, p))
199 return -EFAULT;
200
201 if (options & WDIOS_DISABLECARD) {
202 ibwdt_disable();
203 retval = 0;
204 }
205 if (options & WDIOS_ENABLECARD) {
206 ibwdt_ping();
207 retval = 0;
208 }
209 return retval;
210 }
211 case WDIOC_KEEPALIVE:
212 ibwdt_ping();
213 break;
214
215 case WDIOC_SETTIMEOUT:
216 if (get_user(new_margin, p))
217 return -EFAULT;
218 if (ibwdt_set_heartbeat(new_margin))
219 return -EINVAL;
220 ibwdt_ping();
221
222
223 case WDIOC_GETTIMEOUT:
224 return put_user(timeout, p);
225
226 default:
227 return -ENOTTY;
228 }
229 return 0;
230}
231
232static int ibwdt_open(struct inode *inode, struct file *file)
233{
234 if (test_and_set_bit(0, &ibwdt_is_open))
235 return -EBUSY;
236 if (nowayout)
237 __module_get(THIS_MODULE);
238
239
240 ibwdt_ping();
241 return nonseekable_open(inode, file);
242}
243
244static int ibwdt_close(struct inode *inode, struct file *file)
245{
246 if (expect_close == 42) {
247 ibwdt_disable();
248 } else {
249 pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
250 ibwdt_ping();
251 }
252 clear_bit(0, &ibwdt_is_open);
253 expect_close = 0;
254 return 0;
255}
256
257
258
259
260
261static const struct file_operations ibwdt_fops = {
262 .owner = THIS_MODULE,
263 .llseek = no_llseek,
264 .write = ibwdt_write,
265 .unlocked_ioctl = ibwdt_ioctl,
266 .open = ibwdt_open,
267 .release = ibwdt_close,
268};
269
270static struct miscdevice ibwdt_miscdev = {
271 .minor = WATCHDOG_MINOR,
272 .name = "watchdog",
273 .fops = &ibwdt_fops,
274};
275
276
277
278
279
280static int __init ibwdt_probe(struct platform_device *dev)
281{
282 int res;
283
284#if WDT_START != WDT_STOP
285 if (!request_region(WDT_STOP, 1, "IB700 WDT")) {
286 pr_err("STOP method I/O %X is not available\n", WDT_STOP);
287 res = -EIO;
288 goto out_nostopreg;
289 }
290#endif
291
292 if (!request_region(WDT_START, 1, "IB700 WDT")) {
293 pr_err("START method I/O %X is not available\n", WDT_START);
294 res = -EIO;
295 goto out_nostartreg;
296 }
297
298
299
300 if (ibwdt_set_heartbeat(timeout)) {
301 ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
302 pr_info("timeout value must be 0<=x<=30, using %d\n", timeout);
303 }
304
305 res = misc_register(&ibwdt_miscdev);
306 if (res) {
307 pr_err("failed to register misc device\n");
308 goto out_nomisc;
309 }
310 return 0;
311
312out_nomisc:
313 release_region(WDT_START, 1);
314out_nostartreg:
315#if WDT_START != WDT_STOP
316 release_region(WDT_STOP, 1);
317#endif
318out_nostopreg:
319 return res;
320}
321
322static int ibwdt_remove(struct platform_device *dev)
323{
324 misc_deregister(&ibwdt_miscdev);
325 release_region(WDT_START, 1);
326#if WDT_START != WDT_STOP
327 release_region(WDT_STOP, 1);
328#endif
329 return 0;
330}
331
332static void ibwdt_shutdown(struct platform_device *dev)
333{
334
335 ibwdt_disable();
336}
337
338static struct platform_driver ibwdt_driver = {
339 .remove = ibwdt_remove,
340 .shutdown = ibwdt_shutdown,
341 .driver = {
342 .name = DRV_NAME,
343 },
344};
345
346static int __init ibwdt_init(void)
347{
348 int err;
349
350 pr_info("WDT driver for IB700 single board computer initialising\n");
351
352 ibwdt_platform_device = platform_device_register_simple(DRV_NAME,
353 -1, NULL, 0);
354 if (IS_ERR(ibwdt_platform_device))
355 return PTR_ERR(ibwdt_platform_device);
356
357 err = platform_driver_probe(&ibwdt_driver, ibwdt_probe);
358 if (err)
359 goto unreg_platform_device;
360
361 return 0;
362
363unreg_platform_device:
364 platform_device_unregister(ibwdt_platform_device);
365 return err;
366}
367
368static void __exit ibwdt_exit(void)
369{
370 platform_device_unregister(ibwdt_platform_device);
371 platform_driver_unregister(&ibwdt_driver);
372 pr_info("Watchdog Module Unloaded\n");
373}
374
375module_init(ibwdt_init);
376module_exit(ibwdt_exit);
377
378MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
379MODULE_DESCRIPTION("IB700 SBC watchdog driver");
380MODULE_LICENSE("GPL");
381
382
383