Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
authorLinus Torvalds <torvalds@g5.osdl.org>
Mon, 23 Oct 2006 22:56:26 +0000 (15:56 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 23 Oct 2006 22:56:26 +0000 (15:56 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog: (33 commits)
  [WATCHDOG] remove experimental on iTCO_wdt.c
  [WATCHDOG] Atmel AT91RM9200 rename.
  [WATCHDOG] includes for sample watchdog program.
  [WATCHDOG] watchdog/iTCO_wdt: fix bug related to gcc uninit warning
  [WATCHDOG] add ich8 support to iTCO_wdt.c (patch 2)
  [WATCHDOG] add ich8 support to iTCO_wdt.c
  [WATCHDOG] ioremap balanced with iounmap for drivers/char/watchdog/s3c2410_wdt.c
  [WATCHDOG] w83697hf/hg WDT driver - Kconfig patch
  [WATCHDOG] w83697hf/hg WDT driver - autodetect patch
  [WATCHDOG] w83697hf/hg WDT driver - patch 16
  [WATCHDOG] w83697hf/hg WDT driver - patch 15
  [WATCHDOG] w83697hf/hg WDT driver - patch 14
  [WATCHDOG] w83697hf/hg WDT driver - patch 13
  [WATCHDOG] w83697hf/hg WDT driver - patch 12
  [WATCHDOG] w83697hf/hg WDT driver - patch 11
  [WATCHDOG] w83697hf/hg WDT driver - patch 10
  [WATCHDOG] w83697hf/hg WDT driver - patch 9
  [WATCHDOG] w83697hf/hg WDT driver - patch 8
  [WATCHDOG] w83697hf/hg WDT driver - patch 7
  [WATCHDOG] w83697hf/hg WDT driver - patch 6
  ...

15 files changed:
Documentation/watchdog/src/watchdog-simple.c
arch/arm/configs/at91rm9200dk_defconfig
arch/arm/configs/at91rm9200ek_defconfig
arch/arm/configs/csb337_defconfig
arch/arm/configs/csb637_defconfig
arch/arm/configs/kafa_defconfig
arch/arm/configs/onearm_defconfig
drivers/char/watchdog/Kconfig
drivers/char/watchdog/Makefile
drivers/char/watchdog/at91rm9200_wdt.c [moved from drivers/char/watchdog/at91_wdt.c with 100% similarity]
drivers/char/watchdog/iTCO_wdt.c
drivers/char/watchdog/s3c2410_wdt.c
drivers/char/watchdog/smsc37b787_wdt.c [new file with mode: 0644]
drivers/char/watchdog/w83627hf_wdt.c
drivers/char/watchdog/w83697hf_wdt.c [new file with mode: 0644]

index 85cf17c48669d73111ff14ae8490417425da7030..47801bc7e742dd44a650322e2e8b79bbf463adcf 100644 (file)
@@ -1,4 +1,6 @@
+#include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include <fcntl.h>
 
 int main(int argc, const char *argv[]) {
index c82e4667f45e25e4a9e2efb3e922b77efe6df987..b43041476e02b9c550f48d82fabf536ea06aa052 100644 (file)
@@ -577,7 +577,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
 # Watchdog Device Drivers
 #
 # CONFIG_SOFT_WATCHDOG is not set
-CONFIG_AT91_WATCHDOG=y
+CONFIG_AT91RM9200_WATCHDOG=y
 
 #
 # USB-based Watchdog Cards
index b983fc59aa42b437d42aec4af7bc6795819f23b5..d96fc8386e2f024ea7905e54a3b287a7f5244294 100644 (file)
@@ -558,7 +558,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
 # Watchdog Device Drivers
 #
 # CONFIG_SOFT_WATCHDOG is not set
-CONFIG_AT91_WATCHDOG=y
+CONFIG_AT91RM9200_WATCHDOG=y
 
 #
 # USB-based Watchdog Cards
index a2d6fd398f16b4acea1abd03b21e6f0e2e85a74d..20e68250d835196252e65d142e1b9f2e302450b8 100644 (file)
@@ -615,7 +615,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
 # Watchdog Device Drivers
 #
 # CONFIG_SOFT_WATCHDOG is not set
-CONFIG_AT91_WATCHDOG=y
+CONFIG_AT91RM9200_WATCHDOG=y
 
 #
 # USB-based Watchdog Cards
index 2a1ac6c60abc0a1ce184008f45fb0b28a2b56154..df8595ac031f15cb6a72406e965acd1f80f51eca 100644 (file)
@@ -615,7 +615,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
 # Watchdog Device Drivers
 #
 # CONFIG_SOFT_WATCHDOG is not set
-CONFIG_AT91_WATCHDOG=y
+CONFIG_AT91RM9200_WATCHDOG=y
 
 #
 # USB-based Watchdog Cards
index 54fcd75779da062a12865aeb2e795f8127ddb206..a4cdafc1548a1113abe42de978a1595a3bfc83d9 100644 (file)
@@ -560,7 +560,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
 # Watchdog Device Drivers
 #
 # CONFIG_SOFT_WATCHDOG is not set
-CONFIG_AT91_WATCHDOG=y
+CONFIG_AT91RM9200_WATCHDOG=y
 # CONFIG_NVRAM is not set
 # CONFIG_DTLK is not set
 # CONFIG_R3964 is not set
index cb1d94f9049ee4e447a9d5ae31840abc4289c884..9b9f2155af35fedcd8999102a3eab37ca4c59d46 100644 (file)
@@ -607,7 +607,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
 # Watchdog Device Drivers
 #
 # CONFIG_SOFT_WATCHDOG is not set
-CONFIG_AT91_WATCHDOG=y
+CONFIG_AT91RM9200_WATCHDOG=y
 
 #
 # USB-based Watchdog Cards
index 89e46d6dfc4ef789af2e5cfe3f89631607f4c345..0187b1185323d026395c6afd7b76f0c1355dc0b6 100644 (file)
@@ -13,7 +13,7 @@ config WATCHDOG
          subsequently opening the file and then failing to write to it for
          longer than 1 minute will result in rebooting the machine. This
          could be useful for a networked machine that needs to come back
-         online as fast as possible after a lock-up. There's both a watchdog
+         on-line as fast as possible after a lock-up. There's both a watchdog
          implementation entirely in software (which can sometimes fail to
          reboot the machine) and a driver for hardware watchdog boards, which
          are more robust and can also keep track of the temperature inside
@@ -60,7 +60,7 @@ config SOFT_WATCHDOG
 
 # ARM Architecture
 
-config AT91_WATCHDOG
+config AT91RM9200_WATCHDOG
        tristate "AT91RM9200 watchdog"
        depends on WATCHDOG && ARCH_AT91RM9200
        help
@@ -71,7 +71,7 @@ config 21285_WATCHDOG
        tristate "DC21285 watchdog"
        depends on WATCHDOG && FOOTBRIDGE
        help
-         The Intel Footbridge chip contains a builtin watchdog circuit. Say Y
+         The Intel Footbridge chip contains a built-in watchdog circuit. Say Y
          here if you wish to use this. Alternatively say M to compile the
          driver as a module, which will be called wdt285.
 
@@ -269,11 +269,11 @@ config IB700_WDT
          Most people will say N.
 
 config IBMASR
-        tristate "IBM Automatic Server Restart"
-        depends on WATCHDOG && X86
-        help
+       tristate "IBM Automatic Server Restart"
+       depends on WATCHDOG && X86
+       help
          This is the driver for the IBM Automatic Server Restart watchdog
-         timer builtin into some eServer xSeries machines.
+         timer built-in into some eServer xSeries machines.
 
          To compile this driver as a module, choose M here: the
          module will be called ibmasr.
@@ -316,13 +316,16 @@ config I8XX_TCO
          To compile this driver as a module, choose M here: the
          module will be called i8xx_tco.
 
+         Note: This driver will be removed in the near future. Please
+         use the Intel TCO Timer/Watchdog driver.
+
 config ITCO_WDT
-       tristate "Intel TCO Timer/Watchdog (EXPERIMENTAL)"
-       depends on WATCHDOG && (X86 || IA64) && PCI && EXPERIMENTAL
+       tristate "Intel TCO Timer/Watchdog"
+       depends on WATCHDOG && (X86 || IA64) && PCI
        ---help---
          Hardware driver for the intel TCO timer based watchdog devices.
          These drivers are included in the Intel 82801 I/O Controller
-         Hub family 'from ICH0 up to ICH7) and in the Intel 6300ESB
+         Hub family (from ICH0 up to ICH8) and in the Intel 6300ESB
          controller hub.
 
          The TCO (Total Cost of Ownership) timer is a watchdog timer
@@ -395,6 +398,26 @@ config CPU5_WDT
          To compile this driver as a module, choose M here: the
          module will be called cpu5wdt.
 
+config SMSC37B787_WDT
+       tristate "Winbond SMsC37B787 Watchdog Timer"
+       depends on WATCHDOG && X86
+       ---help---
+         This is the driver for the hardware watchdog component on the
+         Winbond SMsC37B787 chipset as used on the NetRunner Mainboard
+         from Vision Systems and maybe others.
+
+         This watchdog simply watches your kernel to make sure it doesn't
+         freeze, and if it does, it reboots your computer after a certain
+         amount of time.
+
+         Usually a userspace daemon will notify the kernel WDT driver that
+         userspace is still alive, at regular intervals.
+
+         To compile this driver as a module, choose M here: the
+         module will be called smsc37b787_wdt.
+
+         Most people will say N.
+
 config W83627HF_WDT
        tristate "W83627HF Watchdog Timer"
        depends on WATCHDOG && X86
@@ -410,6 +433,21 @@ config W83627HF_WDT
 
          Most people will say N.
 
+config W83697HF_WDT
+       tristate "W83697HF/W83697HG Watchdog Timer"
+       depends on WATCHDOG && X86
+       ---help---
+         This is the driver for the hardware watchdog on the W83697HF/HG
+         chipset as used in Dedibox/VIA motherboards (and likely others).
+         This watchdog simply watches your kernel to make sure it doesn't
+         freeze, and if it does, it reboots your computer after a certain
+         amount of time.
+
+         To compile this driver as a module, choose M here: the
+         module will be called w83697hf_wdt.
+
+         Most people will say N.
+
 config W83877F_WDT
        tristate "W83877F (EMACS) Watchdog Timer"
        depends on WATCHDOG && X86
@@ -443,7 +481,7 @@ config MACHZ_WDT
        depends on WATCHDOG && X86
        ---help---
          If you are using a ZF Micro MachZ processor, say Y here, otherwise
-         N.  This is the driver for the watchdog timer builtin on that
+         N.  This is the driver for the watchdog timer built-in on that
          processor using ZF-Logic interface.  This watchdog simply watches
          your kernel to make sure it doesn't freeze, and if it does, it
          reboots your computer after a certain amount of time.
@@ -472,7 +510,6 @@ config SBC_EPX_C3_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called sbc_epx_c3.
 
-
 # PowerPC Architecture
 
 config 8xx_WDT
@@ -502,7 +539,7 @@ config WATCHDOG_RTAS
        help
          This driver adds watchdog support for the RTAS watchdog.
 
-          To compile this driver as a module, choose M here. The module
+         To compile this driver as a module, choose M here. The module
          will be called wdrtas.
 
 # MIPS Architecture
@@ -556,7 +593,7 @@ config SH_WDT_MMAP
        help
          If you say Y here, user applications will be able to mmap the
          WDT/CPG registers.
-#
+
 # SPARC64 Architecture
 
 config WATCHDOG_CP1XXX
index 7f70abad465a3b846d2c1b5558c23496b9ea965c..36440497047c40476011616f4ee71cf651418f2e 100644 (file)
@@ -23,7 +23,7 @@ obj-$(CONFIG_WDTPCI) += wdt_pci.o
 obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
-obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o
+obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
 obj-$(CONFIG_977_WATCHDOG) += wdt977.o
@@ -53,7 +53,9 @@ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
 obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
 obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
 obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
+obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
 obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
+obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
 obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
 obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
 obj-$(CONFIG_MACHZ_WDT) += machzwd.o
index aaac94db0d8b7ca9e40e9fd04fc93585c9101192..b6f29cb8bd397bd707a27925e07e80801a3d612a 100644 (file)
  *     82801GDH (ICH7DH)    : document number 307013-002, 307014-009,
  *     82801GBM (ICH7-M)    : document number 307013-002, 307014-009,
  *     82801GHM (ICH7-M DH) : document number 307013-002, 307014-009,
+ *     82801HB  (ICH8)      : document number 313056-002, 313057-004,
+ *     82801HR  (ICH8R)     : document number 313056-002, 313057-004,
+ *     82801HH  (ICH8DH)    : document number 313056-002, 313057-004,
+ *     82801HO  (ICH8DO)    : document number 313056-002, 313057-004,
  *     6300ESB  (6300ESB)   : document number 300641-003
  */
 
@@ -45,7 +49,7 @@
 /* Module and version information */
 #define DRV_NAME        "iTCO_wdt"
 #define DRV_VERSION     "1.00"
-#define DRV_RELDATE     "30-Jul-2006"
+#define DRV_RELDATE     "08-Oct-2006"
 #define PFX            DRV_NAME ": "
 
 /* Includes */
@@ -85,6 +89,9 @@ enum iTCO_chipsets {
        TCO_ICH7,       /* ICH7 & ICH7R */
        TCO_ICH7M,      /* ICH7-M */
        TCO_ICH7MDH,    /* ICH7-M DH */
+       TCO_ICH8,       /* ICH8 & ICH8R */
+       TCO_ICH8DH,     /* ICH8DH */
+       TCO_ICH8DO,     /* ICH8DO */
 };
 
 static struct {
@@ -108,6 +115,9 @@ static struct {
        {"ICH7 or ICH7R", 2},
        {"ICH7-M", 2},
        {"ICH7-M DH", 2},
+       {"ICH8 or ICH8R", 2},
+       {"ICH8DH", 2},
+       {"ICH8DO", 2},
        {NULL,0}
 };
 
@@ -135,6 +145,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = {
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0,      PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7    },
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1,      PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7M   },
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31,     PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7MDH },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0,      PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH8    },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2,      PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH8DH  },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3,      PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH8DO  },
        { 0, },                 /* End of list */
 };
 MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl);
@@ -355,7 +368,8 @@ static int iTCO_wdt_get_timeleft (int *time_left)
                spin_unlock(&iTCO_wdt_private.io_lock);
 
                *time_left = (val8 * 6) / 10;
-       }
+       } else
+               return -EINVAL;
        return 0;
 }
 
@@ -426,7 +440,6 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file,
 {
        int new_options, retval = -EINVAL;
        int new_heartbeat;
-       int time_left;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
        static struct watchdog_info ident = {
@@ -486,6 +499,8 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file,
 
                case WDIOC_GETTIMELEFT:
                {
+                       int time_left;
+
                        if (iTCO_wdt_get_timeleft(&time_left))
                                return -EINVAL;
 
index 68b1ca976d53236f6fa450abddd10a8d520140cb..18cb050c38624b6eb76bc658849c8cecd107c950 100644 (file)
@@ -380,18 +380,21 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
                printk(KERN_INFO PFX "failed to get irq resource\n");
+               iounmap(wdt_base);
                return -ENOENT;
        }
 
        ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev);
        if (ret != 0) {
                printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
+               iounmap(wdt_base);
                return ret;
        }
 
        wdt_clock = clk_get(&pdev->dev, "watchdog");
        if (wdt_clock == NULL) {
                printk(KERN_INFO PFX "failed to find watchdog clock source\n");
+               iounmap(wdt_base);
                return -ENOENT;
        }
 
@@ -415,6 +418,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
        if (ret) {
                printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
                        WATCHDOG_MINOR, ret);
+               iounmap(wdt_base);
                return ret;
        }
 
@@ -451,6 +455,7 @@ static int s3c2410wdt_remove(struct platform_device *dev)
                wdt_clock = NULL;
        }
 
+       iounmap(wdt_base);
        misc_deregister(&s3c2410wdt_miscdev);
        return 0;
 }
diff --git a/drivers/char/watchdog/smsc37b787_wdt.c b/drivers/char/watchdog/smsc37b787_wdt.c
new file mode 100644 (file)
index 0000000..9f56913
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+ *     SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
+ *
+ *      Based on acquirewdt.c by Alan Cox <alan@redhat.com>
+ *       and some other existing drivers
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ *     The authors do NOT admit liability nor provide warranty for
+ *     any of this software. This material is provided "AS-IS" in
+ *      the hope that it may be useful for others.
+ *
+ *     (C) Copyright 2003-2006  Sven Anders <anders@anduras.de>
+ *
+ *  History:
+ *     2003 - Created version 1.0 for Linux 2.4.x.
+ *     2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
+ *             features. Released version 1.1
+ *
+ *  Theory of operation:
+ *
+ *      A Watchdog Timer (WDT) is a hardware circuit that can
+ *      reset the computer system in case of a software fault.
+ *      You probably knew that already.
+ *
+ *      Usually a userspace daemon will notify the kernel WDT driver
+ *      via the /dev/watchdog special device file that userspace is
+ *      still alive, at regular intervals.  When such a notification
+ *      occurs, the driver will usually tell the hardware watchdog
+ *      that everything is in order, and that the watchdog should wait
+ *      for yet another little while to reset the system.
+ *      If userspace fails (RAM error, kernel bug, whatever), the
+ *      notifications cease to occur, and the hardware watchdog will
+ *      reset the system (causing a reboot) after the timeout occurs.
+ *
+ * Create device with:
+ *  mknod /dev/watchdog c 10 130
+ *
+ * For an example userspace keep-alive daemon, see:
+ *   Documentation/watchdog/watchdog.txt
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/* enable support for minutes as units? */
+/* (does not always work correctly, so disabled by default!) */
+#define SMSC_SUPPORT_MINUTES
+#undef SMSC_SUPPORT_MINUTES
+
+#define MAX_TIMEOUT     255
+
+#define UNIT_SECOND     0
+#define UNIT_MINUTE     1
+
+#define MODNAME                "smsc37b787_wdt: "
+#define VERSION         "1.1"
+
+#define IOPORT          0x3F0
+#define IOPORT_SIZE     2
+#define IODEV_NO        8
+
+static int unit = UNIT_SECOND;  /* timer's unit */
+static int timeout = 60;        /* timeout value: default is 60 "units" */
+static unsigned long timer_enabled = 0;   /* is the timer enabled? */
+
+static char expect_close;       /* is the close expected? */
+
+static spinlock_t io_lock;     /* to guard the watchdog from io races */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/* -- Low level function ----------------------------------------*/
+
+/* unlock the IO chip */
+
+static inline void open_io_config(void)
+{
+        outb(0x55, IOPORT);
+       mdelay(1);
+        outb(0x55, IOPORT);
+}
+
+/* lock the IO chip */
+static inline void close_io_config(void)
+{
+        outb(0xAA, IOPORT);
+}
+
+/* select the IO device */
+static inline void select_io_device(unsigned char devno)
+{
+        outb(0x07, IOPORT);
+        outb(devno, IOPORT+1);
+}
+
+/* write to the control register */
+static inline void write_io_cr(unsigned char reg, unsigned char data)
+{
+        outb(reg, IOPORT);
+        outb(data, IOPORT+1);
+}
+
+/* read from the control register */
+static inline char read_io_cr(unsigned char reg)
+{
+        outb(reg, IOPORT);
+        return inb(IOPORT+1);
+}
+
+/* -- Medium level functions ------------------------------------*/
+
+static inline void gpio_bit12(unsigned char reg)
+{
+       // -- General Purpose I/O Bit 1.2 --
+       // Bit 0,   In/Out: 0 = Output, 1 = Input
+       // Bit 1,   Polarity: 0 = No Invert, 1 = Invert
+       // Bit 2,   Group Enable Intr.: 0 = Disable, 1 = Enable
+       // Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
+       //                           11 = Either Edge Triggered Intr. 2
+        // Bit 5/6  (Reserved)
+       // Bit 7,   Output Type: 0 = Push Pull Bit, 1 = Open Drain
+        write_io_cr(0xE2, reg);
+}
+
+static inline void gpio_bit13(unsigned char reg)
+{
+       // -- General Purpose I/O Bit 1.3 --
+       // Bit 0,  In/Out: 0 = Output, 1 = Input
+       // Bit 1,  Polarity: 0 = No Invert, 1 = Invert
+       // Bit 2,  Group Enable Intr.: 0 = Disable, 1 = Enable
+       // Bit 3,  Function select: 0 = GPI/O, 1 = LED
+        // Bit 4-6 (Reserved)
+       // Bit 7,  Output Type: 0 = Push Pull Bit, 1 = Open Drain
+        write_io_cr(0xE3, reg);
+}
+
+static inline void wdt_timer_units(unsigned char new_units)
+{
+       // -- Watchdog timer units --
+       // Bit 0-6 (Reserved)
+       // Bit 7,  WDT Time-out Value Units Select
+       //         (0 = Minutes, 1 = Seconds)
+        write_io_cr(0xF1, new_units);
+}
+
+static inline void wdt_timeout_value(unsigned char new_timeout)
+{
+       // -- Watchdog Timer Time-out Value --
+       // Bit 0-7 Binary coded units (0=Disabled, 1..255)
+        write_io_cr(0xF2, new_timeout);
+}
+
+static inline void wdt_timer_conf(unsigned char conf)
+{
+       // -- Watchdog timer configuration --
+       // Bit 0   Joystick enable: 0* = No Reset, 1 = Reset WDT upon Gameport I/O
+       // Bit 1   Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
+       // Bit 2   Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr.
+        // Bit 3   Reset the timer
+        //         (Wrong in SMsC documentation? Given as: PowerLED Timout Enabled)
+       // Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
+       //            0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
+        write_io_cr(0xF3, conf);
+}
+
+static inline void wdt_timer_ctrl(unsigned char reg)
+{
+       // -- Watchdog timer control --
+       // Bit 0   Status Bit: 0 = Timer counting, 1 = Timeout occured
+       // Bit 1   Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
+       // Bit 2   Force Timeout: 1 = Forces WD timeout event (self-cleaning)
+       // Bit 3   P20 Force Timeout enabled:
+       //          0 = P20 activity does not generate the WD timeout event
+       //          1 = P20 Allows rising edge of P20, from the keyboard
+       //              controller, to force the WD timeout event.
+       // Bit 4   (Reserved)
+       // -- Soft power management --
+       // Bit 5   Stop Counter: 1 = Stop software power down counter
+       //            set via register 0xB8, (self-cleaning)
+       //            (Upon read: 0 = Counter running, 1 = Counter stopped)
+       // Bit 6   Restart Counter: 1 = Restart software power down counter
+       //            set via register 0xB8, (self-cleaning)
+       // Bit 7   SPOFF: 1 = Force software power down (self-cleaning)
+
+        write_io_cr(0xF4, reg);
+}
+
+/* -- Higher level functions ------------------------------------*/
+
+/* initialize watchdog */
+
+static void wb_smsc_wdt_initialize(void)
+{
+        unsigned char old;
+
+       spin_lock(&io_lock);
+        open_io_config();
+        select_io_device(IODEV_NO);
+
+       // enable the watchdog
+       gpio_bit13(0x08);  // Select pin 80 = LED not GPIO
+       gpio_bit12(0x0A);  // Set pin 79 = WDT not GPIO/Output/Polarity=Invert
+
+       // disable the timeout
+        wdt_timeout_value(0);
+
+       // reset control register
+        wdt_timer_ctrl(0x00);
+
+       // reset configuration register
+       wdt_timer_conf(0x00);
+
+       // read old (timer units) register
+        old = read_io_cr(0xF1) & 0x7F;
+        if (unit == UNIT_SECOND) old |= 0x80; // set to seconds
+
+       // set the watchdog timer units
+        wdt_timer_units(old);
+
+        close_io_config();
+       spin_unlock(&io_lock);
+}
+
+/* shutdown the watchdog */
+
+static void wb_smsc_wdt_shutdown(void)
+{
+       spin_lock(&io_lock);
+        open_io_config();
+        select_io_device(IODEV_NO);
+
+       // disable the watchdog
+        gpio_bit13(0x09);
+        gpio_bit12(0x09);
+
+       // reset watchdog config register
+       wdt_timer_conf(0x00);
+
+       // reset watchdog control register
+        wdt_timer_ctrl(0x00);
+
+       // disable timeout
+        wdt_timeout_value(0x00);
+
+        close_io_config();
+       spin_unlock(&io_lock);
+}
+
+/* set timeout => enable watchdog */
+
+static void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
+{
+       spin_lock(&io_lock);
+        open_io_config();
+        select_io_device(IODEV_NO);
+
+       // set Power LED to blink, if we enable the timeout
+        wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
+
+       // set timeout value
+        wdt_timeout_value(new_timeout);
+
+        close_io_config();
+       spin_unlock(&io_lock);
+}
+
+/* get timeout */
+
+static unsigned char wb_smsc_wdt_get_timeout(void)
+{
+        unsigned char set_timeout;
+
+       spin_lock(&io_lock);
+        open_io_config();
+        select_io_device(IODEV_NO);
+        set_timeout = read_io_cr(0xF2);
+        close_io_config();
+       spin_unlock(&io_lock);
+
+        return set_timeout;
+}
+
+/* disable watchdog */
+
+static void wb_smsc_wdt_disable(void)
+{
+        // set the timeout to 0 to disable the watchdog
+        wb_smsc_wdt_set_timeout(0);
+}
+
+/* enable watchdog by setting the current timeout */
+
+static void wb_smsc_wdt_enable(void)
+{
+        // set the current timeout...
+        wb_smsc_wdt_set_timeout(timeout);
+}
+
+/* reset the timer */
+
+static void wb_smsc_wdt_reset_timer(void)
+{
+       spin_lock(&io_lock);
+        open_io_config();
+        select_io_device(IODEV_NO);
+
+       // reset the timer
+       wdt_timeout_value(timeout);
+       wdt_timer_conf(0x08);
+
+        close_io_config();
+       spin_unlock(&io_lock);
+}
+
+/* return, if the watchdog is enabled (timeout is set...) */
+
+static int wb_smsc_wdt_status(void)
+{
+       return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
+}
+
+
+/* -- File operations -------------------------------------------*/
+
+/* open => enable watchdog and set initial timeout */
+
+static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
+{
+       /* /dev/watchdog can only be opened once */
+
+       if (test_and_set_bit(0, &timer_enabled))
+               return -EBUSY;
+
+       if (nowayout)
+               __module_get(THIS_MODULE);
+
+       /* Reload and activate timer */
+       wb_smsc_wdt_enable();
+
+       printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
+
+       return nonseekable_open(inode, file);
+}
+
+/* close => shut off the timer */
+
+static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
+{
+       /* Shut off the timer. */
+
+       if (expect_close == 42) {
+               wb_smsc_wdt_disable();
+               printk(KERN_INFO MODNAME "Watchdog disabled, sleeping again...\n");
+       } else {
+               printk(KERN_CRIT MODNAME "Unexpected close, not stopping watchdog!\n");
+               wb_smsc_wdt_reset_timer();
+       }
+
+       clear_bit(0, &timer_enabled);
+       expect_close = 0;
+       return 0;
+}
+
+/* write => update the timer to keep the machine alive */
+
+static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data,
+                                size_t len, loff_t *ppos)
+{
+       /* See if we got the magic character 'V' and reload the timer */
+       if (len) {
+               if (!nowayout) {
+                       size_t i;
+
+                       /* reset expect flag */
+                       expect_close = 0;
+
+                       /* scan to see whether or not we got the magic character */
+                       for (i = 0; i != len; i++) {
+                               char c;
+                               if (get_user(c, data+i))
+                                       return -EFAULT;
+                               if (c == 'V')
+                                       expect_close = 42;
+                       }
+               }
+
+               /* someone wrote to us, we should reload the timer */
+               wb_smsc_wdt_reset_timer();
+       }
+       return len;
+}
+
+/* ioctl => control interface */
+
+static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file,
+                            unsigned int cmd, unsigned long arg)
+{
+       int new_timeout;
+
+       union {
+               struct watchdog_info __user *ident;
+               int __user *i;
+       } uarg;
+
+       static struct watchdog_info ident = {
+               .options =              WDIOF_KEEPALIVEPING |
+                                       WDIOF_SETTIMEOUT |
+                                       WDIOF_MAGICCLOSE,
+               .firmware_version =     0,
+               .identity =             "SMsC 37B787 Watchdog"
+       };
+
+       uarg.i = (int __user *)arg;
+
+       switch (cmd) {
+               default:
+                       return -ENOTTY;
+
+               case WDIOC_GETSUPPORT:
+                       return copy_to_user(uarg.ident, &ident,
+                               sizeof(ident)) ? -EFAULT : 0;
+
+               case WDIOC_GETSTATUS:
+                       return put_user(wb_smsc_wdt_status(), uarg.i);
+
+               case WDIOC_GETBOOTSTATUS:
+                       return put_user(0, uarg.i);
+
+               case WDIOC_KEEPALIVE:
+                       wb_smsc_wdt_reset_timer();
+                       return 0;
+
+               case WDIOC_SETTIMEOUT:
+                       if (get_user(new_timeout, uarg.i))
+                               return -EFAULT;
+
+                       // the API states this is given in secs
+                       if (unit == UNIT_MINUTE)
+                         new_timeout /= 60;
+
+                       if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
+                               return -EINVAL;
+
+                       timeout = new_timeout;
+                       wb_smsc_wdt_set_timeout(timeout);
+
+                       // fall through and return the new timeout...
+
+               case WDIOC_GETTIMEOUT:
+
+                       new_timeout = timeout;
+
+                       if (unit == UNIT_MINUTE)
+                         new_timeout *= 60;
+
+                       return put_user(new_timeout, uarg.i);
+
+               case WDIOC_SETOPTIONS:
+               {
+                       int options, retval = -EINVAL;
+
+                       if (get_user(options, uarg.i))
+                               return -EFAULT;
+
+                       if (options & WDIOS_DISABLECARD) {
+                               wb_smsc_wdt_disable();
+                               retval = 0;
+                       }
+
+                       if (options & WDIOS_ENABLECARD) {
+                               wb_smsc_wdt_enable();
+                               retval = 0;
+                       }
+
+                       return retval;
+               }
+       }
+}
+
+/* -- Notifier funtions -----------------------------------------*/
+
+static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
+{
+       if (code == SYS_DOWN || code == SYS_HALT)
+       {
+                // set timeout to 0, to avoid possible race-condition
+               timeout = 0;
+               wb_smsc_wdt_disable();
+       }
+       return NOTIFY_DONE;
+}
+
+/* -- Module's structures ---------------------------------------*/
+
+static struct file_operations wb_smsc_wdt_fops =
+{
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .write          = wb_smsc_wdt_write,
+       .ioctl          = wb_smsc_wdt_ioctl,
+       .open           = wb_smsc_wdt_open,
+       .release        = wb_smsc_wdt_release,
+};
+
+static struct notifier_block wb_smsc_wdt_notifier =
+{
+       .notifier_call  = wb_smsc_wdt_notify_sys,
+};
+
+static struct miscdevice wb_smsc_wdt_miscdev =
+{
+       .minor          = WATCHDOG_MINOR,
+       .name           = "watchdog",
+       .fops           = &wb_smsc_wdt_fops,
+};
+
+/* -- Module init functions -------------------------------------*/
+
+/* module's "constructor" */
+
+static int __init wb_smsc_wdt_init(void)
+{
+       int ret;
+
+       spin_lock_init(&io_lock);
+
+       printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n");
+
+       if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
+               printk(KERN_ERR MODNAME "Unable to register IO port %#x\n", IOPORT);
+               ret = -EBUSY;
+               goto out_pnp;
+       }
+
+        // set new maximum, if it's too big
+        if (timeout > MAX_TIMEOUT)
+               timeout = MAX_TIMEOUT;
+
+        // init the watchdog timer
+        wb_smsc_wdt_initialize();
+
+       ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
+       if (ret) {
+               printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret);
+               goto out_io;
+       }
+
+       ret = misc_register(&wb_smsc_wdt_miscdev);
+       if (ret) {
+               printk(KERN_ERR MODNAME "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
+               goto out_rbt;
+       }
+
+       // output info
+       printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
+       printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout);
+
+       // ret = 0
+
+out_clean:
+       return ret;
+
+out_rbt:
+       unregister_reboot_notifier(&wb_smsc_wdt_notifier);
+
+out_io:
+       release_region(IOPORT, IOPORT_SIZE);
+
+out_pnp:
+       goto out_clean;
+}
+
+/* module's "destructor" */
+
+static void __exit wb_smsc_wdt_exit(void)
+{
+       /* Stop the timer before we leave */
+       if (!nowayout)
+       {
+               wb_smsc_wdt_shutdown();
+               printk(KERN_INFO MODNAME "Watchdog disabled.\n");
+       }
+
+       misc_deregister(&wb_smsc_wdt_miscdev);
+       unregister_reboot_notifier(&wb_smsc_wdt_notifier);
+       release_region(IOPORT, IOPORT_SIZE);
+
+       printk("SMsC 37B787 watchdog component driver removed.\n");
+}
+
+module_init(wb_smsc_wdt_init);
+module_exit(wb_smsc_wdt_exit);
+
+MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
+MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version " VERSION ")");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+#ifdef SMSC_SUPPORT_MINUTES
+module_param(unit, int, 0);
+MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0");
+#endif
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
index b4adc527e687e8c7e43f047be489b0fbc13f0ddf..07d4bff27226b378a6d88d3c1fb0abe9f9f8172a 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
+#include <linux/spinlock.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -44,6 +45,7 @@
 
 static unsigned long wdt_is_open;
 static char expect_close;
+static spinlock_t io_lock;
 
 /* You must set this - there is no sane way to probe for this board. */
 static int wdt_io = 0x2E;
@@ -110,12 +112,16 @@ w83627hf_init(void)
 static void
 wdt_ctrl(int timeout)
 {
+       spin_lock(&io_lock);
+       
        w83627hf_select_wd_register();
 
        outb_p(0xF6, WDT_EFER);    /* Select CRF6 */
        outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
 
        w83627hf_unselect_wd_register();
+
+       spin_unlock(&io_lock);
 }
 
 static int
@@ -303,6 +309,8 @@ wdt_init(void)
 {
        int ret;
 
+       spin_lock_init(&io_lock);
+
        printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF Super I/O chip initialising.\n");
 
        if (wdt_set_heartbeat(timeout)) {
diff --git a/drivers/char/watchdog/w83697hf_wdt.c b/drivers/char/watchdog/w83697hf_wdt.c
new file mode 100644 (file)
index 0000000..7768b55
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ *     w83697hf/hg WDT driver
+ *
+ *     (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
+ *     (c) Copyright 2006 Marcus Junker <junker@anduras.de>
+ *
+ *     Based on w83627hf_wdt.c which is based on advantechwdt.c
+ *     which is based on wdt.c.
+ *     Original copyright messages:
+ *
+ *     (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
+ *
+ *     (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ *     (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ *                             http://www.redhat.com
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ *     Neither Marcus Junker nor ANDURAS AG admit liability nor provide
+ *     warranty for any of this software. This material is provided
+ *     "AS-IS" and at no charge.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#define WATCHDOG_NAME "w83697hf/hg WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60            /* 60 sec default timeout */
+
+static unsigned long wdt_is_open;
+static char expect_close;
+static spinlock_t io_lock;
+
+/* You must set this - there is no sane way to probe for this board. */
+static int wdt_io = 0x2e;
+module_param(wdt_io, int, 0);
+MODULE_PARM_DESC(wdt_io, "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
+
+/*
+ *     Kernel methods.
+ */
+
+#define W83697HF_EFER (wdt_io+0)       /* Extended Function Enable Register */
+#define W83697HF_EFIR (wdt_io+0)       /* Extended Function Index Register (same as EFER) */
+#define W83697HF_EFDR (wdt_io+1)       /* Extended Function Data Register */
+
+static inline void
+w83697hf_unlock(void)
+{
+       outb_p(0x87, W83697HF_EFER);    /* Enter extended function mode */
+       outb_p(0x87, W83697HF_EFER);    /* Again according to manual */
+}
+
+static inline void
+w83697hf_lock(void)
+{
+       outb_p(0xAA, W83697HF_EFER);    /* Leave extended function mode */
+}
+
+/*
+ *     The three functions w83697hf_get_reg(), w83697hf_set_reg() and
+ *     w83697hf_write_timeout() must be called with the device unlocked.
+ */
+
+static unsigned char
+w83697hf_get_reg(unsigned char reg)
+{
+       outb_p(reg, W83697HF_EFIR);
+       return inb_p(W83697HF_EFDR);
+}
+
+static void
+w83697hf_set_reg(unsigned char reg, unsigned char data)
+{
+       outb_p(reg, W83697HF_EFIR);
+       outb_p(data, W83697HF_EFDR);
+}
+
+static void
+w83697hf_write_timeout(int timeout)
+{
+       w83697hf_set_reg(0xF4, timeout);        /* Write Timeout counter to CRF4 */
+}
+
+static void
+w83697hf_select_wdt(void)
+{
+       w83697hf_unlock();
+       w83697hf_set_reg(0x07, 0x08);   /* Switch to logic device 8 (GPIO2) */
+}
+
+static inline void
+w83697hf_deselect_wdt(void)
+{
+       w83697hf_lock();
+}
+
+static void
+w83697hf_init(void)
+{
+       unsigned char bbuf;
+
+       w83697hf_select_wdt();
+
+       bbuf = w83697hf_get_reg(0x29);
+       bbuf &= ~0x60;
+       bbuf |= 0x20;
+       w83697hf_set_reg(0x29, bbuf);   /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
+
+       bbuf = w83697hf_get_reg(0xF3);
+       bbuf &= ~0x04;
+       w83697hf_set_reg(0xF3, bbuf);   /* Count mode is seconds */
+
+       w83697hf_deselect_wdt();
+}
+
+static int
+wdt_ping(void)
+{
+       spin_lock(&io_lock);
+       w83697hf_select_wdt();
+
+       w83697hf_write_timeout(timeout);
+
+       w83697hf_deselect_wdt();
+       spin_unlock(&io_lock);
+       return 0;
+}
+
+static int
+wdt_enable(void)
+{
+       spin_lock(&io_lock);
+       w83697hf_select_wdt();
+
+       w83697hf_write_timeout(timeout);
+       w83697hf_set_reg(0x30, 1);      /* Enable timer */
+
+       w83697hf_deselect_wdt();
+       spin_unlock(&io_lock);
+       return 0;
+}
+
+static int
+wdt_disable(void)
+{
+       spin_lock(&io_lock);
+       w83697hf_select_wdt();
+
+       w83697hf_set_reg(0x30, 0);      /* Disable timer */
+       w83697hf_write_timeout(0);
+
+       w83697hf_deselect_wdt();
+       spin_unlock(&io_lock);
+       return 0;
+}
+
+static int
+wdt_set_heartbeat(int t)
+{
+       if ((t < 1) || (t > 255))
+               return -EINVAL;
+
+       timeout = t;
+       return 0;
+}
+
+static ssize_t
+wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+       if (count) {
+               if (!nowayout) {
+                       size_t i;
+
+                       expect_close = 0;
+
+                       for (i = 0; i != count; i++) {
+                               char c;
+                               if (get_user(c, buf+i))
+                                       return -EFAULT;
+                               if (c == 'V')
+                                       expect_close = 42;
+                       }
+               }
+               wdt_ping();
+       }
+       return count;
+}
+
+static int
+wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+         unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       int new_timeout;
+       static struct watchdog_info ident = {
+               .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+               .firmware_version = 1,
+               .identity = "W83697HF WDT",
+       };
+
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               if (copy_to_user(argp, &ident, sizeof(ident)))
+                       return -EFAULT;
+               break;
+
+       case WDIOC_GETSTATUS:
+       case WDIOC_GETBOOTSTATUS:
+               return put_user(0, p);
+
+       case WDIOC_KEEPALIVE:
+               wdt_ping();
+               break;
+
+       case WDIOC_SETTIMEOUT:
+               if (get_user(new_timeout, p))
+                       return -EFAULT;
+               if (wdt_set_heartbeat(new_timeout))
+                       return -EINVAL;
+               wdt_ping();
+               /* Fall */
+
+       case WDIOC_GETTIMEOUT:
+               return put_user(timeout, p);
+
+       case WDIOC_SETOPTIONS:
+       {
+               int options, retval = -EINVAL;
+
+               if (get_user(options, p))
+                       return -EFAULT;
+
+               if (options & WDIOS_DISABLECARD) {
+                       wdt_disable();
+                       retval = 0;
+               }
+
+               if (options & WDIOS_ENABLECARD) {
+                       wdt_enable();
+                       retval = 0;
+               }
+
+               return retval;
+       }
+
+       default:
+               return -ENOTTY;
+       }
+       return 0;
+}
+
+static int
+wdt_open(struct inode *inode, struct file *file)
+{
+       if (test_and_set_bit(0, &wdt_is_open))
+               return -EBUSY;
+       /*
+        *      Activate
+        */
+
+       wdt_enable();
+       return nonseekable_open(inode, file);
+}
+
+static int
+wdt_close(struct inode *inode, struct file *file)
+{
+       if (expect_close == 42) {
+               wdt_disable();
+       } else {
+               printk (KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
+               wdt_ping();
+       }
+       expect_close = 0;
+       clear_bit(0, &wdt_is_open);
+       return 0;
+}
+
+/*
+ *     Notifier for system down
+ */
+
+static int
+wdt_notify_sys(struct notifier_block *this, unsigned long code,
+       void *unused)
+{
+       if (code == SYS_DOWN || code == SYS_HALT) {
+               /* Turn the WDT off */
+               wdt_disable();
+       }
+       return NOTIFY_DONE;
+}
+
+/*
+ *     Kernel Interfaces
+ */
+
+static struct file_operations wdt_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .write          = wdt_write,
+       .ioctl          = wdt_ioctl,
+       .open           = wdt_open,
+       .release        = wdt_close,
+};
+
+static struct miscdevice wdt_miscdev = {
+       .minor = WATCHDOG_MINOR,
+       .name = "watchdog",
+       .fops = &wdt_fops,
+};
+
+/*
+ *     The WDT needs to learn about soft shutdowns in order to
+ *     turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+       .notifier_call = wdt_notify_sys,
+};
+
+static int
+w83697hf_check_wdt(void)
+{
+       if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
+               printk (KERN_ERR PFX "I/O address 0x%x already in use\n", wdt_io);
+               return -EIO;
+       }
+
+       printk (KERN_DEBUG PFX "Looking for watchdog at address 0x%x\n", wdt_io);
+       w83697hf_unlock();
+       if (w83697hf_get_reg(0x20) == 0x60) {
+               printk (KERN_INFO PFX "watchdog found at address 0x%x\n", wdt_io);
+               w83697hf_lock();
+               return 0;
+       }
+       w83697hf_lock();        /* Reprotect in case it was a compatible device */
+
+       printk (KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io);
+       release_region(wdt_io, 2);
+       return -EIO;
+}
+
+static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
+
+static int __init
+wdt_init(void)
+{
+       int ret, i, found = 0;
+
+       spin_lock_init(&io_lock);
+
+       printk (KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n");
+
+       if (wdt_io == 0) {
+               /* we will autodetect the W83697HF/HG watchdog */
+               for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
+                       wdt_io = w83697hf_ioports[i];
+                       if (!w83697hf_check_wdt())
+                               found++;
+               }
+       } else {
+               if (!w83697hf_check_wdt())
+                       found++;
+       }
+
+       if (!found) {
+               printk (KERN_ERR PFX "No W83697HF/HG could be found\n");
+               ret = -EIO;
+               goto out;
+       }
+
+       w83697hf_init();
+       wdt_disable();  /* Disable watchdog until first use */
+
+       if (wdt_set_heartbeat(timeout)) {
+               wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+               printk (KERN_INFO PFX "timeout value must be 1<=timeout<=255, using %d\n",
+                       WATCHDOG_TIMEOUT);
+       }
+
+       ret = register_reboot_notifier(&wdt_notifier);
+       if (ret != 0) {
+               printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
+                       ret);
+               goto unreg_regions;
+       }
+
+       ret = misc_register(&wdt_miscdev);
+       if (ret != 0) {
+               printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
+                       WATCHDOG_MINOR, ret);
+               goto unreg_reboot;
+       }
+
+       printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+               timeout, nowayout);
+
+out:
+       return ret;
+unreg_reboot:
+       unregister_reboot_notifier(&wdt_notifier);
+unreg_regions:
+       release_region(wdt_io, 2);
+       goto out;
+}
+
+static void __exit
+wdt_exit(void)
+{
+       misc_deregister(&wdt_miscdev);
+       unregister_reboot_notifier(&wdt_notifier);
+       release_region(wdt_io, 2);
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, Samuel Tardieu <sam@rfc1149.net>");
+MODULE_DESCRIPTION("w83697hf/hg WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);