File: /usr/src/linux/drivers/char/machzwd.c

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