1485ae77dc7f484563707557ccf8c5d228980619fSven Anders/*
2485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
3485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
429fa0586de4fe518f122a915b8c6e92d12e8ca7fAlan Cox *	Based on acquirewdt.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
5143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	and some other existing drivers
6485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
7485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	This program is free software; you can redistribute it and/or
8485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	modify it under the terms of the GNU General Public License
9485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	as published by the Free Software Foundation; either version
10485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	2 of the License, or (at your option) any later version.
118386c8cfb2131b2a9caae3db6bf94292bbbe1cafWim Van Sebroeck *
12485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	The authors do NOT admit liability nor provide warranty for
13485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	any of this software. This material is provided "AS-IS" in
14143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	the hope that it may be useful for others.
15485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
16485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	(C) Copyright 2003-2006  Sven Anders <anders@anduras.de>
17485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
18485ae77dc7f484563707557ccf8c5d228980619fSven Anders *  History:
19485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	2003 - Created version 1.0 for Linux 2.4.x.
20485ae77dc7f484563707557ccf8c5d228980619fSven Anders *	2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
21598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox *	       features. Released version 1.1
22485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
23485ae77dc7f484563707557ccf8c5d228980619fSven Anders *  Theory of operation:
24485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
25143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	A Watchdog Timer (WDT) is a hardware circuit that can
26143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	reset the computer system in case of a software fault.
27143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	You probably knew that already.
28485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
29143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	Usually a userspace daemon will notify the kernel WDT driver
30143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	via the /dev/watchdog special device file that userspace is
31143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	still alive, at regular intervals.  When such a notification
32143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	occurs, the driver will usually tell the hardware watchdog
33143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	that everything is in order, and that the watchdog should wait
34143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	for yet another little while to reset the system.
35143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	If userspace fails (RAM error, kernel bug, whatever), the
36143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	notifications cease to occur, and the hardware watchdog will
37143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *	reset the system (causing a reboot) after the timeout occurs.
38485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
39485ae77dc7f484563707557ccf8c5d228980619fSven Anders * Create device with:
40485ae77dc7f484563707557ccf8c5d228980619fSven Anders *  mknod /dev/watchdog c 10 130
41485ae77dc7f484563707557ccf8c5d228980619fSven Anders *
42485ae77dc7f484563707557ccf8c5d228980619fSven Anders * For an example userspace keep-alive daemon, see:
43395cf9691d72173d8cdaa613c5f0255f993af94bPaul Bolle *   Documentation/watchdog/wdt.txt
44485ae77dc7f484563707557ccf8c5d228980619fSven Anders */
45485ae77dc7f484563707557ccf8c5d228980619fSven Anders
4627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches
48485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/module.h>
49485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/moduleparam.h>
50485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/types.h>
51485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/miscdevice.h>
52485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/watchdog.h>
53485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/delay.h>
54485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/fs.h>
55485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/ioport.h>
56485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/notifier.h>
57485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/reboot.h>
58485ae77dc7f484563707557ccf8c5d228980619fSven Anders#include <linux/init.h>
59aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck#include <linux/spinlock.h>
60598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox#include <linux/io.h>
61598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox#include <linux/uaccess.h>
62485ae77dc7f484563707557ccf8c5d228980619fSven Anders
63485ae77dc7f484563707557ccf8c5d228980619fSven Anders
64485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* enable support for minutes as units? */
65485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* (does not always work correctly, so disabled by default!) */
66485ae77dc7f484563707557ccf8c5d228980619fSven Anders#define SMSC_SUPPORT_MINUTES
67485ae77dc7f484563707557ccf8c5d228980619fSven Anders#undef SMSC_SUPPORT_MINUTES
68485ae77dc7f484563707557ccf8c5d228980619fSven Anders
69485ae77dc7f484563707557ccf8c5d228980619fSven Anders#define MAX_TIMEOUT     255
70485ae77dc7f484563707557ccf8c5d228980619fSven Anders
71485ae77dc7f484563707557ccf8c5d228980619fSven Anders#define UNIT_SECOND     0
72485ae77dc7f484563707557ccf8c5d228980619fSven Anders#define UNIT_MINUTE     1
73485ae77dc7f484563707557ccf8c5d228980619fSven Anders
74598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox#define VERSION		"1.1"
75485ae77dc7f484563707557ccf8c5d228980619fSven Anders
76598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox#define IOPORT		0x3F0
77485ae77dc7f484563707557ccf8c5d228980619fSven Anders#define IOPORT_SIZE     2
78598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox#define IODEV_NO	8
79485ae77dc7f484563707557ccf8c5d228980619fSven Anders
80598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic int unit = UNIT_SECOND;	/* timer's unit */
81598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic int timeout = 60;	/* timeout value: default is 60 "units" */
82598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic unsigned long timer_enabled;   /* is the timer enabled? */
83485ae77dc7f484563707557ccf8c5d228980619fSven Anders
84485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic char expect_close;       /* is the close expected? */
85485ae77dc7f484563707557ccf8c5d228980619fSven Anders
86c7dfd0cca300c5dc49213cf1c78c77393600410dAlexey Dobriyanstatic DEFINE_SPINLOCK(io_lock);/* to guard the watchdog from io races */
87aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck
8886a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
89485ae77dc7f484563707557ccf8c5d228980619fSven Anders
90485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- Low level function ----------------------------------------*/
91485ae77dc7f484563707557ccf8c5d228980619fSven Anders
92485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* unlock the IO chip */
93485ae77dc7f484563707557ccf8c5d228980619fSven Anders
94485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void open_io_config(void)
95485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
96598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(0x55, IOPORT);
97485ae77dc7f484563707557ccf8c5d228980619fSven Anders	mdelay(1);
98598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(0x55, IOPORT);
99485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
100485ae77dc7f484563707557ccf8c5d228980619fSven Anders
101485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* lock the IO chip */
102485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void close_io_config(void)
103485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
104598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(0xAA, IOPORT);
105485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
106485ae77dc7f484563707557ccf8c5d228980619fSven Anders
107485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* select the IO device */
108485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void select_io_device(unsigned char devno)
109485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
110598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(0x07, IOPORT);
111598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(devno, IOPORT+1);
112485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
113485ae77dc7f484563707557ccf8c5d228980619fSven Anders
114485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* write to the control register */
115485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void write_io_cr(unsigned char reg, unsigned char data)
116485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
117598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(reg, IOPORT);
118598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(data, IOPORT+1);
119485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
120485ae77dc7f484563707557ccf8c5d228980619fSven Anders
121485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* read from the control register */
122485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline char read_io_cr(unsigned char reg)
123485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
124598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	outb(reg, IOPORT);
125598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	return inb(IOPORT+1);
126485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
127485ae77dc7f484563707557ccf8c5d228980619fSven Anders
128485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- Medium level functions ------------------------------------*/
129485ae77dc7f484563707557ccf8c5d228980619fSven Anders
130485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void gpio_bit12(unsigned char reg)
131485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
132598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* -- General Purpose I/O Bit 1.2 --
133598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 0,   In/Out: 0 = Output, 1 = Input
134598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 1,   Polarity: 0 = No Invert, 1 = Invert
135598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 2,   Group Enable Intr.: 0 = Disable, 1 = Enable
136598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
137598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *                           11 = Either Edge Triggered Intr. 2
138598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 5/6  (Reserved)
139598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 7,   Output Type: 0 = Push Pull Bit, 1 = Open Drain
140598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 */
141598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	write_io_cr(0xE2, reg);
142485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
143485ae77dc7f484563707557ccf8c5d228980619fSven Anders
144485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void gpio_bit13(unsigned char reg)
145485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
146598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* -- General Purpose I/O Bit 1.3 --
147598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 0,  In/Out: 0 = Output, 1 = Input
148598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 1,  Polarity: 0 = No Invert, 1 = Invert
149598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 2,  Group Enable Intr.: 0 = Disable, 1 = Enable
150598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 3,  Function select: 0 = GPI/O, 1 = LED
151598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 4-6 (Reserved)
152598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 7,  Output Type: 0 = Push Pull Bit, 1 = Open Drain
153598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 */
154598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	write_io_cr(0xE3, reg);
155485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
156485ae77dc7f484563707557ccf8c5d228980619fSven Anders
157485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void wdt_timer_units(unsigned char new_units)
158485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
159598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* -- Watchdog timer units --
160598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 0-6 (Reserved)
161598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 7,  WDT Time-out Value Units Select
162598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *         (0 = Minutes, 1 = Seconds)
163598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 */
164598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	write_io_cr(0xF1, new_units);
165485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
166485ae77dc7f484563707557ccf8c5d228980619fSven Anders
167485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void wdt_timeout_value(unsigned char new_timeout)
168485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
169598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* -- Watchdog Timer Time-out Value --
170598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 0-7 Binary coded units (0=Disabled, 1..255)
171598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 */
172598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	write_io_cr(0xF2, new_timeout);
173485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
174485ae77dc7f484563707557ccf8c5d228980619fSven Anders
175485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void wdt_timer_conf(unsigned char conf)
176485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
177598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* -- Watchdog timer configuration --
178598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 0   Joystick enable: 0* = No Reset, 1 = Reset WDT upon
179598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *							Gameport I/O
180598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 1   Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
181598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 2   Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr
182598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 3   Reset the timer
183598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *         (Wrong in SMsC documentation? Given as: PowerLED Timout
184598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *							Enabled)
185598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
186598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *            0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
187598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 */
188598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	write_io_cr(0xF3, conf);
189485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
190485ae77dc7f484563707557ccf8c5d228980619fSven Anders
191485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic inline void wdt_timer_ctrl(unsigned char reg)
192485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
193598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* -- Watchdog timer control --
19425985edcedea6396277003854657b5f3cb31a628Lucas De Marchi	 * Bit 0   Status Bit: 0 = Timer counting, 1 = Timeout occurred
195598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 1   Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
196598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 2   Force Timeout: 1 = Forces WD timeout event (self-cleaning)
197598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 3   P20 Force Timeout enabled:
198598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *          0 = P20 activity does not generate the WD timeout event
199598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *          1 = P20 Allows rising edge of P20, from the keyboard
200598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *              controller, to force the WD timeout event.
201598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 4   (Reserved)
202598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * -- Soft power management --
203598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 5   Stop Counter: 1 = Stop software power down counter
204598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *            set via register 0xB8, (self-cleaning)
205598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *            (Upon read: 0 = Counter running, 1 = Counter stopped)
206598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 6   Restart Counter: 1 = Restart software power down counter
207598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 *            set via register 0xB8, (self-cleaning)
208598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 * Bit 7   SPOFF: 1 = Force software power down (self-cleaning)
209598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	 */
210598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	write_io_cr(0xF4, reg);
211485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
212485ae77dc7f484563707557ccf8c5d228980619fSven Anders
213485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- Higher level functions ------------------------------------*/
214485ae77dc7f484563707557ccf8c5d228980619fSven Anders
215485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* initialize watchdog */
216485ae77dc7f484563707557ccf8c5d228980619fSven Anders
217485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void wb_smsc_wdt_initialize(void)
218485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
219598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	unsigned char old;
220485ae77dc7f484563707557ccf8c5d228980619fSven Anders
221aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_lock(&io_lock);
222598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	open_io_config();
223598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	select_io_device(IODEV_NO);
224485ae77dc7f484563707557ccf8c5d228980619fSven Anders
225598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* enable the watchdog */
226598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	gpio_bit13(0x08);  /* Select pin 80 = LED not GPIO */
227598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	gpio_bit12(0x0A);  /* Set pin 79 = WDT not
228598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox			      GPIO/Output/Polarity=Invert */
229598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* disable the timeout */
230598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timeout_value(0);
231485ae77dc7f484563707557ccf8c5d228980619fSven Anders
232598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* reset control register */
233598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timer_ctrl(0x00);
234485ae77dc7f484563707557ccf8c5d228980619fSven Anders
235598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* reset configuration register */
236485ae77dc7f484563707557ccf8c5d228980619fSven Anders	wdt_timer_conf(0x00);
237485ae77dc7f484563707557ccf8c5d228980619fSven Anders
238598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* read old (timer units) register */
239598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	old = read_io_cr(0xF1) & 0x7F;
240598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	if (unit == UNIT_SECOND)
241598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		old |= 0x80;	/* set to seconds */
242485ae77dc7f484563707557ccf8c5d228980619fSven Anders
243598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* set the watchdog timer units */
244598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timer_units(old);
245485ae77dc7f484563707557ccf8c5d228980619fSven Anders
246598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	close_io_config();
247aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_unlock(&io_lock);
248485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
249485ae77dc7f484563707557ccf8c5d228980619fSven Anders
250485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* shutdown the watchdog */
251485ae77dc7f484563707557ccf8c5d228980619fSven Anders
252485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void wb_smsc_wdt_shutdown(void)
253485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
254aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_lock(&io_lock);
255598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	open_io_config();
256598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	select_io_device(IODEV_NO);
257485ae77dc7f484563707557ccf8c5d228980619fSven Anders
258598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* disable the watchdog */
259598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	gpio_bit13(0x09);
260598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	gpio_bit12(0x09);
261485ae77dc7f484563707557ccf8c5d228980619fSven Anders
262598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* reset watchdog config register */
263485ae77dc7f484563707557ccf8c5d228980619fSven Anders	wdt_timer_conf(0x00);
264485ae77dc7f484563707557ccf8c5d228980619fSven Anders
265598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* reset watchdog control register */
266598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timer_ctrl(0x00);
267485ae77dc7f484563707557ccf8c5d228980619fSven Anders
268598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* disable timeout */
269598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timeout_value(0x00);
270485ae77dc7f484563707557ccf8c5d228980619fSven Anders
271598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	close_io_config();
272aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_unlock(&io_lock);
273485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
274485ae77dc7f484563707557ccf8c5d228980619fSven Anders
275485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* set timeout => enable watchdog */
276485ae77dc7f484563707557ccf8c5d228980619fSven Anders
277485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
278485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
279aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_lock(&io_lock);
280598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	open_io_config();
281598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	select_io_device(IODEV_NO);
282485ae77dc7f484563707557ccf8c5d228980619fSven Anders
283598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* set Power LED to blink, if we enable the timeout */
284598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
285485ae77dc7f484563707557ccf8c5d228980619fSven Anders
286598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* set timeout value */
287598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wdt_timeout_value(new_timeout);
288485ae77dc7f484563707557ccf8c5d228980619fSven Anders
289598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	close_io_config();
290aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_unlock(&io_lock);
291485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
292485ae77dc7f484563707557ccf8c5d228980619fSven Anders
293485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* get timeout */
294485ae77dc7f484563707557ccf8c5d228980619fSven Anders
295485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic unsigned char wb_smsc_wdt_get_timeout(void)
296485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
297598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	unsigned char set_timeout;
298485ae77dc7f484563707557ccf8c5d228980619fSven Anders
299aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_lock(&io_lock);
300598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	open_io_config();
301598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	select_io_device(IODEV_NO);
302598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	set_timeout = read_io_cr(0xF2);
303598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	close_io_config();
304aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_unlock(&io_lock);
305485ae77dc7f484563707557ccf8c5d228980619fSven Anders
306598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	return set_timeout;
307485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
308485ae77dc7f484563707557ccf8c5d228980619fSven Anders
309485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* disable watchdog */
310485ae77dc7f484563707557ccf8c5d228980619fSven Anders
311485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void wb_smsc_wdt_disable(void)
312485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
313598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* set the timeout to 0 to disable the watchdog */
314598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wb_smsc_wdt_set_timeout(0);
315485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
316485ae77dc7f484563707557ccf8c5d228980619fSven Anders
317485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* enable watchdog by setting the current timeout */
318485ae77dc7f484563707557ccf8c5d228980619fSven Anders
319485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void wb_smsc_wdt_enable(void)
320485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
321598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* set the current timeout... */
322598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wb_smsc_wdt_set_timeout(timeout);
323485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
324485ae77dc7f484563707557ccf8c5d228980619fSven Anders
325485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* reset the timer */
326485ae77dc7f484563707557ccf8c5d228980619fSven Anders
327485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void wb_smsc_wdt_reset_timer(void)
328485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
329aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_lock(&io_lock);
330598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	open_io_config();
331598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	select_io_device(IODEV_NO);
332485ae77dc7f484563707557ccf8c5d228980619fSven Anders
333598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* reset the timer */
334485ae77dc7f484563707557ccf8c5d228980619fSven Anders	wdt_timeout_value(timeout);
335485ae77dc7f484563707557ccf8c5d228980619fSven Anders	wdt_timer_conf(0x08);
336485ae77dc7f484563707557ccf8c5d228980619fSven Anders
337598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	close_io_config();
338aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	spin_unlock(&io_lock);
339485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
340485ae77dc7f484563707557ccf8c5d228980619fSven Anders
341485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* return, if the watchdog is enabled (timeout is set...) */
342485ae77dc7f484563707557ccf8c5d228980619fSven Anders
343485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic int wb_smsc_wdt_status(void)
344485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
345485ae77dc7f484563707557ccf8c5d228980619fSven Anders	return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
346485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
347485ae77dc7f484563707557ccf8c5d228980619fSven Anders
348485ae77dc7f484563707557ccf8c5d228980619fSven Anders
349485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- File operations -------------------------------------------*/
350485ae77dc7f484563707557ccf8c5d228980619fSven Anders
351485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* open => enable watchdog and set initial timeout */
352485ae77dc7f484563707557ccf8c5d228980619fSven Anders
353485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic int wb_smsc_wdt_open(struct inode *inode, struct file *file)
354485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
355485ae77dc7f484563707557ccf8c5d228980619fSven Anders	/* /dev/watchdog can only be opened once */
356485ae77dc7f484563707557ccf8c5d228980619fSven Anders
357aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	if (test_and_set_bit(0, &timer_enabled))
358485ae77dc7f484563707557ccf8c5d228980619fSven Anders		return -EBUSY;
359485ae77dc7f484563707557ccf8c5d228980619fSven Anders
360485ae77dc7f484563707557ccf8c5d228980619fSven Anders	if (nowayout)
361485ae77dc7f484563707557ccf8c5d228980619fSven Anders		__module_get(THIS_MODULE);
362485ae77dc7f484563707557ccf8c5d228980619fSven Anders
363485ae77dc7f484563707557ccf8c5d228980619fSven Anders	/* Reload and activate timer */
364485ae77dc7f484563707557ccf8c5d228980619fSven Anders	wb_smsc_wdt_enable();
365485ae77dc7f484563707557ccf8c5d228980619fSven Anders
36627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("Watchdog enabled. Timeout set to %d %s\n",
367598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
368485ae77dc7f484563707557ccf8c5d228980619fSven Anders
369485ae77dc7f484563707557ccf8c5d228980619fSven Anders	return nonseekable_open(inode, file);
370485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
371485ae77dc7f484563707557ccf8c5d228980619fSven Anders
372485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* close => shut off the timer */
373485ae77dc7f484563707557ccf8c5d228980619fSven Anders
374485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic int wb_smsc_wdt_release(struct inode *inode, struct file *file)
375485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
376485ae77dc7f484563707557ccf8c5d228980619fSven Anders	/* Shut off the timer. */
377485ae77dc7f484563707557ccf8c5d228980619fSven Anders
378485ae77dc7f484563707557ccf8c5d228980619fSven Anders	if (expect_close == 42) {
379598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		wb_smsc_wdt_disable();
38027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_info("Watchdog disabled, sleeping again...\n");
381485ae77dc7f484563707557ccf8c5d228980619fSven Anders	} else {
38227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_crit("Unexpected close, not stopping watchdog!\n");
383485ae77dc7f484563707557ccf8c5d228980619fSven Anders		wb_smsc_wdt_reset_timer();
384485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
385485ae77dc7f484563707557ccf8c5d228980619fSven Anders
386aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	clear_bit(0, &timer_enabled);
387485ae77dc7f484563707557ccf8c5d228980619fSven Anders	expect_close = 0;
388485ae77dc7f484563707557ccf8c5d228980619fSven Anders	return 0;
389485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
390485ae77dc7f484563707557ccf8c5d228980619fSven Anders
391485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* write => update the timer to keep the machine alive */
392485ae77dc7f484563707557ccf8c5d228980619fSven Anders
393485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data,
394485ae77dc7f484563707557ccf8c5d228980619fSven Anders				 size_t len, loff_t *ppos)
395485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
396485ae77dc7f484563707557ccf8c5d228980619fSven Anders	/* See if we got the magic character 'V' and reload the timer */
397485ae77dc7f484563707557ccf8c5d228980619fSven Anders	if (len) {
398485ae77dc7f484563707557ccf8c5d228980619fSven Anders		if (!nowayout) {
399485ae77dc7f484563707557ccf8c5d228980619fSven Anders			size_t i;
400485ae77dc7f484563707557ccf8c5d228980619fSven Anders
401485ae77dc7f484563707557ccf8c5d228980619fSven Anders			/* reset expect flag */
402485ae77dc7f484563707557ccf8c5d228980619fSven Anders			expect_close = 0;
403485ae77dc7f484563707557ccf8c5d228980619fSven Anders
404598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox			/* scan to see whether or not we got the
405598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox			   magic character */
406485ae77dc7f484563707557ccf8c5d228980619fSven Anders			for (i = 0; i != len; i++) {
407485ae77dc7f484563707557ccf8c5d228980619fSven Anders				char c;
4087944d3a5a70ee5c1904ed1e8b1d71ff0af2854d9Wim Van Sebroeck				if (get_user(c, data + i))
409485ae77dc7f484563707557ccf8c5d228980619fSven Anders					return -EFAULT;
410485ae77dc7f484563707557ccf8c5d228980619fSven Anders				if (c == 'V')
411485ae77dc7f484563707557ccf8c5d228980619fSven Anders					expect_close = 42;
412485ae77dc7f484563707557ccf8c5d228980619fSven Anders			}
413485ae77dc7f484563707557ccf8c5d228980619fSven Anders		}
414485ae77dc7f484563707557ccf8c5d228980619fSven Anders
415485ae77dc7f484563707557ccf8c5d228980619fSven Anders		/* someone wrote to us, we should reload the timer */
416485ae77dc7f484563707557ccf8c5d228980619fSven Anders		wb_smsc_wdt_reset_timer();
417485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
418485ae77dc7f484563707557ccf8c5d228980619fSven Anders	return len;
419485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
420485ae77dc7f484563707557ccf8c5d228980619fSven Anders
421485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* ioctl => control interface */
422485ae77dc7f484563707557ccf8c5d228980619fSven Anders
423598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic long wb_smsc_wdt_ioctl(struct file *file,
424598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox					unsigned int cmd, unsigned long arg)
425485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
426485ae77dc7f484563707557ccf8c5d228980619fSven Anders	int new_timeout;
427485ae77dc7f484563707557ccf8c5d228980619fSven Anders
428485ae77dc7f484563707557ccf8c5d228980619fSven Anders	union {
429485ae77dc7f484563707557ccf8c5d228980619fSven Anders		struct watchdog_info __user *ident;
430485ae77dc7f484563707557ccf8c5d228980619fSven Anders		int __user *i;
431485ae77dc7f484563707557ccf8c5d228980619fSven Anders	} uarg;
432485ae77dc7f484563707557ccf8c5d228980619fSven Anders
433598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	static const struct watchdog_info ident = {
4345f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck		.options =		WDIOF_KEEPALIVEPING |
435598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox					WDIOF_SETTIMEOUT |
436485ae77dc7f484563707557ccf8c5d228980619fSven Anders					WDIOF_MAGICCLOSE,
437485ae77dc7f484563707557ccf8c5d228980619fSven Anders		.firmware_version =	0,
4385f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck		.identity =		"SMsC 37B787 Watchdog",
439485ae77dc7f484563707557ccf8c5d228980619fSven Anders	};
440485ae77dc7f484563707557ccf8c5d228980619fSven Anders
441485ae77dc7f484563707557ccf8c5d228980619fSven Anders	uarg.i = (int __user *)arg;
442485ae77dc7f484563707557ccf8c5d228980619fSven Anders
443485ae77dc7f484563707557ccf8c5d228980619fSven Anders	switch (cmd) {
444598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	case WDIOC_GETSUPPORT:
445598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		return copy_to_user(uarg.ident, &ident, sizeof(ident))
446598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox								? -EFAULT : 0;
447598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	case WDIOC_GETSTATUS:
448598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		return put_user(wb_smsc_wdt_status(), uarg.i);
449598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	case WDIOC_GETBOOTSTATUS:
450598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		return put_user(0, uarg.i);
4510c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	case WDIOC_SETOPTIONS:
4520c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	{
4530c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		int options, retval = -EINVAL;
4540c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck
4550c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		if (get_user(options, uarg.i))
4560c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			return -EFAULT;
4570c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck
4580c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		if (options & WDIOS_DISABLECARD) {
4590c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			wb_smsc_wdt_disable();
4600c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			retval = 0;
4610c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		}
4620c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		if (options & WDIOS_ENABLECARD) {
4630c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			wb_smsc_wdt_enable();
4640c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			retval = 0;
4650c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		}
4660c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return retval;
4670c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	}
468598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	case WDIOC_KEEPALIVE:
469598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		wb_smsc_wdt_reset_timer();
470598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		return 0;
471598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	case WDIOC_SETTIMEOUT:
472598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		if (get_user(new_timeout, uarg.i))
473598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox			return -EFAULT;
474598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		/* the API states this is given in secs */
475598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		if (unit == UNIT_MINUTE)
476598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox			new_timeout /= 60;
477598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
478598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox			return -EINVAL;
479598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		timeout = new_timeout;
480598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		wb_smsc_wdt_set_timeout(timeout);
481598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		/* fall through and return the new timeout... */
482598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	case WDIOC_GETTIMEOUT:
483598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		new_timeout = timeout;
484598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		if (unit == UNIT_MINUTE)
485143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck			new_timeout *= 60;
486598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		return put_user(new_timeout, uarg.i);
487598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	default:
488598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		return -ENOTTY;
489485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
490485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
491485ae77dc7f484563707557ccf8c5d228980619fSven Anders
492485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- Notifier funtions -----------------------------------------*/
493485ae77dc7f484563707557ccf8c5d228980619fSven Anders
494598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic int wb_smsc_wdt_notify_sys(struct notifier_block *this,
495598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox					unsigned long code, void *unused)
496485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
497598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	if (code == SYS_DOWN || code == SYS_HALT) {
498598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		/* set timeout to 0, to avoid possible race-condition */
499598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		timeout = 0;
500485ae77dc7f484563707557ccf8c5d228980619fSven Anders		wb_smsc_wdt_disable();
501485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
502485ae77dc7f484563707557ccf8c5d228980619fSven Anders	return NOTIFY_DONE;
503485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
504485ae77dc7f484563707557ccf8c5d228980619fSven Anders
505485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- Module's structures ---------------------------------------*/
506485ae77dc7f484563707557ccf8c5d228980619fSven Anders
507598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic const struct file_operations wb_smsc_wdt_fops = {
508598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	.owner	  = THIS_MODULE,
509485ae77dc7f484563707557ccf8c5d228980619fSven Anders	.llseek		= no_llseek,
510485ae77dc7f484563707557ccf8c5d228980619fSven Anders	.write		= wb_smsc_wdt_write,
511598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	.unlocked_ioctl	= wb_smsc_wdt_ioctl,
512485ae77dc7f484563707557ccf8c5d228980619fSven Anders	.open		= wb_smsc_wdt_open,
513aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	.release	= wb_smsc_wdt_release,
514485ae77dc7f484563707557ccf8c5d228980619fSven Anders};
515485ae77dc7f484563707557ccf8c5d228980619fSven Anders
516598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic struct notifier_block wb_smsc_wdt_notifier = {
517aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck	.notifier_call  = wb_smsc_wdt_notify_sys,
518485ae77dc7f484563707557ccf8c5d228980619fSven Anders};
519485ae77dc7f484563707557ccf8c5d228980619fSven Anders
520598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Coxstatic struct miscdevice wb_smsc_wdt_miscdev = {
521485ae77dc7f484563707557ccf8c5d228980619fSven Anders	.minor		= WATCHDOG_MINOR,
522485ae77dc7f484563707557ccf8c5d228980619fSven Anders	.name		= "watchdog",
523485ae77dc7f484563707557ccf8c5d228980619fSven Anders	.fops		= &wb_smsc_wdt_fops,
524485ae77dc7f484563707557ccf8c5d228980619fSven Anders};
525485ae77dc7f484563707557ccf8c5d228980619fSven Anders
526485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* -- Module init functions -------------------------------------*/
527485ae77dc7f484563707557ccf8c5d228980619fSven Anders
528485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* module's "constructor" */
529485ae77dc7f484563707557ccf8c5d228980619fSven Anders
530485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic int __init wb_smsc_wdt_init(void)
531485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
532485ae77dc7f484563707557ccf8c5d228980619fSven Anders	int ret;
533485ae77dc7f484563707557ccf8c5d228980619fSven Anders
53427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("SMsC 37B787 watchdog component driver "
53527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		VERSION " initialising...\n");
536485ae77dc7f484563707557ccf8c5d228980619fSven Anders
537485ae77dc7f484563707557ccf8c5d228980619fSven Anders	if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
53827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register IO port %#x\n", IOPORT);
539485ae77dc7f484563707557ccf8c5d228980619fSven Anders		ret = -EBUSY;
540485ae77dc7f484563707557ccf8c5d228980619fSven Anders		goto out_pnp;
541485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
542485ae77dc7f484563707557ccf8c5d228980619fSven Anders
543598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* set new maximum, if it's too big */
544598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	if (timeout > MAX_TIMEOUT)
545598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		timeout = MAX_TIMEOUT;
546aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck
547598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* init the watchdog timer */
548598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	wb_smsc_wdt_initialize();
549aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeck
550485ae77dc7f484563707557ccf8c5d228980619fSven Anders	ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
551485ae77dc7f484563707557ccf8c5d228980619fSven Anders	if (ret) {
55227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register reboot notifier err = %d\n", ret);
553485ae77dc7f484563707557ccf8c5d228980619fSven Anders		goto out_io;
554485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
555485ae77dc7f484563707557ccf8c5d228980619fSven Anders
556485ae77dc7f484563707557ccf8c5d228980619fSven Anders	ret = misc_register(&wb_smsc_wdt_miscdev);
557485ae77dc7f484563707557ccf8c5d228980619fSven Anders	if (ret) {
55827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register miscdev on minor %d\n",
55927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		       WATCHDOG_MINOR);
560485ae77dc7f484563707557ccf8c5d228980619fSven Anders		goto out_rbt;
561485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
562485ae77dc7f484563707557ccf8c5d228980619fSven Anders
563598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	/* output info */
56427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("Timeout set to %d %s\n",
565598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
56627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("Watchdog initialized and sleeping (nowayout=%d)...\n",
56727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		nowayout);
568485ae77dc7f484563707557ccf8c5d228980619fSven Andersout_clean:
569485ae77dc7f484563707557ccf8c5d228980619fSven Anders	return ret;
570485ae77dc7f484563707557ccf8c5d228980619fSven Anders
571485ae77dc7f484563707557ccf8c5d228980619fSven Andersout_rbt:
572485ae77dc7f484563707557ccf8c5d228980619fSven Anders	unregister_reboot_notifier(&wb_smsc_wdt_notifier);
573485ae77dc7f484563707557ccf8c5d228980619fSven Anders
574485ae77dc7f484563707557ccf8c5d228980619fSven Andersout_io:
575485ae77dc7f484563707557ccf8c5d228980619fSven Anders	release_region(IOPORT, IOPORT_SIZE);
576485ae77dc7f484563707557ccf8c5d228980619fSven Anders
577485ae77dc7f484563707557ccf8c5d228980619fSven Andersout_pnp:
578485ae77dc7f484563707557ccf8c5d228980619fSven Anders	goto out_clean;
5798386c8cfb2131b2a9caae3db6bf94292bbbe1cafWim Van Sebroeck}
580485ae77dc7f484563707557ccf8c5d228980619fSven Anders
581485ae77dc7f484563707557ccf8c5d228980619fSven Anders/* module's "destructor" */
582485ae77dc7f484563707557ccf8c5d228980619fSven Anders
583485ae77dc7f484563707557ccf8c5d228980619fSven Andersstatic void __exit wb_smsc_wdt_exit(void)
584485ae77dc7f484563707557ccf8c5d228980619fSven Anders{
585485ae77dc7f484563707557ccf8c5d228980619fSven Anders	/* Stop the timer before we leave */
586598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox	if (!nowayout) {
587485ae77dc7f484563707557ccf8c5d228980619fSven Anders		wb_smsc_wdt_shutdown();
58827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_info("Watchdog disabled\n");
589485ae77dc7f484563707557ccf8c5d228980619fSven Anders	}
590485ae77dc7f484563707557ccf8c5d228980619fSven Anders
591485ae77dc7f484563707557ccf8c5d228980619fSven Anders	misc_deregister(&wb_smsc_wdt_miscdev);
592485ae77dc7f484563707557ccf8c5d228980619fSven Anders	unregister_reboot_notifier(&wb_smsc_wdt_notifier);
593485ae77dc7f484563707557ccf8c5d228980619fSven Anders	release_region(IOPORT, IOPORT_SIZE);
594485ae77dc7f484563707557ccf8c5d228980619fSven Anders
59527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("SMsC 37B787 watchdog component driver removed\n");
596485ae77dc7f484563707557ccf8c5d228980619fSven Anders}
597485ae77dc7f484563707557ccf8c5d228980619fSven Anders
598485ae77dc7f484563707557ccf8c5d228980619fSven Andersmodule_init(wb_smsc_wdt_init);
599485ae77dc7f484563707557ccf8c5d228980619fSven Andersmodule_exit(wb_smsc_wdt_exit);
600485ae77dc7f484563707557ccf8c5d228980619fSven Anders
601485ae77dc7f484563707557ccf8c5d228980619fSven AndersMODULE_AUTHOR("Sven Anders <anders@anduras.de>");
602598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan CoxMODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "
603598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox								VERSION ")");
604485ae77dc7f484563707557ccf8c5d228980619fSven AndersMODULE_LICENSE("GPL");
605485ae77dc7f484563707557ccf8c5d228980619fSven Anders
606485ae77dc7f484563707557ccf8c5d228980619fSven AndersMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
607485ae77dc7f484563707557ccf8c5d228980619fSven Anders
608485ae77dc7f484563707557ccf8c5d228980619fSven Anders#ifdef SMSC_SUPPORT_MINUTES
609485ae77dc7f484563707557ccf8c5d228980619fSven Andersmodule_param(unit, int, 0);
610598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan CoxMODULE_PARM_DESC(unit,
611598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		"set unit to use, 0=seconds or 1=minutes, default is 0");
612485ae77dc7f484563707557ccf8c5d228980619fSven Anders#endif
613485ae77dc7f484563707557ccf8c5d228980619fSven Anders
614aa1fd4d7c3b131026bf156da40fdf94bcbd705aaWim Van Sebroeckmodule_param(timeout, int, 0);
615485ae77dc7f484563707557ccf8c5d228980619fSven AndersMODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
616485ae77dc7f484563707557ccf8c5d228980619fSven Anders
61786a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
618598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan CoxMODULE_PARM_DESC(nowayout,
619598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox		"Watchdog cannot be stopped once started (default="
620598467938dd8bcdcd4d88e9102c609f4caa9d9efAlan Cox				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
621