1/*
2 *	Wdt977	0.02:	A Watchdog Device for Netwinder W83977AF chip
3 *
4 *	(c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>)
5 *
6 *			-----------------------
7 *
8 *	This program is free software; you can redistribute it and/or
9 *	modify it under the terms of the GNU General Public License
10 *	as published by the Free Software Foundation; either version
11 *	2 of the License, or (at your option) any later version.
12 *
13 *			-----------------------
14 *      14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
15 *           Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
16 */
17
18#include <linux/module.h>
19#include <linux/config.h>
20#include <linux/types.h>
21#include <linux/kernel.h>
22#include <linux/fs.h>
23#include <linux/miscdevice.h>
24#include <linux/init.h>
25#include <linux/smp_lock.h>
26
27#include <asm/io.h>
28#include <asm/system.h>
29#include <asm/mach-types.h>
30
31#define WATCHDOG_MINOR	130
32
33static	int timeout = 3;
34static	int timer_alive;
35static	int testmode;
36static	int expect_close = 0;
37
38#ifdef CONFIG_WATCHDOG_NOWAYOUT
39static int nowayout = 1;
40#else
41static int nowayout = 0;
42#endif
43
44MODULE_PARM(nowayout,"i");
45MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
46
47
48/*
49 *	Allow only one person to hold it open
50 */
51
52static int wdt977_open(struct inode *inode, struct file *file)
53{
54	if(timer_alive)
55		return -EBUSY;
56	if (nowayout) {
57		MOD_INC_USE_COUNT;
58	}
59	timer_alive++;
60
61	//max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog.
62	if (timeout>255)
63	    timeout = 255;
64
65	printk(KERN_INFO "Watchdog: active, current timeout %d min.\n",timeout);
66
67	// unlock the SuperIO chip
68	outb(0x87,0x370);
69	outb(0x87,0x370);
70
71	//select device Aux2 (device=8) and set watchdog regs F2, F3 and F4
72	//F2 has the timeout in minutes
73	//F3 could be set to the POWER LED blink (with GP17 set to PowerLed)
74	//   at timeout, and to reset timer on kbd/mouse activity (not now)
75	//F4 is used to just clear the TIMEOUT'ed state (bit 0)
76
77	outb(0x07,0x370);
78	outb(0x08,0x371);
79	outb(0xF2,0x370);
80	outb(timeout,0x371);
81	outb(0xF3,0x370);
82	outb(0x00,0x371);	//another setting is 0E for kbd/mouse/LED
83	outb(0xF4,0x370);
84	outb(0x00,0x371);
85
86	//at last select device Aux1 (dev=7) and set GP16 as a watchdog output
87	if (!testmode)
88	{
89		outb(0x07,0x370);
90		outb(0x07,0x371);
91		outb(0xE6,0x370);
92		outb(0x08,0x371);
93	}
94
95	// lock the SuperIO chip
96	outb(0xAA,0x370);
97
98	return 0;
99}
100
101static int wdt977_release(struct inode *inode, struct file *file)
102{
103	/*
104	 *	Shut off the timer.
105	 * 	Lock it in if it's a module and we set nowayout
106	 */
107	lock_kernel();
108	if (expect_close) {
109
110		// unlock the SuperIO chip
111		outb(0x87,0x370);
112		outb(0x87,0x370);
113
114		//select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
115		//F3 is reset to its default state
116		//F4 can clear the TIMEOUT'ed state (bit 0) - back to default
117		//We can not use GP17 as a PowerLed, as we use its usage as a RedLed
118
119		outb(0x07,0x370);
120		outb(0x08,0x371);
121		outb(0xF2,0x370);
122		outb(0xFF,0x371);
123		outb(0xF3,0x370);
124		outb(0x00,0x371);
125		outb(0xF4,0x370);
126		outb(0x00,0x371);
127		outb(0xF2,0x370);
128		outb(0x00,0x371);
129
130		//at last select device Aux1 (dev=7) and set GP16 as a watchdog output
131		outb(0x07,0x370);
132		outb(0x07,0x371);
133		outb(0xE6,0x370);
134		outb(0x08,0x371);
135
136		// lock the SuperIO chip
137		outb(0xAA,0x370);
138		printk(KERN_INFO "Watchdog: shutdown.\n");
139	} else {
140		printk(KERN_CRIT "WDT device closed unexpectedly.  WDT will not stop!\n");
141	}
142
143	timer_alive=0;
144	unlock_kernel();
145
146	return 0;
147}
148
149static ssize_t wdt977_write(struct file *file, const char *data, size_t len, loff_t *ppos)
150{
151	if (!nowayout) {
152		size_t i;
153
154		/* In case it was set long ago */
155		expect_close = 0;
156
157		for (i = 0; i != len; i++) {
158			char c;
159			if (get_user(c, data + i))
160				return -EFAULT;
161			if (c == 'V')
162				expect_close = 1;
163		}
164	}
165
166	//max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog.
167	if (timeout>255)
168	    timeout = 255;
169
170	/*
171	 *	Refresh the timer.
172	 */
173
174	//we have a hw bug somewhere, so each 977 minute is actually only 30sec
175	//as such limit the max timeout to half of max of 255 minutes...
176//	if (timeout>126)
177//	    timeout = 126;
178
179	// unlock the SuperIO chip
180	outb(0x87,0x370);
181	outb(0x87,0x370);
182
183	//select device Aux2 (device=8) and kicks watchdog reg F2
184	//F2 has the timeout in minutes
185
186	outb(0x07,0x370);
187	outb(0x08,0x371);
188	outb(0xF2,0x370);
189	outb(timeout,0x371);
190
191	// lock the SuperIO chip
192	outb(0xAA,0x370);
193
194	return 1;
195}
196
197static struct file_operations wdt977_fops=
198{
199	owner:		THIS_MODULE,
200	write:		wdt977_write,
201	open:		wdt977_open,
202	release:	wdt977_release,
203};
204
205static struct miscdevice wdt977_miscdev=
206{
207	WATCHDOG_MINOR,
208	"watchdog",
209	&wdt977_fops
210};
211
212static int __init nwwatchdog_init(void)
213{
214	if (!machine_is_netwinder())
215		return -ENODEV;
216
217	misc_register(&wdt977_miscdev);
218	printk(KERN_INFO "NetWinder Watchdog sleeping.\n");
219	return 0;
220}
221
222static void __exit nwwatchdog_exit(void)
223{
224	misc_deregister(&wdt977_miscdev);
225}
226
227EXPORT_NO_SYMBOLS;
228
229module_init(nwwatchdog_init);
230module_exit(nwwatchdog_exit);
231
232MODULE_LICENSE("GPL");
233