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