xref: /OK3568_Linux_fs/kernel/drivers/watchdog/w83977f_wdt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *	W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *	(c) Copyright 2005  Jose Goncalves <jose.goncalves@inov.pt>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *      Based on w83877f_wdt.c by Scott Jennings,
8*4882a593Smuzhiyun  *           and wdt977.c by Woody Suwalski
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  *			-----------------------
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/moduleparam.h>
17*4882a593Smuzhiyun #include <linux/types.h>
18*4882a593Smuzhiyun #include <linux/kernel.h>
19*4882a593Smuzhiyun #include <linux/fs.h>
20*4882a593Smuzhiyun #include <linux/miscdevice.h>
21*4882a593Smuzhiyun #include <linux/init.h>
22*4882a593Smuzhiyun #include <linux/ioport.h>
23*4882a593Smuzhiyun #include <linux/watchdog.h>
24*4882a593Smuzhiyun #include <linux/notifier.h>
25*4882a593Smuzhiyun #include <linux/reboot.h>
26*4882a593Smuzhiyun #include <linux/uaccess.h>
27*4882a593Smuzhiyun #include <linux/io.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define WATCHDOG_VERSION  "1.00"
31*4882a593Smuzhiyun #define WATCHDOG_NAME     "W83977F WDT"
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #define IO_INDEX_PORT     0x3F0
34*4882a593Smuzhiyun #define IO_DATA_PORT      (IO_INDEX_PORT+1)
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define UNLOCK_DATA       0x87
37*4882a593Smuzhiyun #define LOCK_DATA         0xAA
38*4882a593Smuzhiyun #define DEVICE_REGISTER   0x07
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define	DEFAULT_TIMEOUT   45		/* default timeout in seconds */
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun static	int timeout = DEFAULT_TIMEOUT;
43*4882a593Smuzhiyun static	int timeoutW;			/* timeout in watchdog counter units */
44*4882a593Smuzhiyun static	unsigned long timer_alive;
45*4882a593Smuzhiyun static	int testmode;
46*4882a593Smuzhiyun static	char expect_close;
47*4882a593Smuzhiyun static	DEFINE_SPINLOCK(spinlock);
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun module_param(timeout, int, 0);
50*4882a593Smuzhiyun MODULE_PARM_DESC(timeout,
51*4882a593Smuzhiyun 		"Watchdog timeout in seconds (15..7635), default="
52*4882a593Smuzhiyun 				__MODULE_STRING(DEFAULT_TIMEOUT) ")");
53*4882a593Smuzhiyun module_param(testmode, int, 0);
54*4882a593Smuzhiyun MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun static bool nowayout = WATCHDOG_NOWAYOUT;
57*4882a593Smuzhiyun module_param(nowayout, bool, 0);
58*4882a593Smuzhiyun MODULE_PARM_DESC(nowayout,
59*4882a593Smuzhiyun 		"Watchdog cannot be stopped once started (default="
60*4882a593Smuzhiyun 				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun /*
63*4882a593Smuzhiyun  * Start the watchdog
64*4882a593Smuzhiyun  */
65*4882a593Smuzhiyun 
wdt_start(void)66*4882a593Smuzhiyun static int wdt_start(void)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	unsigned long flags;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	spin_lock_irqsave(&spinlock, flags);
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	/* Unlock the SuperIO chip */
73*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
74*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	/*
77*4882a593Smuzhiyun 	 * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4.
78*4882a593Smuzhiyun 	 * F2 has the timeout in watchdog counter units.
79*4882a593Smuzhiyun 	 * F3 is set to enable watchdog LED blink at timeout.
80*4882a593Smuzhiyun 	 * F4 is used to just clear the TIMEOUT'ed state (bit 0).
81*4882a593Smuzhiyun 	 */
82*4882a593Smuzhiyun 	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
83*4882a593Smuzhiyun 	outb_p(0x08, IO_DATA_PORT);
84*4882a593Smuzhiyun 	outb_p(0xF2, IO_INDEX_PORT);
85*4882a593Smuzhiyun 	outb_p(timeoutW, IO_DATA_PORT);
86*4882a593Smuzhiyun 	outb_p(0xF3, IO_INDEX_PORT);
87*4882a593Smuzhiyun 	outb_p(0x08, IO_DATA_PORT);
88*4882a593Smuzhiyun 	outb_p(0xF4, IO_INDEX_PORT);
89*4882a593Smuzhiyun 	outb_p(0x00, IO_DATA_PORT);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	/* Set device Aux2 active */
92*4882a593Smuzhiyun 	outb_p(0x30, IO_INDEX_PORT);
93*4882a593Smuzhiyun 	outb_p(0x01, IO_DATA_PORT);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	/*
96*4882a593Smuzhiyun 	 * Select device Aux1 (dev=7) to set GP16 as the watchdog output
97*4882a593Smuzhiyun 	 * (in reg E6) and GP13 as the watchdog LED output (in reg E3).
98*4882a593Smuzhiyun 	 * Map GP16 at pin 119.
99*4882a593Smuzhiyun 	 * In test mode watch the bit 0 on F4 to indicate "triggered" or
100*4882a593Smuzhiyun 	 * check watchdog LED on SBC.
101*4882a593Smuzhiyun 	 */
102*4882a593Smuzhiyun 	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
103*4882a593Smuzhiyun 	outb_p(0x07, IO_DATA_PORT);
104*4882a593Smuzhiyun 	if (!testmode) {
105*4882a593Smuzhiyun 		unsigned pin_map;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 		outb_p(0xE6, IO_INDEX_PORT);
108*4882a593Smuzhiyun 		outb_p(0x0A, IO_DATA_PORT);
109*4882a593Smuzhiyun 		outb_p(0x2C, IO_INDEX_PORT);
110*4882a593Smuzhiyun 		pin_map = inb_p(IO_DATA_PORT);
111*4882a593Smuzhiyun 		pin_map |= 0x10;
112*4882a593Smuzhiyun 		pin_map &= ~(0x20);
113*4882a593Smuzhiyun 		outb_p(0x2C, IO_INDEX_PORT);
114*4882a593Smuzhiyun 		outb_p(pin_map, IO_DATA_PORT);
115*4882a593Smuzhiyun 	}
116*4882a593Smuzhiyun 	outb_p(0xE3, IO_INDEX_PORT);
117*4882a593Smuzhiyun 	outb_p(0x08, IO_DATA_PORT);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	/* Set device Aux1 active */
120*4882a593Smuzhiyun 	outb_p(0x30, IO_INDEX_PORT);
121*4882a593Smuzhiyun 	outb_p(0x01, IO_DATA_PORT);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	/* Lock the SuperIO chip */
124*4882a593Smuzhiyun 	outb_p(LOCK_DATA, IO_INDEX_PORT);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	spin_unlock_irqrestore(&spinlock, flags);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	pr_info("activated\n");
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	return 0;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun /*
134*4882a593Smuzhiyun  * Stop the watchdog
135*4882a593Smuzhiyun  */
136*4882a593Smuzhiyun 
wdt_stop(void)137*4882a593Smuzhiyun static int wdt_stop(void)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	unsigned long flags;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	spin_lock_irqsave(&spinlock, flags);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	/* Unlock the SuperIO chip */
144*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
145*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	/*
148*4882a593Smuzhiyun 	 * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4.
149*4882a593Smuzhiyun 	 * F2 is reset to its default value (watchdog timer disabled).
150*4882a593Smuzhiyun 	 * F3 is reset to its default state.
151*4882a593Smuzhiyun 	 * F4 clears the TIMEOUT'ed state (bit 0) - back to default.
152*4882a593Smuzhiyun 	 */
153*4882a593Smuzhiyun 	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
154*4882a593Smuzhiyun 	outb_p(0x08, IO_DATA_PORT);
155*4882a593Smuzhiyun 	outb_p(0xF2, IO_INDEX_PORT);
156*4882a593Smuzhiyun 	outb_p(0xFF, IO_DATA_PORT);
157*4882a593Smuzhiyun 	outb_p(0xF3, IO_INDEX_PORT);
158*4882a593Smuzhiyun 	outb_p(0x00, IO_DATA_PORT);
159*4882a593Smuzhiyun 	outb_p(0xF4, IO_INDEX_PORT);
160*4882a593Smuzhiyun 	outb_p(0x00, IO_DATA_PORT);
161*4882a593Smuzhiyun 	outb_p(0xF2, IO_INDEX_PORT);
162*4882a593Smuzhiyun 	outb_p(0x00, IO_DATA_PORT);
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	/*
165*4882a593Smuzhiyun 	 * Select device Aux1 (dev=7) to set GP16 (in reg E6) and
166*4882a593Smuzhiyun 	 * Gp13 (in reg E3) as inputs.
167*4882a593Smuzhiyun 	 */
168*4882a593Smuzhiyun 	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
169*4882a593Smuzhiyun 	outb_p(0x07, IO_DATA_PORT);
170*4882a593Smuzhiyun 	if (!testmode) {
171*4882a593Smuzhiyun 		outb_p(0xE6, IO_INDEX_PORT);
172*4882a593Smuzhiyun 		outb_p(0x01, IO_DATA_PORT);
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 	outb_p(0xE3, IO_INDEX_PORT);
175*4882a593Smuzhiyun 	outb_p(0x01, IO_DATA_PORT);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	/* Lock the SuperIO chip */
178*4882a593Smuzhiyun 	outb_p(LOCK_DATA, IO_INDEX_PORT);
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	spin_unlock_irqrestore(&spinlock, flags);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	pr_info("shutdown\n");
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun /*
188*4882a593Smuzhiyun  * Send a keepalive ping to the watchdog
189*4882a593Smuzhiyun  * This is done by simply re-writing the timeout to reg. 0xF2
190*4882a593Smuzhiyun  */
191*4882a593Smuzhiyun 
wdt_keepalive(void)192*4882a593Smuzhiyun static int wdt_keepalive(void)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	unsigned long flags;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	spin_lock_irqsave(&spinlock, flags);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	/* Unlock the SuperIO chip */
199*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
200*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	/* Select device Aux2 (device=8) to kick watchdog reg F2 */
203*4882a593Smuzhiyun 	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
204*4882a593Smuzhiyun 	outb_p(0x08, IO_DATA_PORT);
205*4882a593Smuzhiyun 	outb_p(0xF2, IO_INDEX_PORT);
206*4882a593Smuzhiyun 	outb_p(timeoutW, IO_DATA_PORT);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	/* Lock the SuperIO chip */
209*4882a593Smuzhiyun 	outb_p(LOCK_DATA, IO_INDEX_PORT);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	spin_unlock_irqrestore(&spinlock, flags);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	return 0;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun /*
217*4882a593Smuzhiyun  * Set the watchdog timeout value
218*4882a593Smuzhiyun  */
219*4882a593Smuzhiyun 
wdt_set_timeout(int t)220*4882a593Smuzhiyun static int wdt_set_timeout(int t)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	unsigned int tmrval;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/*
225*4882a593Smuzhiyun 	 * Convert seconds to watchdog counter time units, rounding up.
226*4882a593Smuzhiyun 	 * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup
227*4882a593Smuzhiyun 	 * value. This information is supplied in the PCM-5335 manual and was
228*4882a593Smuzhiyun 	 * checked by me on a real board. This is a bit strange because W83977f
229*4882a593Smuzhiyun 	 * datasheet says counter unit is in minutes!
230*4882a593Smuzhiyun 	 */
231*4882a593Smuzhiyun 	if (t < 15)
232*4882a593Smuzhiyun 		return -EINVAL;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	tmrval = ((t + 15) + 29) / 30;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (tmrval > 255)
237*4882a593Smuzhiyun 		return -EINVAL;
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	/*
240*4882a593Smuzhiyun 	 * timeout is the timeout in seconds,
241*4882a593Smuzhiyun 	 * timeoutW is the timeout in watchdog counter units.
242*4882a593Smuzhiyun 	 */
243*4882a593Smuzhiyun 	timeoutW = tmrval;
244*4882a593Smuzhiyun 	timeout = (timeoutW * 30) - 15;
245*4882a593Smuzhiyun 	return 0;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun /*
249*4882a593Smuzhiyun  * Get the watchdog status
250*4882a593Smuzhiyun  */
251*4882a593Smuzhiyun 
wdt_get_status(int * status)252*4882a593Smuzhiyun static int wdt_get_status(int *status)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun 	int new_status;
255*4882a593Smuzhiyun 	unsigned long flags;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	spin_lock_irqsave(&spinlock, flags);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	/* Unlock the SuperIO chip */
260*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
261*4882a593Smuzhiyun 	outb_p(UNLOCK_DATA, IO_INDEX_PORT);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	/* Select device Aux2 (device=8) to read watchdog reg F4 */
264*4882a593Smuzhiyun 	outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
265*4882a593Smuzhiyun 	outb_p(0x08, IO_DATA_PORT);
266*4882a593Smuzhiyun 	outb_p(0xF4, IO_INDEX_PORT);
267*4882a593Smuzhiyun 	new_status = inb_p(IO_DATA_PORT);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	/* Lock the SuperIO chip */
270*4882a593Smuzhiyun 	outb_p(LOCK_DATA, IO_INDEX_PORT);
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	spin_unlock_irqrestore(&spinlock, flags);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	*status = 0;
275*4882a593Smuzhiyun 	if (new_status & 1)
276*4882a593Smuzhiyun 		*status |= WDIOF_CARDRESET;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	return 0;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun /*
283*4882a593Smuzhiyun  *	/dev/watchdog handling
284*4882a593Smuzhiyun  */
285*4882a593Smuzhiyun 
wdt_open(struct inode * inode,struct file * file)286*4882a593Smuzhiyun static int wdt_open(struct inode *inode, struct file *file)
287*4882a593Smuzhiyun {
288*4882a593Smuzhiyun 	/* If the watchdog is alive we don't need to start it again */
289*4882a593Smuzhiyun 	if (test_and_set_bit(0, &timer_alive))
290*4882a593Smuzhiyun 		return -EBUSY;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	if (nowayout)
293*4882a593Smuzhiyun 		__module_get(THIS_MODULE);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	wdt_start();
296*4882a593Smuzhiyun 	return stream_open(inode, file);
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
wdt_release(struct inode * inode,struct file * file)299*4882a593Smuzhiyun static int wdt_release(struct inode *inode, struct file *file)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	/*
302*4882a593Smuzhiyun 	 * Shut off the timer.
303*4882a593Smuzhiyun 	 * Lock it in if it's a module and we set nowayout
304*4882a593Smuzhiyun 	 */
305*4882a593Smuzhiyun 	if (expect_close == 42) {
306*4882a593Smuzhiyun 		wdt_stop();
307*4882a593Smuzhiyun 		clear_bit(0, &timer_alive);
308*4882a593Smuzhiyun 	} else {
309*4882a593Smuzhiyun 		wdt_keepalive();
310*4882a593Smuzhiyun 		pr_crit("unexpected close, not stopping watchdog!\n");
311*4882a593Smuzhiyun 	}
312*4882a593Smuzhiyun 	expect_close = 0;
313*4882a593Smuzhiyun 	return 0;
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun /*
317*4882a593Smuzhiyun  *      wdt_write:
318*4882a593Smuzhiyun  *      @file: file handle to the watchdog
319*4882a593Smuzhiyun  *      @buf: buffer to write (unused as data does not matter here
320*4882a593Smuzhiyun  *      @count: count of bytes
321*4882a593Smuzhiyun  *      @ppos: pointer to the position to write. No seeks allowed
322*4882a593Smuzhiyun  *
323*4882a593Smuzhiyun  *      A write to a watchdog device is defined as a keepalive signal. Any
324*4882a593Smuzhiyun  *      write of data will do, as we we don't define content meaning.
325*4882a593Smuzhiyun  */
326*4882a593Smuzhiyun 
wdt_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)327*4882a593Smuzhiyun static ssize_t wdt_write(struct file *file, const char __user *buf,
328*4882a593Smuzhiyun 			    size_t count, loff_t *ppos)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	/* See if we got the magic character 'V' and reload the timer */
331*4882a593Smuzhiyun 	if (count) {
332*4882a593Smuzhiyun 		if (!nowayout) {
333*4882a593Smuzhiyun 			size_t ofs;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 			/* note: just in case someone wrote the
336*4882a593Smuzhiyun 			   magic character long ago */
337*4882a593Smuzhiyun 			expect_close = 0;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 			/* scan to see whether or not we got the
340*4882a593Smuzhiyun 			   magic character */
341*4882a593Smuzhiyun 			for (ofs = 0; ofs != count; ofs++) {
342*4882a593Smuzhiyun 				char c;
343*4882a593Smuzhiyun 				if (get_user(c, buf + ofs))
344*4882a593Smuzhiyun 					return -EFAULT;
345*4882a593Smuzhiyun 				if (c == 'V')
346*4882a593Smuzhiyun 					expect_close = 42;
347*4882a593Smuzhiyun 			}
348*4882a593Smuzhiyun 		}
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 		/* someone wrote to us, we should restart timer */
351*4882a593Smuzhiyun 		wdt_keepalive();
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 	return count;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun /*
357*4882a593Smuzhiyun  *      wdt_ioctl:
358*4882a593Smuzhiyun  *      @inode: inode of the device
359*4882a593Smuzhiyun  *      @file: file handle to the device
360*4882a593Smuzhiyun  *      @cmd: watchdog command
361*4882a593Smuzhiyun  *      @arg: argument pointer
362*4882a593Smuzhiyun  *
363*4882a593Smuzhiyun  *      The watchdog API defines a common set of functions for all watchdogs
364*4882a593Smuzhiyun  *      according to their available features.
365*4882a593Smuzhiyun  */
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun static const struct watchdog_info ident = {
368*4882a593Smuzhiyun 	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
369*4882a593Smuzhiyun 	.firmware_version =	1,
370*4882a593Smuzhiyun 	.identity = WATCHDOG_NAME,
371*4882a593Smuzhiyun };
372*4882a593Smuzhiyun 
wdt_ioctl(struct file * file,unsigned int cmd,unsigned long arg)373*4882a593Smuzhiyun static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun 	int status;
376*4882a593Smuzhiyun 	int new_options, retval = -EINVAL;
377*4882a593Smuzhiyun 	int new_timeout;
378*4882a593Smuzhiyun 	union {
379*4882a593Smuzhiyun 		struct watchdog_info __user *ident;
380*4882a593Smuzhiyun 		int __user *i;
381*4882a593Smuzhiyun 	} uarg;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	uarg.i = (int __user *)arg;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	switch (cmd) {
386*4882a593Smuzhiyun 	case WDIOC_GETSUPPORT:
387*4882a593Smuzhiyun 		return copy_to_user(uarg.ident, &ident,
388*4882a593Smuzhiyun 						sizeof(ident)) ? -EFAULT : 0;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	case WDIOC_GETSTATUS:
391*4882a593Smuzhiyun 		wdt_get_status(&status);
392*4882a593Smuzhiyun 		return put_user(status, uarg.i);
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	case WDIOC_GETBOOTSTATUS:
395*4882a593Smuzhiyun 		return put_user(0, uarg.i);
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	case WDIOC_SETOPTIONS:
398*4882a593Smuzhiyun 		if (get_user(new_options, uarg.i))
399*4882a593Smuzhiyun 			return -EFAULT;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 		if (new_options & WDIOS_DISABLECARD) {
402*4882a593Smuzhiyun 			wdt_stop();
403*4882a593Smuzhiyun 			retval = 0;
404*4882a593Smuzhiyun 		}
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 		if (new_options & WDIOS_ENABLECARD) {
407*4882a593Smuzhiyun 			wdt_start();
408*4882a593Smuzhiyun 			retval = 0;
409*4882a593Smuzhiyun 		}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 		return retval;
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	case WDIOC_KEEPALIVE:
414*4882a593Smuzhiyun 		wdt_keepalive();
415*4882a593Smuzhiyun 		return 0;
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	case WDIOC_SETTIMEOUT:
418*4882a593Smuzhiyun 		if (get_user(new_timeout, uarg.i))
419*4882a593Smuzhiyun 			return -EFAULT;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 		if (wdt_set_timeout(new_timeout))
422*4882a593Smuzhiyun 			return -EINVAL;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 		wdt_keepalive();
425*4882a593Smuzhiyun 		fallthrough;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	case WDIOC_GETTIMEOUT:
428*4882a593Smuzhiyun 		return put_user(timeout, uarg.i);
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	default:
431*4882a593Smuzhiyun 		return -ENOTTY;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	}
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun 
wdt_notify_sys(struct notifier_block * this,unsigned long code,void * unused)436*4882a593Smuzhiyun static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
437*4882a593Smuzhiyun 	void *unused)
438*4882a593Smuzhiyun {
439*4882a593Smuzhiyun 	if (code == SYS_DOWN || code == SYS_HALT)
440*4882a593Smuzhiyun 		wdt_stop();
441*4882a593Smuzhiyun 	return NOTIFY_DONE;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun static const struct file_operations wdt_fops = {
445*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
446*4882a593Smuzhiyun 	.llseek		= no_llseek,
447*4882a593Smuzhiyun 	.write		= wdt_write,
448*4882a593Smuzhiyun 	.unlocked_ioctl	= wdt_ioctl,
449*4882a593Smuzhiyun 	.compat_ioctl	= compat_ptr_ioctl,
450*4882a593Smuzhiyun 	.open		= wdt_open,
451*4882a593Smuzhiyun 	.release	= wdt_release,
452*4882a593Smuzhiyun };
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun static struct miscdevice wdt_miscdev = {
455*4882a593Smuzhiyun 	.minor		= WATCHDOG_MINOR,
456*4882a593Smuzhiyun 	.name		= "watchdog",
457*4882a593Smuzhiyun 	.fops		= &wdt_fops,
458*4882a593Smuzhiyun };
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun static struct notifier_block wdt_notifier = {
461*4882a593Smuzhiyun 	.notifier_call = wdt_notify_sys,
462*4882a593Smuzhiyun };
463*4882a593Smuzhiyun 
w83977f_wdt_init(void)464*4882a593Smuzhiyun static int __init w83977f_wdt_init(void)
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun 	int rc;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	pr_info("driver v%s\n", WATCHDOG_VERSION);
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	/*
471*4882a593Smuzhiyun 	 * Check that the timeout value is within it's range;
472*4882a593Smuzhiyun 	 * if not reset to the default
473*4882a593Smuzhiyun 	 */
474*4882a593Smuzhiyun 	if (wdt_set_timeout(timeout)) {
475*4882a593Smuzhiyun 		wdt_set_timeout(DEFAULT_TIMEOUT);
476*4882a593Smuzhiyun 		pr_info("timeout value must be 15 <= timeout <= 7635, using %d\n",
477*4882a593Smuzhiyun 			DEFAULT_TIMEOUT);
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
481*4882a593Smuzhiyun 		pr_err("I/O address 0x%04x already in use\n", IO_INDEX_PORT);
482*4882a593Smuzhiyun 		rc = -EIO;
483*4882a593Smuzhiyun 		goto err_out;
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	rc = register_reboot_notifier(&wdt_notifier);
487*4882a593Smuzhiyun 	if (rc) {
488*4882a593Smuzhiyun 		pr_err("cannot register reboot notifier (err=%d)\n", rc);
489*4882a593Smuzhiyun 		goto err_out_region;
490*4882a593Smuzhiyun 	}
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	rc = misc_register(&wdt_miscdev);
493*4882a593Smuzhiyun 	if (rc) {
494*4882a593Smuzhiyun 		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
495*4882a593Smuzhiyun 		       wdt_miscdev.minor, rc);
496*4882a593Smuzhiyun 		goto err_out_reboot;
497*4882a593Smuzhiyun 	}
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	pr_info("initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
500*4882a593Smuzhiyun 		timeout, nowayout, testmode);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	return 0;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun err_out_reboot:
505*4882a593Smuzhiyun 	unregister_reboot_notifier(&wdt_notifier);
506*4882a593Smuzhiyun err_out_region:
507*4882a593Smuzhiyun 	release_region(IO_INDEX_PORT, 2);
508*4882a593Smuzhiyun err_out:
509*4882a593Smuzhiyun 	return rc;
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun 
w83977f_wdt_exit(void)512*4882a593Smuzhiyun static void __exit w83977f_wdt_exit(void)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun 	wdt_stop();
515*4882a593Smuzhiyun 	misc_deregister(&wdt_miscdev);
516*4882a593Smuzhiyun 	unregister_reboot_notifier(&wdt_notifier);
517*4882a593Smuzhiyun 	release_region(IO_INDEX_PORT, 2);
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun module_init(w83977f_wdt_init);
521*4882a593Smuzhiyun module_exit(w83977f_wdt_exit);
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");
524*4882a593Smuzhiyun MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");
525*4882a593Smuzhiyun MODULE_LICENSE("GPL");
526