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