Blame view

drivers/watchdog/pcwd.c 26.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   * PC Watchdog Driver
   * by Ken Hollis (khollis@bitgate.com)
   *
f9146f26d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
5
   * Permission granted from Simon Machell (smachell@berkprod.com)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   * Written for the Linux Kernel, and GPLed by Ken Hollis
   *
   * 960107	Added request_region routines, modulized the whole thing.
   * 960108	Fixed end-of-file pointer (Thanks to Dan Hollis), added
   *		WD_TIMEOUT define.
   * 960216	Added eof marker on the file, and changed verbose messages.
   * 960716	Made functional and cosmetic changes to the source for
   *		inclusion in Linux 2.0.x kernels, thanks to Alan Cox.
   * 960717	Removed read/seek routines, replaced with ioctl.  Also, added
   *		check_region command due to Alan's suggestion.
   * 960821	Made changes to compile in newer 2.0.x kernels.  Added
   *		"cold reboot sense" entry.
   * 960825	Made a few changes to code, deleted some defines and made
   *		typedefs to replace them.  Made heartbeat reset only available
   *		via ioctl, and removed the write routine.
   * 960828	Added new items for PC Watchdog Rev.C card.
   * 960829	Changed around all of the IOCTLs, added new features,
   *		added watchdog disable/re-enable routines.  Added firmware
   *		version reporting.  Added read routine for temperature.
   *		Removed some extra defines, added an autodetect Revision
   *		routine.
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
27
28
   * 961006	Revised some documentation, fixed some cosmetic bugs.  Made
   *		drivers to panic the system if it's overheating at bootup.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
   * 961118	Changed some verbiage on some of the output, tidied up
   *		code bits, and added compatibility to 2.1.x.
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
31
   * 970912	Enabled board on open and disable on close.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
   * 971107	Took account of recent VFS changes (broke read).
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
33
34
35
36
37
38
   * 971210	Disable board on initialisation in case board already ticking.
   * 971222	Changed open/close for temperature handling
   *		Michael Meskes <meskes@debian.org>.
   * 980112	Used minor numbers from include/linux/miscdevice.h
   * 990403	Clear reset status after reading control status register in
   *		pcwd_showprevstate(). [Marc Boucher <marc@mbsi.ca>]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
   * 990605	Made changes to code to support Firmware 1.22a, added
   *		fairly useless proc entry.
   * 990610	removed said useless proc code for the merge <alan>
   * 000403	Removed last traces of proc code. <davej>
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
43
44
   * 011214	Added nowayout module option to override
   *		CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
45
   *		Added timeout module option to override default
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
   */
  
  /*
   *	A bells and whistles driver is available from http://www.pcwd.de/
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
50
51
   *	More info available at http://www.berkprod.com/ or
   *	http://www.pcwatchdog.com/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
   */
27c766aaa   Joe Perches   watchdog: Use pr_...
53
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
54
55
56
57
58
59
60
61
  #include <linux/module.h>	/* For module specific items */
  #include <linux/moduleparam.h>	/* For new moduleparam's */
  #include <linux/types.h>	/* For standard types (like size_t) */
  #include <linux/errno.h>	/* For the -ENODEV/... values */
  #include <linux/kernel.h>	/* For printk/panic/... */
  #include <linux/delay.h>	/* For mdelay function */
  #include <linux/timer.h>	/* For timer related operations */
  #include <linux/jiffies.h>	/* For jiffies stuff */
487722cf2   Jean Delvare   watchdog: Get rid...
62
  #include <linux/miscdevice.h>	/* For struct miscdevice */
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
63
  #include <linux/watchdog.h>	/* For the watchdog specific items */
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
64
  #include <linux/reboot.h>	/* For kernel_power_off() */
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
65
66
  #include <linux/init.h>		/* For __init/__exit/... */
  #include <linux/fs.h>		/* For file operations */
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
67
  #include <linux/isa.h>		/* For isa devices */
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
68
69
  #include <linux/ioport.h>	/* For io-port access */
  #include <linux/spinlock.h>	/* For spin_lock/spin_unlock/... */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
70
71
  #include <linux/uaccess.h>	/* For copy_to_user/put_user/... */
  #include <linux/io.h>		/* For inb/outb/... */
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
72
73
  
  /* Module and version information */
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
74
75
  #define WATCHDOG_VERSION "1.20"
  #define WATCHDOG_DATE "18 Feb 2007"
a7122f916   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
76
77
  #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
  #define WATCHDOG_NAME "pcwd"
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
78
79
  #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "
  "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
87
88
89
90
91
  
  /*
   * It should be noted that PCWD_REVISION_B was removed because A and B
   * are essentially the same types of card, with the exception that B
   * has temperature reporting.  Since I didn't receive a Rev.B card,
   * the Rev.B card is not supported.  (It's a good thing too, as they
   * are no longer in production.)
   */
  #define	PCWD_REVISION_A		1
  #define	PCWD_REVISION_C		2
  
  /*
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
92
93
94
95
96
97
98
99
100
   * These are the auto-probe addresses available.
   *
   * Revision A only uses ports 0x270 and 0x370.  Revision C introduced 0x350.
   * Revision A has an address range of 2 addresses, while Revision C has 4.
   */
  #define PCWD_ISA_NR_CARDS	3
  static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
  
  /*
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
101
102
103
104
   * These are the defines that describe the control status bits for the
   * PCI-PC Watchdog card.
  */
  /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
105
106
107
108
109
  #define WD_WDRST		0x01	/* Previously reset state */
  #define WD_T110			0x02	/* Temperature overheat sense */
  #define WD_HRTBT		0x04	/* Heartbeat sense */
  #define WD_RLY2			0x08	/* External relay triggered */
  #define WD_SRLY2		0x80	/* Software external relay triggered */
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
110
  /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
111
112
113
  #define WD_REVC_WTRP		0x01	/* Watchdog Trip status */
  #define WD_REVC_HRBT		0x02	/* Watchdog Heartbeat */
  #define WD_REVC_TTRP		0x04	/* Temperature Trip status */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
114
115
  #define WD_REVC_RL2A		0x08	/* Relay 2 activated by
  							on-board processor */
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
116
117
118
  #define WD_REVC_RL1A		0x10	/* Relay 1 active */
  #define WD_REVC_R2DS		0x40	/* Relay 2 disable */
  #define WD_REVC_RLY2		0x80	/* Relay 2 activated? */
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
119
120
121
  /* Port 2 : Control Status #2 */
  #define WD_WDIS			0x10	/* Watchdog Disabled */
  #define WD_ENTP			0x20	/* Watchdog Enable Temperature Trip */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
122
123
  #define WD_SSEL			0x40	/* Watchdog Switch Select
  							(1:SW1 <-> 0:SW2) */
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
124
  #define WD_WCMD			0x80	/* Watchdog Command Mode */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
130
  
  /* max. time we give an ISA watchdog card to process a command */
  /* 500ms for each 4 bit response (according to spec.) */
  #define ISA_COMMAND_TIMEOUT     1000
  
  /* Watchdog's internal commands */
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
131
132
133
134
135
136
  #define CMD_ISA_IDLE			0x00
  #define CMD_ISA_VERSION_INTEGER		0x01
  #define CMD_ISA_VERSION_TENTH		0x02
  #define CMD_ISA_VERSION_HUNDRETH	0x03
  #define CMD_ISA_VERSION_MINOR		0x04
  #define CMD_ISA_SWITCH_SETTINGS		0x05
369fa2529   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
137
138
139
140
  #define CMD_ISA_RESET_PC		0x06
  #define CMD_ISA_ARM_0			0x07
  #define CMD_ISA_ARM_30			0x08
  #define CMD_ISA_ARM_60			0x09
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
141
142
143
  #define CMD_ISA_DELAY_TIME_2SECS	0x0A
  #define CMD_ISA_DELAY_TIME_4SECS	0x0B
  #define CMD_ISA_DELAY_TIME_8SECS	0x0C
369fa2529   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
144
  #define CMD_ISA_RESET_RELAYS		0x0D
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145

f3dc07330   Wim Van Sebroeck   [WATCHDOG] pcwd_u...
146
  /* Watchdog's Dip Switch heartbeat values */
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
147
  static const int heartbeat_tbl[] = {
f3dc07330   Wim Van Sebroeck   [WATCHDOG] pcwd_u...
148
149
150
151
152
153
154
155
156
  	20,	/* OFF-OFF-OFF	= 20 Sec  */
  	40,	/* OFF-OFF-ON	= 40 Sec  */
  	60,	/* OFF-ON-OFF	=  1 Min  */
  	300,	/* OFF-ON-ON	=  5 Min  */
  	600,	/* ON-OFF-OFF	= 10 Min  */
  	1800,	/* ON-OFF-ON	= 30 Min  */
  	3600,	/* ON-ON-OFF	=  1 Hour */
  	7200,	/* ON-ON-ON	=  2 hour */
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
164
165
166
167
168
  /*
   * We are using an kernel timer to do the pinging of the watchdog
   * every ~500ms. We try to set the internal heartbeat of the
   * watchdog to 2 ms.
   */
  
  #define WDT_INTERVAL (HZ/2+1)
  
  /* We can only use 1 card due to the /dev/watchdog restriction */
  static int cards_found;
  
  /* internal variables */
36cbaa877   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
169
  static unsigned long open_allowed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
  static char expect_close;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  static int temp_panic;
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
172
173
174
  
  /* this is private data for each ISA-PC watchdog card */
  static struct {
2891b6ad1   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
175
  	char fw_ver_str[6];		/* The cards firmware version */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
176
  	int revision;			/* The card's revision */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
177
178
179
180
  	int supports_temp;		/* Whether or not the card has
  						a temperature device */
  	int command_mode;		/* Whether or not the card is in
  						command mode */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
181
182
183
184
185
186
  	int boot_status;		/* The card's boot status */
  	int io_addr;			/* The cards I/O address */
  	spinlock_t io_lock;		/* the lock for io operations */
  	struct timer_list timer;	/* The timer that pings the watchdog */
  	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
  } pcwd_private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
  
  /* module parameters */
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
189
190
191
192
193
  #define QUIET	0	/* Default */
  #define VERBOSE	1	/* Verbose */
  #define DEBUG	2	/* print fancy stuff too */
  static int debug = QUIET;
  module_param(debug, int, 0);
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
194
195
  MODULE_PARM_DESC(debug,
  		"Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
196

261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
197
198
  /* default heartbeat = delay-time from dip-switches */
  #define WATCHDOG_HEARTBEAT 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
  static int heartbeat = WATCHDOG_HEARTBEAT;
  module_param(heartbeat, int, 0);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
201
202
203
  MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
  	"(2 <= heartbeat <= 7200 or 0=delay-time from dip-switches, default="
  				__MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204

86a1e1896   Wim Van Sebroeck   watchdog: nowayou...
205
206
  static bool nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, bool, 0);
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
207
208
209
  MODULE_PARM_DESC(nowayout,
  		"Watchdog cannot be stopped once started (default="
  				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
211
212
213
214
215
216
217
218
219
  
  /*
   *	Internal functions
   */
  
  static int send_isa_command(int cmd)
  {
  	int i;
  	int control_status;
  	int port0, last_port0;	/* Double read for stabilising */
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
220
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
221
222
  		pr_debug("sending following data cmd=0x%02x
  ", cmd);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
223

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  	/* The WCMD bit must be 1 and the command is only 4 bits in size */
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
225
  	control_status = (cmd & 0x0F) | WD_WCMD;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
226
  	outb_p(control_status, pcwd_private.io_addr + 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
  	udelay(ISA_COMMAND_TIMEOUT);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
228
  	port0 = inb_p(pcwd_private.io_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
  	for (i = 0; i < 25; ++i) {
  		last_port0 = port0;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
231
  		port0 = inb_p(pcwd_private.io_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
  
  		if (port0 == last_port0)
  			break;	/* Data is stable */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
235
  		udelay(250);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  	}
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
237
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
238
239
240
  		pr_debug("received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x
  ",
  			 cmd, port0, last_port0);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
241

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
  	return port0;
  }
  
  static int set_command_mode(void)
  {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
247
  	int i, found = 0, count = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
  
  	/* Set the card into command mode */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
250
  	spin_lock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
257
  	while ((!found) && (count < 3)) {
  		i = send_isa_command(CMD_ISA_IDLE);
  
  		if (i == 0x00)
  			found = 1;
  		else if (i == 0xF3) {
  			/* Card does not like what we've done to it */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
258
  			outb_p(0x00, pcwd_private.io_addr + 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  			udelay(1200);	/* Spec says wait 1ms */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
260
  			outb_p(0x00, pcwd_private.io_addr + 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
  			udelay(ISA_COMMAND_TIMEOUT);
  		}
  		count++;
  	}
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
265
266
  	spin_unlock(&pcwd_private.io_lock);
  	pcwd_private.command_mode = found;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267

c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
268
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
269
270
  		pr_debug("command_mode=%d
  ", pcwd_private.command_mode);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
271

7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
272
  	return found;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
  }
  
  static void unset_command_mode(void)
  {
  	/* Set the card into normal mode */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
278
279
  	spin_lock(&pcwd_private.io_lock);
  	outb_p(0x00, pcwd_private.io_addr + 2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	udelay(ISA_COMMAND_TIMEOUT);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
281
  	spin_unlock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282

a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
283
  	pcwd_private.command_mode = 0;
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
284
285
  
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
286
287
  		pr_debug("command_mode=%d
  ", pcwd_private.command_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  }
85875211a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
289
290
291
292
293
  static inline void pcwd_check_temperature_support(void)
  {
  	if (inb(pcwd_private.io_addr) != 0xF0)
  		pcwd_private.supports_temp = 1;
  }
2891b6ad1   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
294
  static inline void pcwd_get_firmware(void)
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
295
296
  {
  	int one, ten, hund, minor;
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
297

6bbc20bc0   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
298
  	strcpy(pcwd_private.fw_ver_str, "ERROR");
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
299
300
301
302
303
304
  
  	if (set_command_mode()) {
  		one = send_isa_command(CMD_ISA_VERSION_INTEGER);
  		ten = send_isa_command(CMD_ISA_VERSION_TENTH);
  		hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
  		minor = send_isa_command(CMD_ISA_VERSION_MINOR);
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
305
306
  		sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c",
  					one, ten, hund, minor);
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
307
  	}
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
308
  	unset_command_mode();
2891b6ad1   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
309
310
  
  	return;
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
311
312
313
314
  }
  
  static inline int pcwd_get_option_switches(void)
  {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
315
  	int option_switches = 0;
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
316
317
318
319
320
321
322
  
  	if (set_command_mode()) {
  		/* Get switch settings */
  		option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS);
  	}
  
  	unset_command_mode();
7944d3a5a   Wim Van Sebroeck   [WATCHDOG] more c...
323
  	return option_switches;
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
324
325
326
327
  }
  
  static void pcwd_show_card_info(void)
  {
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
328
329
330
331
  	int option_switches;
  
  	/* Get some extra info from the hardware (in command/debug/diag mode) */
  	if (pcwd_private.revision == PCWD_REVISION_A)
27c766aaa   Joe Perches   watchdog: Use pr_...
332
333
334
  		pr_info("ISA-PC Watchdog (REV.A) detected at port 0x%04x
  ",
  			pcwd_private.io_addr);
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
335
  	else if (pcwd_private.revision == PCWD_REVISION_C) {
2891b6ad1   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
336
  		pcwd_get_firmware();
27c766aaa   Joe Perches   watchdog: Use pr_...
337
338
  		pr_info("ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)
  ",
2891b6ad1   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
339
  			pcwd_private.io_addr, pcwd_private.fw_ver_str);
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
340
  		option_switches = pcwd_get_option_switches();
27c766aaa   Joe Perches   watchdog: Use pr_...
341
342
  		pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s
  ",
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
343
344
345
346
347
348
349
350
351
352
353
354
  			option_switches,
  			((option_switches & 0x10) ? "ON" : "OFF"),
  			((option_switches & 0x08) ? "ON" : "OFF"));
  
  		/* Reprogram internal heartbeat to 2 seconds */
  		if (set_command_mode()) {
  			send_isa_command(CMD_ISA_DELAY_TIME_2SECS);
  			unset_command_mode();
  		}
  	}
  
  	if (pcwd_private.supports_temp)
27c766aaa   Joe Perches   watchdog: Use pr_...
355
356
  		pr_info("Temperature Option Detected
  ");
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
357
358
  
  	if (pcwd_private.boot_status & WDIOF_CARDRESET)
27c766aaa   Joe Perches   watchdog: Use pr_...
359
360
  		pr_info("Previous reboot was caused by the card
  ");
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
361
362
  
  	if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
27c766aaa   Joe Perches   watchdog: Use pr_...
363
364
365
366
  		pr_emerg("Card senses a CPU Overheat. Panicking!
  ");
  		pr_emerg("CPU Overheat
  ");
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
367
368
369
  	}
  
  	if (pcwd_private.boot_status == 0)
27c766aaa   Joe Perches   watchdog: Use pr_...
370
371
  		pr_info("No previous trip detected - Cold boot or reset
  ");
af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
372
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
377
378
  static void pcwd_timer_ping(unsigned long data)
  {
  	int wdrst_stat;
  
  	/* If we got a heartbeat pulse within the WDT_INTERVAL
  	 * we agree to ping the WDT */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
379
  	if (time_before(jiffies, pcwd_private.next_heartbeat)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  		/* Ping the watchdog */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
381
382
  		spin_lock(&pcwd_private.io_lock);
  		if (pcwd_private.revision == PCWD_REVISION_A) {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
383
384
  			/*  Rev A cards are reset by setting the
  			    WD_WDRST bit in register 1 */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
385
  			wdrst_stat = inb_p(pcwd_private.io_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  			wdrst_stat &= 0x0F;
  			wdrst_stat |= WD_WDRST;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
388
  			outb_p(wdrst_stat, pcwd_private.io_addr + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
  		} else {
  			/* Re-trigger watchdog by writing to port 0 */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
391
  			outb_p(0x00, pcwd_private.io_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
  		}
  
  		/* Re-set the timer interval */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
395
  		mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396

a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
397
  		spin_unlock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
  	} else {
27c766aaa   Joe Perches   watchdog: Use pr_...
399
400
  		pr_warn("Heartbeat lost! Will not ping the watchdog
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
405
406
  	}
  }
  
  static int pcwd_start(void)
  {
  	int stat_reg;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
407
  	pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
  
  	/* Start the timer */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
410
  	mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
  
  	/* Enable the port */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
413
414
415
  	if (pcwd_private.revision == PCWD_REVISION_C) {
  		spin_lock(&pcwd_private.io_lock);
  		outb_p(0x00, pcwd_private.io_addr + 3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  		udelay(ISA_COMMAND_TIMEOUT);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
417
418
  		stat_reg = inb_p(pcwd_private.io_addr + 2);
  		spin_unlock(&pcwd_private.io_lock);
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
419
  		if (stat_reg & WD_WDIS) {
27c766aaa   Joe Perches   watchdog: Use pr_...
420
421
  			pr_info("Could not start watchdog
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
  			return -EIO;
  		}
  	}
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
425
426
  
  	if (debug >= VERBOSE)
27c766aaa   Joe Perches   watchdog: Use pr_...
427
428
  		pr_debug("Watchdog started
  ");
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
429

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
432
433
434
435
436
437
  	return 0;
  }
  
  static int pcwd_stop(void)
  {
  	int stat_reg;
  
  	/* Stop the timer */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
438
  	del_timer(&pcwd_private.timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
  
  	/*  Disable the board  */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
441
442
443
  	if (pcwd_private.revision == PCWD_REVISION_C) {
  		spin_lock(&pcwd_private.io_lock);
  		outb_p(0xA5, pcwd_private.io_addr + 3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
  		udelay(ISA_COMMAND_TIMEOUT);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
445
  		outb_p(0xA5, pcwd_private.io_addr + 3);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  		udelay(ISA_COMMAND_TIMEOUT);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
447
448
  		stat_reg = inb_p(pcwd_private.io_addr + 2);
  		spin_unlock(&pcwd_private.io_lock);
8f0235dcc   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
449
  		if ((stat_reg & WD_WDIS) == 0) {
27c766aaa   Joe Perches   watchdog: Use pr_...
450
451
  			pr_info("Could not stop watchdog
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
454
  			return -EIO;
  		}
  	}
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
455
456
  
  	if (debug >= VERBOSE)
27c766aaa   Joe Perches   watchdog: Use pr_...
457
458
  		pr_debug("Watchdog stopped
  ");
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
459

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
  	return 0;
  }
  
  static int pcwd_keepalive(void)
  {
  	/* user land ping */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
466
  	pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
467
468
  
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
469
470
  		pr_debug("Watchdog keepalive signal send
  ");
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
471

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
  	return 0;
  }
  
  static int pcwd_set_heartbeat(int t)
  {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
477
  	if (t < 2 || t > 7200) /* arbitrary upper limit */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
  		return -EINVAL;
  
  	heartbeat = t;
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
481
482
  
  	if (debug >= VERBOSE)
27c766aaa   Joe Perches   watchdog: Use pr_...
483
484
  		pr_debug("New heartbeat: %d
  ", heartbeat);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
485

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
488
489
490
  	return 0;
  }
  
  static int pcwd_get_status(int *status)
  {
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
491
  	int control_status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492

261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
493
  	*status = 0;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
494
495
  	spin_lock(&pcwd_private.io_lock);
  	if (pcwd_private.revision == PCWD_REVISION_A)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
  		/* Rev A cards return status information from
  		 * the base register, which is used for the
  		 * temperature in other cards. */
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
499
  		control_status = inb(pcwd_private.io_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
504
505
  	else {
  		/* Rev C cards return card status in the base
  		 * address + 1 register. And use different bits
  		 * to indicate a card initiated reset, and an
  		 * over-temperature condition. And the reboot
  		 * status can be reset. */
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
506
  		control_status = inb(pcwd_private.io_addr + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  	}
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
508
  	spin_unlock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509

a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
510
  	if (pcwd_private.revision == PCWD_REVISION_A) {
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
511
  		if (control_status & WD_WDRST)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
  			*status |= WDIOF_CARDRESET;
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
513
  		if (control_status & WD_T110) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
  			*status |= WDIOF_OVERHEAT;
  			if (temp_panic) {
27c766aaa   Joe Perches   watchdog: Use pr_...
516
517
  				pr_info("Temperature overheat trip!
  ");
68acc05d0   Eric W. Biederman   [PATCH] pcwd.c: C...
518
  				kernel_power_off();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
  			}
  		}
  	} else {
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
522
  		if (control_status & WD_REVC_WTRP)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
  			*status |= WDIOF_CARDRESET;
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
524
  		if (control_status & WD_REVC_TTRP) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
  			*status |= WDIOF_OVERHEAT;
  			if (temp_panic) {
27c766aaa   Joe Perches   watchdog: Use pr_...
527
528
  				pr_info("Temperature overheat trip!
  ");
68acc05d0   Eric W. Biederman   [PATCH] pcwd.c: C...
529
  				kernel_power_off();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
533
534
535
536
537
538
  			}
  		}
  	}
  
  	return 0;
  }
  
  static int pcwd_clear_status(void)
  {
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
539
  	int control_status;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
540
541
  	if (pcwd_private.revision == PCWD_REVISION_C) {
  		spin_lock(&pcwd_private.io_lock);
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
542

c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
543
  		if (debug >= VERBOSE)
27c766aaa   Joe Perches   watchdog: Use pr_...
544
545
  			pr_info("clearing watchdog trip status
  ");
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
546

4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
547
  		control_status = inb_p(pcwd_private.io_addr + 1);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
548
  		if (debug >= DEBUG) {
27c766aaa   Joe Perches   watchdog: Use pr_...
549
550
551
552
553
  			pr_debug("status was: 0x%02x
  ", control_status);
  			pr_debug("sending: 0x%02x
  ",
  				 (control_status & WD_REVC_R2DS));
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
554
  		}
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
555
  		/* clear reset status & Keep Relay 2 disable state as it is */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
556
557
  		outb_p((control_status & WD_REVC_R2DS),
  						pcwd_private.io_addr + 1);
4206f0c4d   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
558

a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
559
  		spin_unlock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
561
562
563
564
565
566
  	}
  	return 0;
  }
  
  static int pcwd_get_temperature(int *temperature)
  {
  	/* check that port 0 gives temperature info and no command results */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
567
  	if (pcwd_private.command_mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
  		return -1;
  
  	*temperature = 0;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
571
  	if (!pcwd_private.supports_temp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
573
574
575
576
577
  		return -ENODEV;
  
  	/*
  	 * Convert celsius to fahrenheit, since this was
  	 * the decided 'standard' for this return value.
  	 */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
578
579
580
  	spin_lock(&pcwd_private.io_lock);
  	*temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
  	spin_unlock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581

c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
582
  	if (debug >= DEBUG) {
27c766aaa   Joe Perches   watchdog: Use pr_...
583
584
  		pr_debug("temperature is: %d F
  ", *temperature);
c324ab428   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
585
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
589
590
591
  	return 0;
  }
  
  /*
   *	/dev/watchdog handling
   */
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
592
  static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
597
598
  {
  	int rv;
  	int status;
  	int temperature;
  	int new_heartbeat;
  	int __user *argp = (int __user *)arg;
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
599
  	static const struct watchdog_info ident = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
602
603
604
605
606
607
  		.options =		WDIOF_OVERHEAT |
  					WDIOF_CARDRESET |
  					WDIOF_KEEPALIVEPING |
  					WDIOF_SETTIMEOUT |
  					WDIOF_MAGICCLOSE,
  		.firmware_version =	1,
  		.identity =		"PCWD",
  	};
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
608
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
  	case WDIOC_GETSUPPORT:
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
610
  		if (copy_to_user(argp, &ident, sizeof(ident)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
616
617
618
  			return -EFAULT;
  		return 0;
  
  	case WDIOC_GETSTATUS:
  		pcwd_get_status(&status);
  		return put_user(status, argp);
  
  	case WDIOC_GETBOOTSTATUS:
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
619
  		return put_user(pcwd_private.boot_status, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
622
623
624
625
626
627
  
  	case WDIOC_GETTEMP:
  		if (pcwd_get_temperature(&temperature))
  			return -EFAULT;
  
  		return put_user(temperature, argp);
  
  	case WDIOC_SETOPTIONS:
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
628
629
  		if (pcwd_private.revision == PCWD_REVISION_C) {
  			if (get_user(rv, argp))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
  				return -EFAULT;
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
631
632
633
634
  			if (rv & WDIOS_DISABLECARD) {
  				status = pcwd_stop();
  				if (status < 0)
  					return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  			}
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
636
637
638
639
  			if (rv & WDIOS_ENABLECARD) {
  				status = pcwd_start();
  				if (status < 0)
  					return status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
  			if (rv & WDIOS_TEMPPANIC)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
  				temp_panic = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
  		}
  		return -EINVAL;
  
  	case WDIOC_KEEPALIVE:
  		pcwd_keepalive();
  		return 0;
  
  	case WDIOC_SETTIMEOUT:
  		if (get_user(new_heartbeat, argp))
  			return -EFAULT;
  
  		if (pcwd_set_heartbeat(new_heartbeat))
  			return -EINVAL;
  
  		pcwd_keepalive();
  		/* Fall */
  
  	case WDIOC_GETTIMEOUT:
  		return put_user(heartbeat, argp);
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
662
663
664
  
  	default:
  		return -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
  	}
  
  	return 0;
  }
  
  static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len,
  			  loff_t *ppos)
  {
  	if (len) {
  		if (!nowayout) {
  			size_t i;
  
  			/* In case it was set long ago */
  			expect_close = 0;
  
  			for (i = 0; i != len; i++) {
  				char c;
  
  				if (get_user(c, buf + i))
  					return -EFAULT;
  				if (c == 'V')
  					expect_close = 42;
  			}
  		}
  		pcwd_keepalive();
  	}
  	return len;
  }
  
  static int pcwd_open(struct inode *inode, struct file *file)
  {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
696
  	if (test_and_set_bit(0, &open_allowed))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
  		return -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
  	if (nowayout)
  		__module_get(THIS_MODULE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
701
702
703
704
705
706
707
  	/* Activate */
  	pcwd_start();
  	pcwd_keepalive();
  	return nonseekable_open(inode, file);
  }
  
  static int pcwd_close(struct inode *inode, struct file *file)
  {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
708
  	if (expect_close == 42)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
  		pcwd_stop();
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
710
  	else {
27c766aaa   Joe Perches   watchdog: Use pr_...
711
712
  		pr_crit("Unexpected close, not stopping watchdog!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
714
715
  		pcwd_keepalive();
  	}
  	expect_close = 0;
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
716
  	clear_bit(0, &open_allowed);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
  	return 0;
  }
  
  /*
   *	/dev/temperature handling
   */
  
  static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count,
  			 loff_t *ppos)
  {
  	int temperature;
  
  	if (pcwd_get_temperature(&temperature))
  		return -EFAULT;
  
  	if (copy_to_user(buf, &temperature, 1))
  		return -EFAULT;
  
  	return 1;
  }
  
  static int pcwd_temp_open(struct inode *inode, struct file *file)
  {
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
740
  	if (!pcwd_private.supports_temp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
743
744
745
746
747
748
749
750
751
  		return -ENODEV;
  
  	return nonseekable_open(inode, file);
  }
  
  static int pcwd_temp_close(struct inode *inode, struct file *file)
  {
  	return 0;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
   *	Kernel Interfaces
   */
62322d255   Arjan van de Ven   [PATCH] make more...
754
  static const struct file_operations pcwd_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
  	.write		= pcwd_write,
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
758
  	.unlocked_ioctl	= pcwd_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
761
762
763
764
765
766
767
  	.open		= pcwd_open,
  	.release	= pcwd_close,
  };
  
  static struct miscdevice pcwd_miscdev = {
  	.minor =	WATCHDOG_MINOR,
  	.name =		"watchdog",
  	.fops =		&pcwd_fops,
  };
62322d255   Arjan van de Ven   [PATCH] make more...
768
  static const struct file_operations pcwd_temp_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
775
776
777
778
779
780
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
  	.read		= pcwd_temp_read,
  	.open		= pcwd_temp_open,
  	.release	= pcwd_temp_close,
  };
  
  static struct miscdevice temp_miscdev = {
  	.minor =	TEMP_MINOR,
  	.name =		"temperature",
  	.fops =		&pcwd_temp_fops,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
783
  /*
   *	Init & exit routines
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
  static inline int get_revision(void)
  {
  	int r = PCWD_REVISION_C;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
787
  	spin_lock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  	/* REV A cards use only 2 io ports; test
  	 * presumes a floating bus reads as 0xff. */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
790
791
  	if ((inb(pcwd_private.io_addr + 2) == 0xFF) ||
  	    (inb(pcwd_private.io_addr + 3) == 0xFF))
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
792
  		r = PCWD_REVISION_A;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
793
  	spin_unlock(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
796
  
  	return r;
  }
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
797
798
799
800
801
802
803
  /*
   *  The ISA cards have a heartbeat bit in one of the registers, which
   *  register is card dependent.  The heartbeat bit is monitored, and if
   *  found, is considered proof that a Berkshire card has been found.
   *  The initial rate is once per second at board start up, then twice
   *  per second for normal operation.
   */
2d991a164   Bill Pemberton   watchdog: remove ...
804
  static int pcwd_isa_match(struct device *dev, unsigned int id)
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
805
  {
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
806
  	int base_addr = pcwd_ioports[id];
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
807
808
809
810
811
812
  	int port0, last_port0;	/* Reg 0, in case it's REV A */
  	int port1, last_port1;	/* Register 1 for REV C cards */
  	int i;
  	int retval;
  
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
813
814
  		pr_debug("pcwd_isa_match id=%d
  ", id);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
815

261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
816
  	if (!request_region(base_addr, 4, "PCWD")) {
27c766aaa   Joe Perches   watchdog: Use pr_...
817
818
  		pr_info("Port 0x%04x unavailable
  ", base_addr);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
  		return 0;
  	}
  
  	retval = 0;
  
  	port0 = inb_p(base_addr);	/* For REV A boards */
  	port1 = inb_p(base_addr + 1);	/* For REV C boards */
  	if (port0 != 0xff || port1 != 0xff) {
  		/* Not an 'ff' from a floating bus, so must be a card! */
  		for (i = 0; i < 4; ++i) {
  
  			msleep(500);
  
  			last_port0 = port0;
  			last_port1 = port1;
  
  			port0 = inb_p(base_addr);
  			port1 = inb_p(base_addr + 1);
  
  			/* Has either hearbeat bit changed?  */
  			if ((port0 ^ last_port0) & WD_HRTBT ||
  			    (port1 ^ last_port1) & WD_REVC_HRBT) {
  				retval = 1;
  				break;
  			}
  		}
  	}
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
846
  	release_region(base_addr, 4);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
847
848
849
  
  	return retval;
  }
2d991a164   Bill Pemberton   watchdog: remove ...
850
  static int pcwd_isa_probe(struct device *dev, unsigned int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
  {
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853

5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
854
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
855
856
  		pr_debug("pcwd_isa_probe id=%d
  ", id);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
857

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
859
  	cards_found++;
  	if (cards_found == 1)
27c766aaa   Joe Perches   watchdog: Use pr_...
860
861
  		pr_info("v%s Ken Hollis (kenji@bitgate.com)
  ",
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
862
  							WATCHDOG_VERSION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
864
  
  	if (cards_found > 1) {
27c766aaa   Joe Perches   watchdog: Use pr_...
865
866
  		pr_err("This driver only supports 1 device
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
  		return -ENODEV;
  	}
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
869
  	if (pcwd_ioports[id] == 0x0000) {
27c766aaa   Joe Perches   watchdog: Use pr_...
870
871
  		pr_err("No I/O-Address for card detected
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
  		return -ENODEV;
  	}
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
874
875
876
  	pcwd_private.io_addr = pcwd_ioports[id];
  
  	spin_lock_init(&pcwd_private.io_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
878
  
  	/* Check card's revision */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
879
  	pcwd_private.revision = get_revision();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880

261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
881
882
  	if (!request_region(pcwd_private.io_addr,
  		(pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
27c766aaa   Joe Perches   watchdog: Use pr_...
883
884
885
  		pr_err("I/O address 0x%04x already in use
  ",
  		       pcwd_private.io_addr);
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
886
  		ret = -EIO;
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
887
  		goto error_request_region;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
  	}
  
  	/* Initial variables */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
891
  	pcwd_private.supports_temp = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
  	temp_panic = 0;
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
893
  	pcwd_private.boot_status = 0x0000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
  
  	/* get the boot_status */
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
896
  	pcwd_get_status(&pcwd_private.boot_status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
898
899
  
  	/* clear the "card caused reboot" flag */
  	pcwd_clear_status();
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
900
  	setup_timer(&pcwd_private.timer, pcwd_timer_ping, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
903
904
905
  
  	/*  Disable the board  */
  	pcwd_stop();
  
  	/*  Check whether or not the card supports the temperature device */
85875211a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
906
  	pcwd_check_temperature_support();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907

af3b38d99   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
908
909
  	/* Show info about the card itself */
  	pcwd_show_card_info();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
910

f3dc07330   Wim Van Sebroeck   [WATCHDOG] pcwd_u...
911
912
913
  	/* If heartbeat = 0 then we use the heartbeat from the dip-switches */
  	if (heartbeat == 0)
  		heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)];
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
914
915
  	/* Check that the heartbeat value is within it's range;
  	   if not reset to the default */
fd41fa616   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
916
917
  	if (pcwd_set_heartbeat(heartbeat)) {
  		pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
27c766aaa   Joe Perches   watchdog: Use pr_...
918
919
920
  		pr_info("heartbeat value must be 2 <= heartbeat <= 7200, using %d
  ",
  			WATCHDOG_HEARTBEAT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
  	}
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
922
  	if (pcwd_private.supports_temp) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923
924
  		ret = misc_register(&temp_miscdev);
  		if (ret) {
27c766aaa   Joe Perches   watchdog: Use pr_...
925
926
927
  			pr_err("cannot register miscdev on minor=%d (err=%d)
  ",
  			       TEMP_MINOR, ret);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
928
  			goto error_misc_register_temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
930
931
932
933
  		}
  	}
  
  	ret = misc_register(&pcwd_miscdev);
  	if (ret) {
27c766aaa   Joe Perches   watchdog: Use pr_...
934
935
936
  		pr_err("cannot register miscdev on minor=%d (err=%d)
  ",
  		       WATCHDOG_MINOR, ret);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
937
  		goto error_misc_register_watchdog;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  	}
27c766aaa   Joe Perches   watchdog: Use pr_...
939
940
  	pr_info("initialized. heartbeat=%d sec (nowayout=%d)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
941
942
943
  		heartbeat, nowayout);
  
  	return 0;
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
944
945
946
947
948
  
  error_misc_register_watchdog:
  	if (pcwd_private.supports_temp)
  		misc_deregister(&temp_miscdev);
  error_misc_register_temp:
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
949
950
  	release_region(pcwd_private.io_addr,
  			(pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
951
952
953
954
  error_request_region:
  	pcwd_private.io_addr = 0x0000;
  	cards_found--;
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
  }
4b12b896c   Bill Pemberton   watchdog: remove ...
956
  static int pcwd_isa_remove(struct device *dev, unsigned int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
  {
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
958
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
959
960
  		pr_debug("pcwd_isa_remove id=%d
  ", id);
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
961
962
963
  
  	if (!pcwd_private.io_addr)
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964
965
966
967
968
969
  	/*  Disable the board  */
  	if (!nowayout)
  		pcwd_stop();
  
  	/* Deregister */
  	misc_deregister(&pcwd_miscdev);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
970
  	if (pcwd_private.supports_temp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
  		misc_deregister(&temp_miscdev);
261dcc70a   Alan Cox   [WATCHDOG 32/57] ...
972
973
  	release_region(pcwd_private.io_addr,
  			(pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
a2be87860   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
974
  	pcwd_private.io_addr = 0x0000;
f1c3a0567   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
975
  	cards_found--;
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
976
977
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
  }
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
979
  static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
  {
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
981
  	if (debug >= DEBUG)
27c766aaa   Joe Perches   watchdog: Use pr_...
982
983
  		pr_debug("pcwd_isa_shutdown id=%d
  ", id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984

5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
985
  	pcwd_stop();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
  }
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
987
988
989
  static struct isa_driver pcwd_isa_driver = {
  	.match		= pcwd_isa_match,
  	.probe		= pcwd_isa_probe,
82268714b   Bill Pemberton   watchdog: remove ...
990
  	.remove		= pcwd_isa_remove,
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
991
992
993
994
995
996
  	.shutdown	= pcwd_isa_shutdown,
  	.driver		= {
  		.owner	= THIS_MODULE,
  		.name	= WATCHDOG_NAME,
  	},
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
998
999
  
  static int __init pcwd_init_module(void)
  {
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
1000
  	return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
1003
1004
  }
  
  static void __exit pcwd_cleanup_module(void)
  {
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
1005
  	isa_unregister_driver(&pcwd_isa_driver);
27c766aaa   Joe Perches   watchdog: Use pr_...
1006
1007
  	pr_info("Watchdog Module Unloaded
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
1009
1010
1011
  }
  
  module_init(pcwd_init_module);
  module_exit(pcwd_cleanup_module);
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
1012
1013
  MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
  		"Wim Van Sebroeck <wim@iguana.be>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
  MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
5aa15050a   Wim Van Sebroeck   [WATCHDOG] pcwd.c...
1015
  MODULE_VERSION(WATCHDOG_VERSION);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
  MODULE_LICENSE("GPL");