aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/watchdog/Kconfig21
-rw-r--r--drivers/char/watchdog/Makefile1
-rw-r--r--drivers/char/watchdog/iTCO_wdt.c706
3 files changed, 728 insertions, 0 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index 06f3fa2fe877..89e46d6dfc4e 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -316,6 +316,27 @@ config I8XX_TCO
316 To compile this driver as a module, choose M here: the 316 To compile this driver as a module, choose M here: the
317 module will be called i8xx_tco. 317 module will be called i8xx_tco.
318 318
319config ITCO_WDT
320 tristate "Intel TCO Timer/Watchdog (EXPERIMENTAL)"
321 depends on WATCHDOG && (X86 || IA64) && PCI && EXPERIMENTAL
322 ---help---
323 Hardware driver for the intel TCO timer based watchdog devices.
324 These drivers are included in the Intel 82801 I/O Controller
325 Hub family 'from ICH0 up to ICH7) and in the Intel 6300ESB
326 controller hub.
327
328 The TCO (Total Cost of Ownership) timer is a watchdog timer
329 that will reboot the machine after its second expiration. The
330 expiration time can be configured with the "heartbeat" parameter.
331
332 On some motherboards the driver may fail to reset the chipset's
333 NO_REBOOT flag which prevents the watchdog from rebooting the
334 machine. If this is the case you will get a kernel message like
335 "failed to reset NO_REBOOT flag, reboot disabled by hardware".
336
337 To compile this driver as a module, choose M here: the
338 module will be called iTCO_wdt.
339
319config SC1200_WDT 340config SC1200_WDT
320 tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" 341 tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
321 depends on WATCHDOG && X86 342 depends on WATCHDOG && X86
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 6ffca7cb56ab..7f70abad465a 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_IBMASR) += ibmasr.o
47obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o 47obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
48obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o 48obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o
49obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o 49obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o
50obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
50obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o 51obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
51obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o 52obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
52obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o 53obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
diff --git a/drivers/char/watchdog/iTCO_wdt.c b/drivers/char/watchdog/iTCO_wdt.c
new file mode 100644
index 000000000000..c1a5787fde5b
--- /dev/null
+++ b/drivers/char/watchdog/iTCO_wdt.c
@@ -0,0 +1,706 @@
1/*
2 * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets)
3 *
4 * (c) Copyright 2006 Wim Van Sebroeck <wim@iguana.be>.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
12 * provide warranty for any of this software. This material is
13 * provided "AS-IS" and at no charge.
14 *
15 * The TCO watchdog is implemented in the following I/O controller hubs:
16 * (See the intel documentation on http://developer.intel.com.)
17 * 82801AA (ICH) : document number 290655-003, 290677-014,
18 * 82801AB (ICHO) : document number 290655-003, 290677-014,
19 * 82801BA (ICH2) : document number 290687-002, 298242-027,
20 * 82801BAM (ICH2-M) : document number 290687-002, 298242-027,
21 * 82801CA (ICH3-S) : document number 290733-003, 290739-013,
22 * 82801CAM (ICH3-M) : document number 290716-001, 290718-007,
23 * 82801DB (ICH4) : document number 290744-001, 290745-020,
24 * 82801DBM (ICH4-M) : document number 252337-001, 252663-005,
25 * 82801E (C-ICH) : document number 273599-001, 273645-002,
26 * 82801EB (ICH5) : document number 252516-001, 252517-003,
27 * 82801ER (ICH5R) : document number 252516-001, 252517-003,
28 * 82801FB (ICH6) : document number 301473-002, 301474-007,
29 * 82801FR (ICH6R) : document number 301473-002, 301474-007,
30 * 82801FBM (ICH6-M) : document number 301473-002, 301474-007,
31 * 82801FW (ICH6W) : document number 301473-001, 301474-007,
32 * 82801FRW (ICH6RW) : document number 301473-001, 301474-007,
33 * 82801GB (ICH7) : document number 307013-002, 307014-009,
34 * 82801GR (ICH7R) : document number 307013-002, 307014-009,
35 * 82801GDH (ICH7DH) : document number 307013-002, 307014-009,
36 * 82801GBM (ICH7-M) : document number 307013-002, 307014-009,
37 * 82801GHM (ICH7-M DH) : document number 307013-002, 307014-009,
38 * 6300ESB (6300ESB) : document number 300641-003
39 */
40
41/*
42 * Includes, defines, variables, module parameters, ...
43 */
44
45/* Module and version information */
46#define DRV_NAME "iTCO_wdt"
47#define DRV_VERSION "1.00"
48#define DRV_RELDATE "21-May-2006"
49#define PFX DRV_NAME ": "
50
51/* Includes */
52#include <linux/config.h> /* For CONFIG_WATCHDOG_NOWAYOUT/... */
53#include <linux/module.h> /* For module specific items */
54#include <linux/moduleparam.h> /* For new moduleparam's */
55#include <linux/types.h> /* For standard types (like size_t) */
56#include <linux/errno.h> /* For the -ENODEV/... values */
57#include <linux/kernel.h> /* For printk/panic/... */
58#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
59#include <linux/watchdog.h> /* For the watchdog specific items */
60#include <linux/notifier.h> /* For notifier support */
61#include <linux/reboot.h> /* For reboot_notifier stuff */
62#include <linux/init.h> /* For __init/__exit/... */
63#include <linux/fs.h> /* For file operations */
64#include <linux/pci.h> /* For pci functions */
65#include <linux/ioport.h> /* For io-port access */
66#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
67
68#include <asm/uaccess.h> /* For copy_to_user/put_user/... */
69#include <asm/io.h> /* For inb/outb/... */
70
71/* TCO related info */
72enum iTCO_chipsets {
73 TCO_ICH = 0, /* ICH */
74 TCO_ICH0, /* ICH0 */
75 TCO_ICH2, /* ICH2 */
76 TCO_ICH2M, /* ICH2-M */
77 TCO_ICH3, /* ICH3-S */
78 TCO_ICH3M, /* ICH3-M */
79 TCO_ICH4, /* ICH4 */
80 TCO_ICH4M, /* ICH4-M */
81 TCO_CICH, /* C-ICH */
82 TCO_ICH5, /* ICH5 & ICH5R */
83 TCO_6300ESB, /* 6300ESB */
84 TCO_ICH6, /* ICH6 & ICH6R */
85 TCO_ICH6M, /* ICH6-M */
86 TCO_ICH6W, /* ICH6W & ICH6RW */
87 TCO_ICH7, /* ICH7 & ICH7R */
88 TCO_ICH7M, /* ICH7-M */
89 TCO_ICH7MDH, /* ICH7-M DH */
90};
91
92static struct {
93 char *name;
94 unsigned int iTCO_version;
95} iTCO_chipset_info[] __devinitdata = {
96 {"ICH", 1},
97 {"ICH0", 1},
98 {"ICH2", 1},
99 {"ICH2-M", 1},
100 {"ICH3-S", 1},
101 {"ICH3-M", 1},
102 {"ICH4", 1},
103 {"ICH4-M", 1},
104 {"C-ICH", 1},
105 {"ICH5 or ICH5R", 1},
106 {"6300ESB", 1},
107 {"ICH6 or ICH6R", 2},
108 {"ICH6-M", 2},
109 {"ICH6W or ICH6RW", 2},
110 {"ICH7 or ICH7R", 2},
111 {"ICH7-M", 2},
112 {"ICH7-M DH", 2},
113 {NULL,0}
114};
115
116/*
117 * This data only exists for exporting the supported PCI ids
118 * via MODULE_DEVICE_TABLE. We do not actually register a
119 * pci_driver, because the I/O Controller Hub has also other
120 * functions that probably will be registered by other drivers.
121 */
122static struct pci_device_id iTCO_wdt_pci_tbl[] = {
123 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH },
124 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH0 },
125 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2 },
126 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2M },
127 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3 },
128 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3M },
129 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4 },
130 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4M },
131 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_CICH },
132 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH5 },
133 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_6300ESB },
134 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6 },
135 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6M },
136 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6W },
137 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7 },
138 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7M },
139 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7MDH },
140 { 0, }, /* End of list */
141};
142MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl);
143
144/* Address definitions for the TCO */
145#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 /* TCO base address */
146#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 /* SMI Control and Enable Register */
147
148#define TCO_RLD TCOBASE + 0x00 /* TCO Timer Reload and Current Value */
149#define TCOv1_TMR TCOBASE + 0x01 /* TCOv1 Timer Initial Value */
150#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */
151#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */
152#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */
153#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */
154#define TCO1_CNT TCOBASE + 0x08 /* TCO1 Control Register */
155#define TCO2_CNT TCOBASE + 0x0a /* TCO2 Control Register */
156#define TCOv2_TMR TCOBASE + 0x12 /* TCOv2 Timer Initial Value */
157
158/* internal variables */
159static unsigned long is_active;
160static char expect_release;
161static struct { /* this is private data for the iTCO_wdt device */
162 unsigned int iTCO_version; /* TCO version/generation */
163 unsigned long ACPIBASE; /* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
164 unsigned long __iomem *gcs; /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2) */
165 spinlock_t io_lock; /* the lock for io operations */
166 struct pci_dev *pdev; /* the PCI-device */
167} iTCO_wdt_private;
168
169/* module parameters */
170#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
171static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
172module_param(heartbeat, int, 0);
173MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39 (TCO v1) or 613 (TCO v2), default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
174
175static int nowayout = WATCHDOG_NOWAYOUT;
176module_param(nowayout, int, 0);
177MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
178
179/*
180 * Some TCO specific functions
181 */
182
183static inline unsigned int seconds_to_ticks(int seconds)
184{
185 /* the internal timer is stored as ticks which decrement
186 * every 0.6 seconds */
187 return (seconds * 10) / 6;
188}
189
190static void iTCO_wdt_set_NO_REBOOT_bit(void)
191{
192 u32 val32;
193
194 /* Set the NO_REBOOT bit: this disables reboots */
195 if (iTCO_wdt_private.iTCO_version == 2) {
196 val32 = readl(iTCO_wdt_private.gcs);
197 val32 |= 0x00000020;
198 writel(val32, iTCO_wdt_private.gcs);
199 } else if (iTCO_wdt_private.iTCO_version == 1) {
200 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
201 val32 |= 0x00000002;
202 pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
203 }
204}
205
206static int iTCO_wdt_unset_NO_REBOOT_bit(void)
207{
208 int ret = 0;
209 u32 val32;
210
211 /* Unset the NO_REBOOT bit: this enables reboots */
212 if (iTCO_wdt_private.iTCO_version == 2) {
213 val32 = readl(iTCO_wdt_private.gcs);
214 val32 &= 0xffffffdf;
215 writel(val32, iTCO_wdt_private.gcs);
216
217 val32 = readl(iTCO_wdt_private.gcs);
218 if (val32 & 0x00000020)
219 ret = -EIO;
220 } else if (iTCO_wdt_private.iTCO_version == 1) {
221 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
222 val32 &= 0xfffffffd;
223 pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
224
225 pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
226 if (val32 & 0x00000002)
227 ret = -EIO;
228 }
229
230 return ret; /* returns: 0 = OK, -EIO = Error */
231}
232
233static int iTCO_wdt_start(void)
234{
235 unsigned int val;
236
237 spin_lock(&iTCO_wdt_private.io_lock);
238
239 /* disable chipset's NO_REBOOT bit */
240 if (iTCO_wdt_unset_NO_REBOOT_bit()) {
241 printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
242 return -EIO;
243 }
244
245 /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
246 val = inw(TCO1_CNT);
247 val &= 0xf7ff;
248 outw(val, TCO1_CNT);
249 val = inw(TCO1_CNT);
250 spin_unlock(&iTCO_wdt_private.io_lock);
251
252 if (val & 0x0800)
253 return -1;
254 return 0;
255}
256
257static int iTCO_wdt_stop(void)
258{
259 unsigned int val;
260
261 spin_lock(&iTCO_wdt_private.io_lock);
262
263 /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
264 val = inw(TCO1_CNT);
265 val |= 0x0800;
266 outw(val, TCO1_CNT);
267 val = inw(TCO1_CNT);
268
269 /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
270 iTCO_wdt_set_NO_REBOOT_bit();
271
272 spin_unlock(&iTCO_wdt_private.io_lock);
273
274 if ((val & 0x0800) == 0)
275 return -1;
276 return 0;
277}
278
279static int iTCO_wdt_keepalive(void)
280{
281 spin_lock(&iTCO_wdt_private.io_lock);
282
283 /* Reload the timer by writing to the TCO Timer Counter register */
284 if (iTCO_wdt_private.iTCO_version == 2) {
285 outw(0x01, TCO_RLD);
286 } else if (iTCO_wdt_private.iTCO_version == 1) {
287 outb(0x01, TCO_RLD);
288 }
289
290 spin_unlock(&iTCO_wdt_private.io_lock);
291 return 0;
292}
293
294static int iTCO_wdt_set_heartbeat(int t)
295{
296 unsigned int val16;
297 unsigned char val8;
298 unsigned int tmrval;
299
300 tmrval = seconds_to_ticks(t);
301 /* from the specs: */
302 /* "Values of 0h-3h are ignored and should not be attempted" */
303 if (tmrval < 0x04)
304 return -EINVAL;
305 if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) ||
306 ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
307 return -EINVAL;
308
309 /* Write new heartbeat to watchdog */
310 if (iTCO_wdt_private.iTCO_version == 2) {
311 spin_lock(&iTCO_wdt_private.io_lock);
312 val16 = inw(TCOv2_TMR);
313 val16 &= 0xfc00;
314 val16 |= tmrval;
315 outw(val16, TCOv2_TMR);
316 val16 = inw(TCOv2_TMR);
317 spin_unlock(&iTCO_wdt_private.io_lock);
318
319 if ((val16 & 0x3ff) != tmrval)
320 return -EINVAL;
321 } else if (iTCO_wdt_private.iTCO_version == 1) {
322 spin_lock(&iTCO_wdt_private.io_lock);
323 val8 = inb(TCOv1_TMR);
324 val8 &= 0xc0;
325 val8 |= (tmrval & 0xff);
326 outb(val8, TCOv1_TMR);
327 val8 = inb(TCOv1_TMR);
328 spin_unlock(&iTCO_wdt_private.io_lock);
329
330 if ((val8 & 0x3f) != tmrval)
331 return -EINVAL;
332 }
333
334 heartbeat = t;
335 return 0;
336}
337
338static int iTCO_wdt_get_timeleft (int *time_left)
339{
340 unsigned int val16;
341 unsigned char val8;
342
343 /* read the TCO Timer */
344 if (iTCO_wdt_private.iTCO_version == 2) {
345 spin_lock(&iTCO_wdt_private.io_lock);
346 val16 = inw(TCO_RLD);
347 val16 &= 0x3ff;
348 spin_unlock(&iTCO_wdt_private.io_lock);
349
350 *time_left = (val16 * 6) / 10;
351 } else if (iTCO_wdt_private.iTCO_version == 1) {
352 spin_lock(&iTCO_wdt_private.io_lock);
353 val8 = inb(TCO_RLD);
354 val8 &= 0x3f;
355 spin_unlock(&iTCO_wdt_private.io_lock);
356
357 *time_left = (val8 * 6) / 10;
358 }
359 return 0;
360}
361
362/*
363 * /dev/watchdog handling
364 */
365
366static int iTCO_wdt_open (struct inode *inode, struct file *file)
367{
368 /* /dev/watchdog can only be opened once */
369 if (test_and_set_bit(0, &is_active))
370 return -EBUSY;
371
372 /*
373 * Reload and activate timer
374 */
375 iTCO_wdt_keepalive();
376 iTCO_wdt_start();
377 return nonseekable_open(inode, file);
378}
379
380static int iTCO_wdt_release (struct inode *inode, struct file *file)
381{
382 /*
383 * Shut off the timer.
384 */
385 if (expect_release == 42) {
386 iTCO_wdt_stop();
387 } else {
388 printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
389 iTCO_wdt_keepalive();
390 }
391 clear_bit(0, &is_active);
392 expect_release = 0;
393 return 0;
394}
395
396static ssize_t iTCO_wdt_write (struct file *file, const char __user *data,
397 size_t len, loff_t * ppos)
398{
399 /* See if we got the magic character 'V' and reload the timer */
400 if (len) {
401 if (!nowayout) {
402 size_t i;
403
404 /* note: just in case someone wrote the magic character
405 * five months ago... */
406 expect_release = 0;
407
408 /* scan to see whether or not we got the magic character */
409 for (i = 0; i != len; i++) {
410 char c;
411 if (get_user(c, data+i))
412 return -EFAULT;
413 if (c == 'V')
414 expect_release = 42;
415 }
416 }
417
418 /* someone wrote to us, we should reload the timer */
419 iTCO_wdt_keepalive();
420 }
421 return len;
422}
423
424static int iTCO_wdt_ioctl (struct inode *inode, struct file *file,
425 unsigned int cmd, unsigned long arg)
426{
427 int new_options, retval = -EINVAL;
428 int new_heartbeat;
429 int time_left;
430 void __user *argp = (void __user *)arg;
431 int __user *p = argp;
432 static struct watchdog_info ident = {
433 .options = WDIOF_SETTIMEOUT |
434 WDIOF_KEEPALIVEPING |
435 WDIOF_MAGICCLOSE,
436 .firmware_version = 0,
437 .identity = DRV_NAME,
438 };
439
440 switch (cmd) {
441 case WDIOC_GETSUPPORT:
442 return copy_to_user(argp, &ident,
443 sizeof (ident)) ? -EFAULT : 0;
444
445 case WDIOC_GETSTATUS:
446 case WDIOC_GETBOOTSTATUS:
447 return put_user(0, p);
448
449 case WDIOC_KEEPALIVE:
450 iTCO_wdt_keepalive();
451 return 0;
452
453 case WDIOC_SETOPTIONS:
454 {
455 if (get_user(new_options, p))
456 return -EFAULT;
457
458 if (new_options & WDIOS_DISABLECARD) {
459 iTCO_wdt_stop();
460 retval = 0;
461 }
462
463 if (new_options & WDIOS_ENABLECARD) {
464 iTCO_wdt_keepalive();
465 iTCO_wdt_start();
466 retval = 0;
467 }
468
469 return retval;
470 }
471
472 case WDIOC_SETTIMEOUT:
473 {
474 if (get_user(new_heartbeat, p))
475 return -EFAULT;
476
477 if (iTCO_wdt_set_heartbeat(new_heartbeat))
478 return -EINVAL;
479
480 iTCO_wdt_keepalive();
481 /* Fall */
482 }
483
484 case WDIOC_GETTIMEOUT:
485 return put_user(heartbeat, p);
486
487 case WDIOC_GETTIMELEFT:
488 {
489 if (iTCO_wdt_get_timeleft(&time_left))
490 return -EINVAL;
491
492 return put_user(time_left, p);
493 }
494
495 default:
496 return -ENOIOCTLCMD;
497 }
498}
499
500/*
501 * Notify system
502 */
503
504static int iTCO_wdt_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
505{
506 if (code==SYS_DOWN || code==SYS_HALT) {
507 /* Turn the WDT off */
508 iTCO_wdt_stop();
509 }
510
511 return NOTIFY_DONE;
512}
513
514/*
515 * Kernel Interfaces
516 */
517
518static struct file_operations iTCO_wdt_fops = {
519 .owner = THIS_MODULE,
520 .llseek = no_llseek,
521 .write = iTCO_wdt_write,
522 .ioctl = iTCO_wdt_ioctl,
523 .open = iTCO_wdt_open,
524 .release = iTCO_wdt_release,
525};
526
527static struct miscdevice iTCO_wdt_miscdev = {
528 .minor = WATCHDOG_MINOR,
529 .name = "watchdog",
530 .fops = &iTCO_wdt_fops,
531};
532
533static struct notifier_block iTCO_wdt_notifier = {
534 .notifier_call = iTCO_wdt_notify_sys,
535};
536
537/*
538 * Init & exit routines
539 */
540
541static int __init iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent)
542{
543 int ret;
544 u32 base_address;
545 unsigned long RCBA;
546 unsigned long val32;
547
548 /*
549 * Find the ACPI/PM base I/O address which is the base
550 * for the TCO registers (TCOBASE=ACPIBASE + 0x60)
551 * ACPIBASE is bits [15:7] from 0x40-0x43
552 */
553 pci_read_config_dword(pdev, 0x40, &base_address);
554 base_address &= 0x00007f80;
555 if (base_address == 0x00000000) {
556 /* Something's wrong here, ACPIBASE has to be set */
557 printk(KERN_ERR PFX "failed to get TCOBASE address\n");
558 return -ENODEV;
559 }
560 iTCO_wdt_private.iTCO_version = iTCO_chipset_info[ent->driver_data].iTCO_version;
561 iTCO_wdt_private.ACPIBASE = base_address;
562 iTCO_wdt_private.pdev = pdev;
563
564 /* Get the Memory-Mapped GCS register, we need it for the NO_REBOOT flag (TCO v2) */
565 /* To get access to it you have to read RCBA from PCI Config space 0xf0
566 and use it as base. GCS = RCBA + ICH6_GCS(0x3410). */
567 if (iTCO_wdt_private.iTCO_version == 2) {
568 pci_read_config_dword(pdev, 0xf0, &base_address);
569 RCBA = base_address & 0xffffc000;
570 iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410),4);
571 }
572
573 /* Check chipset's NO_REBOOT bit */
574 if (iTCO_wdt_unset_NO_REBOOT_bit()) {
575 printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
576 ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
577 goto out;
578 }
579
580 /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
581 iTCO_wdt_set_NO_REBOOT_bit();
582
583 /* Set the TCO_EN bit in SMI_EN register */
584 if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
585 printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n",
586 SMI_EN );
587 ret = -EIO;
588 goto out;
589 }
590 val32 = inl(SMI_EN);
591 val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
592 outl(val32, SMI_EN);
593 release_region(SMI_EN, 4);
594
595 /* The TCO I/O registers reside in a 32-byte range pointed to by the TCOBASE value */
596 if (!request_region (TCOBASE, 0x20, "iTCO_wdt")) {
597 printk (KERN_ERR PFX "I/O address 0x%04lx already in use\n",
598 TCOBASE);
599 ret = -EIO;
600 goto out;
601 }
602
603 printk(KERN_INFO PFX "Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
604 iTCO_chipset_info[ent->driver_data].name,
605 iTCO_chipset_info[ent->driver_data].iTCO_version,
606 TCOBASE);
607
608 /* Clear out the (probably old) status */
609 outb(0, TCO1_STS);
610 outb(3, TCO2_STS);
611
612 /* Make sure the watchdog is not running */
613 iTCO_wdt_stop();
614
615 /* Check that the heartbeat value is within it's range ; if not reset to the default */
616 if (iTCO_wdt_set_heartbeat(heartbeat)) {
617 iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
618 printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39 (TCO v1) or 613 (TCO v2), using %d\n",
619 heartbeat);
620 }
621
622 ret = register_reboot_notifier(&iTCO_wdt_notifier);
623 if (ret != 0) {
624 printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
625 ret);
626 goto unreg_region;
627 }
628
629 ret = misc_register(&iTCO_wdt_miscdev);
630 if (ret != 0) {
631 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
632 WATCHDOG_MINOR, ret);
633 goto unreg_notifier;
634 }
635
636 printk (KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
637 heartbeat, nowayout);
638
639 return 0;
640
641unreg_notifier:
642 unregister_reboot_notifier(&iTCO_wdt_notifier);
643unreg_region:
644 release_region (TCOBASE, 0x20);
645out:
646 if (iTCO_wdt_private.iTCO_version == 2)
647 iounmap(iTCO_wdt_private.gcs);
648 iTCO_wdt_private.ACPIBASE = 0;
649 return ret;
650}
651
652static void __exit iTCO_wdt_cleanup(void)
653{
654 /* Stop the timer before we leave */
655 if (!nowayout)
656 iTCO_wdt_stop();
657
658 /* Deregister */
659 misc_deregister(&iTCO_wdt_miscdev);
660 unregister_reboot_notifier(&iTCO_wdt_notifier);
661 release_region(TCOBASE, 0x20);
662 if (iTCO_wdt_private.iTCO_version == 2)
663 iounmap(iTCO_wdt_private.gcs);
664}
665
666static int __init iTCO_wdt_init_module(void)
667{
668 int found = 0;
669 struct pci_dev *pdev = NULL;
670 const struct pci_device_id *ent;
671
672 spin_lock_init(&iTCO_wdt_private.io_lock);
673
674 for_each_pci_dev(pdev) {
675 ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
676 if (ent) {
677 if (!(iTCO_wdt_init(pdev, ent))) {
678 found++;
679 break;
680 }
681 }
682 }
683
684 if (!found) {
685 printk(KERN_INFO PFX "No card detected\n");
686 return -ENODEV;
687 }
688
689 return 0;
690}
691
692static void __exit iTCO_wdt_cleanup_module(void)
693{
694 if (iTCO_wdt_private.ACPIBASE)
695 iTCO_wdt_cleanup();
696
697 printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
698}
699
700module_init(iTCO_wdt_init_module);
701module_exit(iTCO_wdt_cleanup_module);
702
703MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
704MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
705MODULE_LICENSE("GPL");
706MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);