1 /*
2  *  MachZ ZF-Logic Watchdog Timer driver for Linux
3  *
4  *
5  *  This program is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU General Public License
7  *  as published by the Free Software Foundation; either version
8  *  2 of the License, or (at your option) any later version.
9  *
10  *  The author does NOT admit liability nor provide warranty for
11  *  any of this software. This material is provided "AS-IS" in
12  *  the hope that it may be useful for others.
13  *
14  *  Author: Fernando Fuganti <fuganti@conectiva.com.br>
15  *
16  *  Based on sbc60xxwdt.c by Jakob Oestergaard
17  *
18  *
19  *  We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the
20  *  following periods:
21  *      wd#1 - 2 seconds;
22  *      wd#2 - 7.2 ms;
23  *  After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
24  *  a system RESET and it starts wd#2 that unconditionaly will RESET
25  *  the system when the counter reaches zero.
26  *
27  *  14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
28  * 	Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
29  */
30 
31 #include <linux/config.h>
32 #include <linux/module.h>
33 #include <linux/version.h>
34 #include <linux/types.h>
35 #include <linux/errno.h>
36 #include <linux/kernel.h>
37 #include <linux/timer.h>
38 #include <linux/sched.h>
39 #include <linux/miscdevice.h>
40 #include <linux/watchdog.h>
41 #include <linux/slab.h>
42 #include <linux/ioport.h>
43 #include <linux/fcntl.h>
44 #include <linux/smp_lock.h>
45 #include <asm/io.h>
46 #include <asm/uaccess.h>
47 #include <asm/system.h>
48 #include <linux/notifier.h>
49 #include <linux/reboot.h>
50 #include <linux/init.h>
51 
52 
53 /* ports */
54 #define ZF_IOBASE	0x218
55 #define INDEX		0x218
56 #define DATA_B		0x219
57 #define DATA_W		0x21A
58 #define DATA_D		0x21A
59 
60 /* indexes */			/* size */
61 #define ZFL_VERSION	0x02	/* 16   */
62 #define CONTROL 	0x10	/* 16   */
63 #define STATUS		0x12	/* 8    */
64 #define COUNTER_1	0x0C	/* 16   */
65 #define COUNTER_2	0x0E	/* 8    */
66 #define PULSE_LEN	0x0F	/* 8    */
67 
68 /* controls */
69 #define ENABLE_WD1	0x0001
70 #define ENABLE_WD2	0x0002
71 #define RESET_WD1	0x0010
72 #define RESET_WD2	0x0020
73 #define GEN_SCI		0x0100
74 #define GEN_NMI		0x0200
75 #define GEN_SMI		0x0400
76 #define GEN_RESET	0x0800
77 
78 
79 /* utilities */
80 
81 #define WD1	0
82 #define WD2	1
83 
84 #define zf_writew(port, data)  { outb(port, INDEX); outw(data, DATA_W); }
85 #define zf_writeb(port, data)  { outb(port, INDEX); outb(data, DATA_B); }
86 #define zf_get_ZFL_version()   zf_readw(ZFL_VERSION)
87 
88 
zf_readw(unsigned char port)89 static unsigned short zf_readw(unsigned char port)
90 {
91 	outb(port, INDEX);
92 	return inw(DATA_W);
93 }
94 
zf_readb(unsigned char port)95 static unsigned short zf_readb(unsigned char port)
96 {
97 	outb(port, INDEX);
98 	return inb(DATA_B);
99 }
100 
101 
102 MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
103 MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
104 MODULE_LICENSE("GPL");
105 MODULE_PARM(action, "i");
106 MODULE_PARM_DESC(action, "after watchdog resets, generate: 0 = RESET(*)  1 = SMI  2 = NMI  3 = SCI");
107 
108 #ifdef CONFIG_WATCHDOG_NOWAYOUT
109 static int nowayout = 1;
110 #else
111 static int nowayout = 0;
112 #endif
113 
114 MODULE_PARM(nowayout,"i");
115 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
116 
117 #define PFX "machzwd"
118 
119 static struct watchdog_info zf_info = {
120 	options:		WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
121 	firmware_version:	1,
122 	identity:		"ZF-Logic watchdog"
123 };
124 
125 
126 /*
127  * action refers to action taken when watchdog resets
128  * 0 = GEN_RESET
129  * 1 = GEN_SMI
130  * 2 = GEN_NMI
131  * 3 = GEN_SCI
132  * defaults to GEN_RESET (0)
133  */
134 static int action = 0;
135 static int zf_action = GEN_RESET;
136 static int zf_is_open = 0;
137 static int zf_expect_close = 0;
138 static spinlock_t zf_lock;
139 static spinlock_t zf_port_lock;
140 static struct timer_list zf_timer;
141 static unsigned long next_heartbeat = 0;
142 
143 
144 /* timeout for user land heart beat (10 seconds) */
145 #define ZF_USER_TIMEO (HZ*10)
146 
147 /* timeout for hardware watchdog (~500ms) */
148 #define ZF_HW_TIMEO (HZ/2)
149 
150 /* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */
151 #define ZF_CTIMEOUT 0xffff
152 
153 #ifndef ZF_DEBUG
154 #	define dprintk(format, args...)
155 #else
156 #	define dprintk(format, args...) printk(KERN_DEBUG PFX; ":" __FUNCTION__ ":%d: " format, __LINE__ , ## args)
157 #endif
158 
159 
160 /* STATUS register functions */
161 
zf_get_status(void)162 static inline unsigned char zf_get_status(void)
163 {
164 	return zf_readb(STATUS);
165 }
166 
zf_set_status(unsigned char new)167 static inline void zf_set_status(unsigned char new)
168 {
169 	zf_writeb(STATUS, new);
170 }
171 
172 
173 /* CONTROL register functions */
174 
zf_get_control(void)175 static inline unsigned short zf_get_control(void)
176 {
177 	return zf_readw(CONTROL);
178 }
179 
zf_set_control(unsigned short new)180 static inline void zf_set_control(unsigned short new)
181 {
182 	zf_writew(CONTROL, new);
183 }
184 
185 
186 /* WD#? counter functions */
187 /*
188  *	Just get current counter value
189  */
190 
zf_get_timer(unsigned char n)191 static inline unsigned short zf_get_timer(unsigned char n)
192 {
193 	switch(n){
194 		case WD1:
195 			return zf_readw(COUNTER_1);
196 		case WD2:
197 			return zf_readb(COUNTER_2);
198 		default:
199 			return 0;
200 	}
201 }
202 
203 /*
204  *	Just set counter value
205  */
206 
zf_set_timer(unsigned short new,unsigned char n)207 static inline void zf_set_timer(unsigned short new, unsigned char n)
208 {
209 	switch(n){
210 		case WD1:
211 			zf_writew(COUNTER_1, new);
212 		case WD2:
213 			zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
214 		default:
215 			return;
216 	}
217 }
218 
219 /*
220  * stop hardware timer
221  */
zf_timer_off(void)222 static void zf_timer_off(void)
223 {
224 	unsigned int ctrl_reg = 0;
225 	unsigned long flags;
226 
227 	/* stop internal ping */
228 	del_timer_sync(&zf_timer);
229 
230 	spin_lock_irqsave(&zf_port_lock, flags);
231 	/* stop watchdog timer */
232 	ctrl_reg = zf_get_control();
233 	ctrl_reg |= (ENABLE_WD1|ENABLE_WD2);	/* disable wd1 and wd2 */
234 	ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
235 	zf_set_control(ctrl_reg);
236 	spin_unlock_irqrestore(&zf_port_lock, flags);
237 
238 	printk(KERN_INFO PFX ": Watchdog timer is now disabled\n");
239 }
240 
241 
242 /*
243  * start hardware timer
244  */
zf_timer_on(void)245 static void zf_timer_on(void)
246 {
247 	unsigned int ctrl_reg = 0;
248 	unsigned long flags;
249 
250 	spin_lock_irqsave(&zf_port_lock, flags);
251 
252 	zf_writeb(PULSE_LEN, 0xff);
253 
254 	zf_set_timer(ZF_CTIMEOUT, WD1);
255 
256 	/* user land ping */
257 	next_heartbeat = jiffies + ZF_USER_TIMEO;
258 
259 	/* start the timer for internal ping */
260 	zf_timer.expires = jiffies + ZF_HW_TIMEO;
261 
262 	add_timer(&zf_timer);
263 
264 	/* start watchdog timer */
265 	ctrl_reg = zf_get_control();
266 	ctrl_reg |= (ENABLE_WD1|zf_action);
267 	zf_set_control(ctrl_reg);
268 	spin_unlock_irqrestore(&zf_port_lock, flags);
269 
270 	printk(KERN_INFO PFX ": Watchdog timer is now enabled\n");
271 }
272 
273 
zf_ping(unsigned long data)274 static void zf_ping(unsigned long data)
275 {
276 	unsigned int ctrl_reg = 0;
277 	unsigned long flags;
278 
279 	zf_writeb(COUNTER_2, 0xff);
280 
281 	if(time_before(jiffies, next_heartbeat)){
282 
283 		dprintk("time_before: %ld\n", next_heartbeat - jiffies);
284 
285 		/*
286 		 * reset event is activated by transition from 0 to 1 on
287 		 * RESET_WD1 bit and we assume that it is already zero...
288 		 */
289 
290 		spin_lock_irqsave(&zf_port_lock, flags);
291 		ctrl_reg = zf_get_control();
292 		ctrl_reg |= RESET_WD1;
293 		zf_set_control(ctrl_reg);
294 
295 		/* ...and nothing changes until here */
296 		ctrl_reg &= ~(RESET_WD1);
297 		zf_set_control(ctrl_reg);
298 		spin_unlock_irqrestore(&zf_port_lock, flags);
299 
300 		zf_timer.expires = jiffies + ZF_HW_TIMEO;
301 		add_timer(&zf_timer);
302 	}else{
303 		printk(KERN_CRIT PFX ": I will reset your machine\n");
304 	}
305 }
306 
zf_write(struct file * file,const char * buf,size_t count,loff_t * ppos)307 static ssize_t zf_write(struct file *file, const char *buf, size_t count,
308 								loff_t *ppos)
309 {
310 	/*  Can't seek (pwrite) on this device  */
311 	if (ppos != &file->f_pos)
312 		return -ESPIPE;
313 
314 	/* See if we got the magic character */
315 	if(count){
316 
317 		/*
318 		 * no need to check for close confirmation
319 		 * no way to disable watchdog ;)
320 		 */
321 		if (!nowayout) {
322 			size_t ofs;
323 
324 			/*
325 			 * note: just in case someone wrote the
326 			 * magic character five months ago...
327 			 */
328 			zf_expect_close = 0;
329 
330 			/* now scan */
331 			for(ofs = 0; ofs != count; ofs++){
332 				char c;
333 				if(get_user(c, buf+ofs))
334 					return -EFAULT;
335 				if(c == 'V'){
336 					zf_expect_close = 1;
337 					dprintk("zf_expect_close 1\n");
338 				}
339 			}
340 		}
341 		/*
342 		 * Well, anyhow someone wrote to us,
343 		 * we should return that favour
344 		 */
345 		next_heartbeat = jiffies + ZF_USER_TIMEO;
346 		dprintk("user ping at %ld\n", jiffies);
347 
348 		return 1;
349 	}
350 
351 	return 0;
352 }
353 
zf_read(struct file * file,char * buf,size_t count,loff_t * ppos)354 static ssize_t zf_read(struct file *file, char *buf, size_t count,
355 							loff_t *ppos)
356 {
357 	return -EINVAL;
358 }
359 
360 
361 
zf_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)362 static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
363 	unsigned long arg)
364 {
365 	switch(cmd){
366 		case WDIOC_GETSUPPORT:
367 			if (copy_to_user((struct watchdog_info *)arg,
368 					 &zf_info, sizeof(zf_info)))
369 				return -EFAULT;
370 			break;
371 
372 		case WDIOC_GETSTATUS:
373 			if (copy_to_user((int *)arg, &zf_is_open, sizeof(int)))
374 				return -EFAULT;
375 			break;
376 
377 		case WDIOC_KEEPALIVE:
378 			zf_ping(0);
379 			break;
380 
381 		default:
382 			return -ENOTTY;
383 	}
384 
385 	return 0;
386 }
387 
zf_open(struct inode * inode,struct file * file)388 static int zf_open(struct inode *inode, struct file *file)
389 {
390 	switch(MINOR(inode->i_rdev)){
391 		case WATCHDOG_MINOR:
392 			spin_lock(&zf_lock);
393 			if(zf_is_open){
394 				spin_unlock(&zf_lock);
395 				return -EBUSY;
396 			}
397 
398 			if (nowayout) {
399 				MOD_INC_USE_COUNT;
400 			}
401 			zf_is_open = 1;
402 
403 			spin_unlock(&zf_lock);
404 
405 			zf_timer_on();
406 
407 			return 0;
408 		default:
409 			return -ENODEV;
410 	}
411 }
412 
zf_close(struct inode * inode,struct file * file)413 static int zf_close(struct inode *inode, struct file *file)
414 {
415 	if(MINOR(inode->i_rdev) == WATCHDOG_MINOR){
416 
417 		if(zf_expect_close){
418 			zf_timer_off();
419 		} else {
420 			del_timer(&zf_timer);
421 			printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n");
422 		}
423 
424 		spin_lock(&zf_lock);
425 		zf_is_open = 0;
426 		spin_unlock(&zf_lock);
427 
428 		zf_expect_close = 0;
429 	}
430 
431 	return 0;
432 }
433 
434 /*
435  * Notifier for system down
436  */
437 
zf_notify_sys(struct notifier_block * this,unsigned long code,void * unused)438 static int zf_notify_sys(struct notifier_block *this, unsigned long code,
439 								void *unused)
440 {
441 	if(code == SYS_DOWN || code == SYS_HALT){
442 		zf_timer_off();
443 	}
444 
445 	return NOTIFY_DONE;
446 }
447 
448 
449 
450 
451 static struct file_operations zf_fops = {
452 	owner:          THIS_MODULE,
453 	read:           zf_read,
454 	write:          zf_write,
455 	ioctl:          zf_ioctl,
456 	open:           zf_open,
457 	release:        zf_close,
458 };
459 
460 static struct miscdevice zf_miscdev = {
461 	WATCHDOG_MINOR,
462 	"watchdog",
463 	&zf_fops
464 };
465 
466 
467 /*
468  * The device needs to learn about soft shutdowns in order to
469  * turn the timebomb registers off.
470  */
471 static struct notifier_block zf_notifier = {
472 	zf_notify_sys,
473 	NULL,
474 	0
475 };
476 
zf_show_action(int act)477 static void __init zf_show_action(int act)
478 {
479 	char *str[] = { "RESET", "SMI", "NMI", "SCI" };
480 
481 	printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]);
482 }
483 
zf_init(void)484 static int __init zf_init(void)
485 {
486 	int ret;
487 
488 	printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n");
489 
490 	ret = zf_get_ZFL_version();
491 	printk("%#x\n", ret);
492 	if((!ret) || (ret != 0xffff)){
493 		printk(KERN_WARNING PFX ": no ZF-Logic found\n");
494 		return -ENODEV;
495 	}
496 
497 	if((action <= 3) && (action >= 0)){
498 		zf_action = zf_action>>action;
499 	} else
500 		action = 0;
501 
502 	zf_show_action(action);
503 
504 	spin_lock_init(&zf_lock);
505 	spin_lock_init(&zf_port_lock);
506 
507 	ret = misc_register(&zf_miscdev);
508 	if (ret){
509 		printk(KERN_ERR "can't misc_register on minor=%d\n",
510 							WATCHDOG_MINOR);
511 		goto out;
512 	}
513 
514 	if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){
515 		printk(KERN_ERR "cannot reserve I/O ports at %d\n",
516 							ZF_IOBASE);
517 		ret = -EBUSY;
518 		goto no_region;
519 	}
520 
521 	ret = register_reboot_notifier(&zf_notifier);
522 	if(ret){
523 		printk(KERN_ERR "can't register reboot notifier (err=%d)\n",
524 									ret);
525 		goto no_reboot;
526 	}
527 
528 	zf_set_status(0);
529 	zf_set_control(0);
530 
531 	/* this is the timer that will do the hard work */
532 	init_timer(&zf_timer);
533 	zf_timer.function = zf_ping;
534 	zf_timer.data = 0;
535 
536 	return 0;
537 
538 no_reboot:
539 	release_region(ZF_IOBASE, 3);
540 no_region:
541 	misc_deregister(&zf_miscdev);
542 out:
543 	return ret;
544 }
545 
546 
zf_exit(void)547 void __exit zf_exit(void)
548 {
549 	zf_timer_off();
550 
551 	misc_deregister(&zf_miscdev);
552 	unregister_reboot_notifier(&zf_notifier);
553 	release_region(ZF_IOBASE, 3);
554 }
555 
556 module_init(zf_init);
557 module_exit(zf_exit);
558