WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [linux-2.6.18-xen] Intel TCO Timer/Watchdog driver backp

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [linux-2.6.18-xen] Intel TCO Timer/Watchdog driver backported
From: "Xen patchbot-linux-2.6.18-xen" <patchbot-linux-2.6.18-xen@xxxxxxxxxxxxxxxxxxx>
Date: Tue, 24 Nov 2009 09:30:30 -0800
Delivery-date: Tue, 24 Nov 2009 09:31:33 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1259083544 0
# Node ID 36535669d2ec3d9d2ff7cdc0d775b81497535603
# Parent  6b3493131a97095fde9966edd5615bf7dc1b5b89
Intel TCO Timer/Watchdog driver backported
from Linux Kernel Ver. 2.6.29.2
Signed-off-by: Daniel Kiper <dkiper@xxxxxxxxxxxx>
---
 drivers/char/watchdog/i8xx_tco.c            |  566 ------------------
 drivers/char/watchdog/i8xx_tco.h            |   42 -
 buildconfigs/linux-defconfig_xen_x86_32     |    3 
 buildconfigs/linux-defconfig_xen_x86_64     |    3 
 drivers/char/watchdog/Kconfig               |   27 
 drivers/char/watchdog/Makefile              |    5 
 drivers/char/watchdog/iTCO_vendor.h         |   15 
 drivers/char/watchdog/iTCO_vendor_support.c |  312 ++++++++++
 drivers/char/watchdog/iTCO_wdt.c            |  857 ++++++++++++++++++++++++++++
 9 files changed, 1211 insertions(+), 619 deletions(-)

diff -r 6b3493131a97 -r 36535669d2ec buildconfigs/linux-defconfig_xen_x86_32
--- a/buildconfigs/linux-defconfig_xen_x86_32   Tue Nov 24 17:25:22 2009 +0000
+++ b/buildconfigs/linux-defconfig_xen_x86_32   Tue Nov 24 17:25:44 2009 +0000
@@ -1900,7 +1900,8 @@ CONFIG_IBMASR=m
 CONFIG_IBMASR=m
 CONFIG_WAFER_WDT=m
 CONFIG_I6300ESB_WDT=m
-CONFIG_I8XX_TCO=m
+CONFIG_ITCO_WDT=m
+CONFIG_ITCO_VENDOR_SUPPORT=y
 CONFIG_SC1200_WDT=m
 CONFIG_SCx200_WDT=m
 CONFIG_60XX_WDT=m
diff -r 6b3493131a97 -r 36535669d2ec buildconfigs/linux-defconfig_xen_x86_64
--- a/buildconfigs/linux-defconfig_xen_x86_64   Tue Nov 24 17:25:22 2009 +0000
+++ b/buildconfigs/linux-defconfig_xen_x86_64   Tue Nov 24 17:25:44 2009 +0000
@@ -1778,7 +1778,8 @@ CONFIG_IBMASR=m
 CONFIG_IBMASR=m
 CONFIG_WAFER_WDT=m
 CONFIG_I6300ESB_WDT=m
-CONFIG_I8XX_TCO=m
+CONFIG_ITCO_WDT=m
+CONFIG_ITCO_VENDOR_SUPPORT=y
 CONFIG_SC1200_WDT=m
 CONFIG_60XX_WDT=m
 CONFIG_SBC8360_WDT=m
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/Kconfig
--- a/drivers/char/watchdog/Kconfig     Tue Nov 24 17:25:22 2009 +0000
+++ b/drivers/char/watchdog/Kconfig     Tue Nov 24 17:25:44 2009 +0000
@@ -280,15 +280,18 @@ config I6300ESB_WDT
          To compile this driver as a module, choose M here: the
          module will be called i6300esb.
 
-config I8XX_TCO
-       tristate "Intel i8xx TCO Timer/Watchdog"
+config ITCO_WDT
+       tristate "Intel TCO Timer/Watchdog"
        depends on WATCHDOG && (X86 || IA64) && PCI
        ---help---
-         Hardware driver for the TCO timer built into the Intel 82801
-         I/O Controller Hub family.  The TCO (Total Cost of Ownership)
-         timer is a watchdog timer that will reboot the machine after
-         its second expiration. The expiration time can be configured
-         with the "heartbeat" parameter.
+         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 ICH10) and in the Intel 63xxESB
+         controller hub.
+
+         The TCO (Total Cost of Ownership) timer is a watchdog timer
+         that will reboot the machine after its second expiration. The
+         expiration time can be configured with the "heartbeat" parameter.
 
          On some motherboards the driver may fail to reset the chipset's
          NO_REBOOT flag which prevents the watchdog from rebooting the
@@ -296,7 +299,15 @@ config I8XX_TCO
          "failed to reset NO_REBOOT flag, reboot disabled by hardware".
 
          To compile this driver as a module, choose M here: the
-         module will be called i8xx_tco.
+         module will be called iTCO_wdt.
+
+config ITCO_VENDOR_SUPPORT
+       bool "Intel TCO Timer/Watchdog Specific Vendor Support"
+       depends on ITCO_WDT
+       ---help---
+         Add vendor specific support to the intel TCO timer based watchdog
+         devices. At this moment we only have additional support for some
+         SuperMicro Inc. motherboards.
 
 config SC1200_WDT
        tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/Makefile
--- a/drivers/char/watchdog/Makefile    Tue Nov 24 17:25:22 2009 +0000
+++ b/drivers/char/watchdog/Makefile    Tue Nov 24 17:25:44 2009 +0000
@@ -44,7 +44,10 @@ obj-$(CONFIG_IBMASR) += ibmasr.o
 obj-$(CONFIG_IBMASR) += ibmasr.o
 obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
 obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o
-obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o
+obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o
+ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y)
+obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
+endif
 obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
 obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
 obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/i8xx_tco.c
--- a/drivers/char/watchdog/i8xx_tco.c  Tue Nov 24 17:25:22 2009 +0000
+++ /dev/null   Thu Jan 01 00:00:00 1970 +0000
@@ -1,566 +0,0 @@
-/*
- *     i8xx_tco:       TCO timer driver for i8xx chipsets
- *
- *     (c) Copyright 2000 kernel concepts <nils@xxxxxxxxxxxxxxxxx>, All Rights 
Reserved.
- *                             http://www.kernelconcepts.de
- *
- *     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 kernel concepts nor Nils Faerber admit liability nor provide
- *     warranty for any of this software. This material is provided
- *     "AS-IS" and at no charge.
- *
- *     (c) Copyright 2000      kernel concepts <nils@xxxxxxxxxxxxxxxxx>
- *                             developed for
- *                              Jentro AG, Haar/Munich (Germany)
- *
- *     TCO timer driver for i8xx chipsets
- *     based on softdog.c by Alan Cox <alan@xxxxxxxxxx>
- *
- *     The TCO timer is implemented in the following I/O controller hubs:
- *     (See the intel documentation on http://developer.intel.com.)
- *     82801AA  (ICH)    : document number 290655-003, 290677-014,
- *     82801AB  (ICHO)   : document number 290655-003, 290677-014,
- *     82801BA  (ICH2)   : document number 290687-002, 298242-027,
- *     82801BAM (ICH2-M) : document number 290687-002, 298242-027,
- *     82801CA  (ICH3-S) : document number 290733-003, 290739-013,
- *     82801CAM (ICH3-M) : document number 290716-001, 290718-007,
- *     82801DB  (ICH4)   : document number 290744-001, 290745-020,
- *     82801DBM (ICH4-M) : document number 252337-001, 252663-005,
- *     82801E   (C-ICH)  : document number 273599-001, 273645-002,
- *     82801EB  (ICH5)   : document number 252516-001, 252517-003,
- *     82801ER  (ICH5R)  : document number 252516-001, 252517-003,
- *
- *  20000710 Nils Faerber
- *     Initial Version 0.01
- *  20000728 Nils Faerber
- *     0.02 Fix for SMI_EN->TCO_EN bit, some cleanups
- *  20011214 Matt Domsch <Matt_Domsch@xxxxxxxx>
- *     0.03 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
- *          Didn't add timeout option as i810_margin already exists.
- *  20020224 Joel Becker, Wim Van Sebroeck
- *     0.04 Support for 82801CA(M) chipset, timer margin needs to be > 3,
- *          add support for WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT.
- *  20020412 Rob Radez <rob@xxxxxxxxxxxxxx>, Wim Van Sebroeck
- *     0.05 Fix possible timer_alive race, add expect close support,
- *          clean up ioctls (WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS and
- *          WDIOC_SETOPTIONS), made i810tco_getdevice __init,
- *          removed boot_status, removed tco_timer_read,
- *          added support for 82801DB and 82801E chipset,
- *          added support for 82801EB and 8280ER chipset,
- *          general cleanup.
- *  20030921 Wim Van Sebroeck <wim@xxxxxxxxx>
- *     0.06 change i810_margin to heartbeat, use module_param,
- *          added notify system support, renamed module to i8xx_tco.
- *  20050128 Wim Van Sebroeck <wim@xxxxxxxxx>
- *     0.07 Added support for the ICH4-M, ICH6, ICH6R, ICH6-M, ICH6W and ICH6RW
- *          chipsets. Also added support for the "undocumented" ICH7 chipset.
- *  20050807 Wim Van Sebroeck <wim@xxxxxxxxx>
- *     0.08 Make sure that the watchdog is only "armed" when started.
- *          (Kernel Bug 4251)
- *  20060416 Wim Van Sebroeck <wim@xxxxxxxxx>
- *     0.09 Remove support for the ICH6, ICH6R, ICH6-M, ICH6W and ICH6RW and
- *          ICH7 chipsets. (See Kernel Bug 6031 - other code will support these
- *          chipsets)
- */
-
-/*
- *     Includes, defines, variables, module parameters, ...
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/pci.h>
-#include <linux/ioport.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-#include "i8xx_tco.h"
-
-/* Module and version information */
-#define TCO_VERSION "0.09"
-#define TCO_MODULE_NAME "i8xx TCO timer"
-#define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION
-#define PFX TCO_MODULE_NAME ": "
-
-/* internal variables */
-static unsigned int ACPIBASE;
-static spinlock_t tco_lock;    /* Guards the hardware */
-static unsigned long timer_alive;
-static char tco_expect_close;
-static struct pci_dev *i8xx_tco_pci;
-
-/* module parameters */
-#define WATCHDOG_HEARTBEAT 30  /* 30 sec default heartbeat (2<heartbeat<39) */
-static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
-module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, 
default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
-
-static int nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, int, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started 
(default=CONFIG_WATCHDOG_NOWAYOUT)");
-
-/*
- * Some TCO specific functions
- */
-
-static inline unsigned char seconds_to_ticks(int seconds)
-{
-       /* the internal timer is stored as ticks which decrement
-        * every 0.6 seconds */
-       return (seconds * 10) / 6;
-}
-
-static int tco_timer_start (void)
-{
-       unsigned char val;
-
-       spin_lock(&tco_lock);
-
-       /* disable chipset's NO_REBOOT bit */
-       pci_read_config_byte (i8xx_tco_pci, 0xd4, &val);
-       val &= 0xfd;
-       pci_write_config_byte (i8xx_tco_pci, 0xd4, val);
-
-       /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
-       val = inb (TCO1_CNT + 1);
-       val &= 0xf7;
-       outb (val, TCO1_CNT + 1);
-       val = inb (TCO1_CNT + 1);
-
-       spin_unlock(&tco_lock);
-
-       if (val & 0x08)
-               return -1;
-       return 0;
-}
-
-static int tco_timer_stop (void)
-{
-       unsigned char val, val1;
-
-       spin_lock(&tco_lock);
-       /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
-       val = inb (TCO1_CNT + 1);
-       val |= 0x08;
-       outb (val, TCO1_CNT + 1);
-       val = inb (TCO1_CNT + 1);
-
-       /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
-       pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
-       val1 |= 0x02;
-       pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
-
-       spin_unlock(&tco_lock);
-
-       if ((val & 0x08) == 0)
-               return -1;
-       return 0;
-}
-
-static int tco_timer_keepalive (void)
-{
-       spin_lock(&tco_lock);
-       /* Reload the timer by writing to the TCO Timer Reload register */
-       outb (0x01, TCO1_RLD);
-       spin_unlock(&tco_lock);
-       return 0;
-}
-
-static int tco_timer_set_heartbeat (int t)
-{
-       unsigned char val;
-       unsigned char tmrval;
-
-       tmrval = seconds_to_ticks(t);
-       /* from the specs: */
-       /* "Values of 0h-3h are ignored and should not be attempted" */
-       if (tmrval > 0x3f || tmrval < 0x04)
-               return -EINVAL;
-
-       /* Write new heartbeat to watchdog */
-       spin_lock(&tco_lock);
-       val = inb (TCO1_TMR);
-       val &= 0xc0;
-       val |= tmrval;
-       outb (val, TCO1_TMR);
-       val = inb (TCO1_TMR);
-       spin_unlock(&tco_lock);
-
-       if ((val & 0x3f) != tmrval)
-               return -EINVAL;
-
-       heartbeat = t;
-       return 0;
-}
-
-static int tco_timer_get_timeleft (int *time_left)
-{
-       unsigned char val;
-
-       spin_lock(&tco_lock);
-
-       /* read the TCO Timer */
-       val = inb (TCO1_RLD);
-       val &= 0x3f;
-
-       spin_unlock(&tco_lock);
-
-       *time_left = (int)((val * 6) / 10);
-
-       return 0;
-}
-
-/*
- *     /dev/watchdog handling
- */
-
-static int i8xx_tco_open (struct inode *inode, struct file *file)
-{
-       /* /dev/watchdog can only be opened once */
-       if (test_and_set_bit(0, &timer_alive))
-               return -EBUSY;
-
-       /*
-        *      Reload and activate timer
-        */
-       tco_timer_keepalive ();
-       tco_timer_start ();
-       return nonseekable_open(inode, file);
-}
-
-static int i8xx_tco_release (struct inode *inode, struct file *file)
-{
-       /*
-        *      Shut off the timer.
-        */
-       if (tco_expect_close == 42) {
-               tco_timer_stop ();
-       } else {
-               printk(KERN_CRIT PFX "Unexpected close, not stopping 
watchdog!\n");
-               tco_timer_keepalive ();
-       }
-       clear_bit(0, &timer_alive);
-       tco_expect_close = 0;
-       return 0;
-}
-
-static ssize_t i8xx_tco_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;
-
-                       /* note: just in case someone wrote the magic character
-                        * five months ago... */
-                       tco_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')
-                                       tco_expect_close = 42;
-                       }
-               }
-
-               /* someone wrote to us, we should reload the timer */
-               tco_timer_keepalive ();
-       }
-       return len;
-}
-
-static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
-                         unsigned int cmd, unsigned long arg)
-{
-       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 = {
-               .options =              WDIOF_SETTIMEOUT |
-                                       WDIOF_KEEPALIVEPING |
-                                       WDIOF_MAGICCLOSE,
-               .firmware_version =     0,
-               .identity =             TCO_MODULE_NAME,
-       };
-
-       switch (cmd) {
-               case WDIOC_GETSUPPORT:
-                       return copy_to_user(argp, &ident,
-                               sizeof (ident)) ? -EFAULT : 0;
-
-               case WDIOC_GETSTATUS:
-               case WDIOC_GETBOOTSTATUS:
-                       return put_user (0, p);
-
-               case WDIOC_KEEPALIVE:
-                       tco_timer_keepalive ();
-                       return 0;
-
-               case WDIOC_SETOPTIONS:
-               {
-                       if (get_user (new_options, p))
-                               return -EFAULT;
-
-                       if (new_options & WDIOS_DISABLECARD) {
-                               tco_timer_stop ();
-                               retval = 0;
-                       }
-
-                       if (new_options & WDIOS_ENABLECARD) {
-                               tco_timer_keepalive ();
-                               tco_timer_start ();
-                               retval = 0;
-                       }
-
-                       return retval;
-               }
-
-               case WDIOC_SETTIMEOUT:
-               {
-                       if (get_user(new_heartbeat, p))
-                               return -EFAULT;
-
-                       if (tco_timer_set_heartbeat(new_heartbeat))
-                               return -EINVAL;
-
-                       tco_timer_keepalive ();
-                       /* Fall */
-               }
-
-               case WDIOC_GETTIMEOUT:
-                       return put_user(heartbeat, p);
-
-               case WDIOC_GETTIMELEFT:
-               {
-                       if (tco_timer_get_timeleft(&time_left))
-                               return -EINVAL;
-
-                       return put_user(time_left, p);
-               }
-
-               default:
-                       return -ENOIOCTLCMD;
-       }
-}
-
-/*
- *     Notify system
- */
-
-static int i8xx_tco_notify_sys (struct notifier_block *this, unsigned long 
code, void *unused)
-{
-       if (code==SYS_DOWN || code==SYS_HALT) {
-               /* Turn the WDT off */
-               tco_timer_stop ();
-       }
-
-       return NOTIFY_DONE;
-}
-
-/*
- *     Kernel Interfaces
- */
-
-static const struct file_operations i8xx_tco_fops = {
-       .owner =        THIS_MODULE,
-       .llseek =       no_llseek,
-       .write =        i8xx_tco_write,
-       .ioctl =        i8xx_tco_ioctl,
-       .open =         i8xx_tco_open,
-       .release =      i8xx_tco_release,
-};
-
-static struct miscdevice i8xx_tco_miscdev = {
-       .minor =        WATCHDOG_MINOR,
-       .name =         "watchdog",
-       .fops =         &i8xx_tco_fops,
-};
-
-static struct notifier_block i8xx_tco_notifier = {
-       .notifier_call =        i8xx_tco_notify_sys,
-};
-
-/*
- * Data for PCI driver interface
- *
- * This data only exists for exporting the supported
- * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
- * register a pci_driver, because someone else might one day
- * want to register another driver on the same PCI id.
- */
-static struct pci_device_id i8xx_tco_pci_tbl[] = {
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0,   PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0,   PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,   PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10,  PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,   PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12,  PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,   PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12,  PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0,    PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,   PCI_ANY_ID, 
PCI_ANY_ID, },
-       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,       PCI_ANY_ID, 
PCI_ANY_ID, },
-       { 0, },                 /* End of list */
-};
-MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl);
-
-/*
- *     Init & exit routines
- */
-
-static unsigned char __init i8xx_tco_getdevice (void)
-{
-       struct pci_dev *dev = NULL;
-       u8 val1, val2;
-       u16 badr;
-       /*
-        *      Find the PCI device
-        */
-
-       while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-               if (pci_match_id(i8xx_tco_pci_tbl, dev)) {
-                       i8xx_tco_pci = dev;
-                       break;
-               }
-       }
-
-       if (i8xx_tco_pci) {
-               /*
-                *      Find the ACPI base I/O address which is the base
-                *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
-                *      ACPIBASE is bits [15:7] from 0x40-0x43
-                */
-               pci_read_config_byte (i8xx_tco_pci, 0x40, &val1);
-               pci_read_config_byte (i8xx_tco_pci, 0x41, &val2);
-               badr = ((val2 << 1) | (val1 >> 7)) << 7;
-               ACPIBASE = badr;
-               /* Something's wrong here, ACPIBASE has to be set */
-               if (badr == 0x0001 || badr == 0x0000) {
-                       printk (KERN_ERR PFX "failed to get TCOBASE address\n");
-                       return 0;
-               }
-
-               /* Check chipset's NO_REBOOT bit */
-               pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
-               if (val1 & 0x02) {
-                       val1 &= 0xfd;
-                       pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
-                       pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
-                       if (val1 & 0x02) {
-                               printk (KERN_ERR PFX "failed to reset NO_REBOOT 
flag, reboot disabled by hardware\n");
-                               return 0;       /* Cannot reset NO_REBOOT bit */
-                       }
-               }
-               /* Disable reboots untill the watchdog starts */
-               val1 |= 0x02;
-               pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
-
-               /* Set the TCO_EN bit in SMI_EN register */
-               if (!request_region (SMI_EN + 1, 1, "i8xx TCO")) {
-                       printk (KERN_ERR PFX "I/O address 0x%04x already in 
use\n",
-                               SMI_EN + 1);
-                       return 0;
-               }
-               val1 = inb (SMI_EN + 1);
-               val1 &= 0xdf;
-               outb (val1, SMI_EN + 1);
-               release_region (SMI_EN + 1, 1);
-               return 1;
-       }
-       return 0;
-}
-
-static int __init watchdog_init (void)
-{
-       int ret;
-
-       spin_lock_init(&tco_lock);
-
-       /* Check whether or not the hardware watchdog is there */
-       if (!i8xx_tco_getdevice () || i8xx_tco_pci == NULL)
-               return -ENODEV;
-
-       if (!request_region (TCOBASE, 0x10, "i8xx TCO")) {
-               printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
-                       TCOBASE);
-               ret = -EIO;
-               goto out;
-       }
-
-       /* Clear out the (probably old) status */
-       outb (0, TCO1_STS);
-       outb (3, TCO2_STS);
-
-       /* Check that the heartbeat value is within it's range ; if not reset 
to the default */
-       if (tco_timer_set_heartbeat (heartbeat)) {
-               heartbeat = WATCHDOG_HEARTBEAT;
-               tco_timer_set_heartbeat (heartbeat);
-               printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, 
using %d\n",
-                       heartbeat);
-       }
-
-       ret = register_reboot_notifier(&i8xx_tco_notifier);
-       if (ret != 0) {
-               printk(KERN_ERR PFX "cannot register reboot notifier 
(err=%d)\n",
-                       ret);
-               goto unreg_region;
-       }
-
-       ret = misc_register(&i8xx_tco_miscdev);
-       if (ret != 0) {
-               printk(KERN_ERR PFX "cannot register miscdev on minor=%d 
(err=%d)\n",
-                       WATCHDOG_MINOR, ret);
-               goto unreg_notifier;
-       }
-
-       tco_timer_stop ();
-
-       printk (KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec 
(nowayout=%d)\n",
-               TCOBASE, heartbeat, nowayout);
-
-       return 0;
-
-unreg_notifier:
-       unregister_reboot_notifier(&i8xx_tco_notifier);
-unreg_region:
-       release_region (TCOBASE, 0x10);
-out:
-       return ret;
-}
-
-static void __exit watchdog_cleanup (void)
-{
-       /* Stop the timer before we leave */
-       if (!nowayout)
-               tco_timer_stop ();
-
-       /* Deregister */
-       misc_deregister (&i8xx_tco_miscdev);
-       unregister_reboot_notifier(&i8xx_tco_notifier);
-       release_region (TCOBASE, 0x10);
-}
-
-module_init(watchdog_init);
-module_exit(watchdog_cleanup);
-
-MODULE_AUTHOR("Nils Faerber");
-MODULE_DESCRIPTION("TCO timer driver for i8xx chipsets");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/i8xx_tco.h
--- a/drivers/char/watchdog/i8xx_tco.h  Tue Nov 24 17:25:22 2009 +0000
+++ /dev/null   Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- *     i8xx_tco:       TCO timer driver for i8xx chipsets
- *
- *     (c) Copyright 2000 kernel concepts <nils@xxxxxxxxxxxxxxxxx>, All Rights 
Reserved.
- *                             http://www.kernelconcepts.de
- *
- *     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 kernel concepts nor Nils Faerber admit liability nor provide
- *     warranty for any of this software. This material is provided
- *     "AS-IS" and at no charge.
- *
- *     (c) Copyright 2000      kernel concepts <nils@xxxxxxxxxxxxxxxxx>
- *                             developed for
- *                              Jentro AG, Haar/Munich (Germany)
- *
- *     TCO timer driver for i8xx chipsets
- *     based on softdog.c by Alan Cox <alan@xxxxxxxxxx>
- *
- *     For history and the complete list of supported I/O Controller Hub's
- *     see i8xx_tco.c
- */
-
-
-/*
- * Some address definitions for the TCO
- */
-
-#define        TCOBASE         ACPIBASE + 0x60 /* TCO base address             
*/
-#define TCO1_RLD       TCOBASE + 0x00  /* TCO Timer Reload and Current Value */
-#define TCO1_TMR       TCOBASE + 0x01  /* TCO Timer Initial Value      */
-#define        TCO1_DAT_IN     TCOBASE + 0x02  /* TCO Data In Register         
*/
-#define        TCO1_DAT_OUT    TCOBASE + 0x03  /* TCO Data Out Register        
*/
-#define        TCO1_STS        TCOBASE + 0x04  /* TCO1 Status Register         
*/
-#define        TCO2_STS        TCOBASE + 0x06  /* TCO2 Status Register         
*/
-#define TCO1_CNT       TCOBASE + 0x08  /* TCO1 Control Register        */
-#define TCO2_CNT       TCOBASE + 0x0a  /* TCO2 Control Register        */
-
-#define        SMI_EN          ACPIBASE + 0x30 /* SMI Control and Enable 
Register */
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/iTCO_vendor.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/char/watchdog/iTCO_vendor.h       Tue Nov 24 17:25:44 2009 +0000
@@ -0,0 +1,15 @@
+/* iTCO Vendor Specific Support hooks */
+#ifdef CONFIG_ITCO_VENDOR_SUPPORT
+extern void iTCO_vendor_pre_start(unsigned long, unsigned int);
+extern void iTCO_vendor_pre_stop(unsigned long);
+extern void iTCO_vendor_pre_keepalive(unsigned long, unsigned int);
+extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
+extern int iTCO_vendor_check_noreboot_on(void);
+#else
+#define iTCO_vendor_pre_start(acpibase, heartbeat)     {}
+#define iTCO_vendor_pre_stop(acpibase)                 {}
+#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {}
+#define iTCO_vendor_pre_set_heartbeat(heartbeat)       {}
+#define iTCO_vendor_check_noreboot_on()                        1
+                               /* 1=check noreboot; 0=don't check */
+#endif
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/iTCO_vendor_support.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/char/watchdog/iTCO_vendor_support.c       Tue Nov 24 17:25:44 
2009 +0000
@@ -0,0 +1,312 @@
+/*
+ *     intel TCO vendor specific watchdog driver support
+ *
+ *     (c) Copyright 2006-2009 Wim Van Sebroeck <wim@xxxxxxxxx>.
+ *
+ *     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 Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ */
+
+/*
+ *     Includes, defines, variables, module parameters, ...
+ */
+
+/* Module and version information */
+#define DRV_NAME       "iTCO_vendor_support"
+#define DRV_VERSION    "1.03"
+#define PFX            DRV_NAME ": "
+
+/* Includes */
+#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/init.h>                        /* For __init/__exit/... */
+#include <linux/ioport.h>              /* For io-port access */
+#include <linux/io.h>                  /* For inb/outb/... */
+
+#include "iTCO_vendor.h"
+
+/* iTCO defines */
+#define        SMI_EN          acpibase + 0x30 /* SMI Control and Enable 
Register */
+#define        TCOBASE         acpibase + 0x60 /* TCO base address */
+#define        TCO1_STS        TCOBASE + 0x04  /* TCO1 Status Register */
+
+/* List of vendor support modes */
+/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
+#define SUPERMICRO_OLD_BOARD   1
+/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */
+#define SUPERMICRO_NEW_BOARD   2
+
+static int vendorsupport;
+module_param(vendorsupport, int, 0);
+MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=0 
(none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+");
+
+/*
+ *     Vendor Specific Support
+ */
+
+/*
+ *     Vendor Support: 1
+ *     Board: Super Micro Computer Inc. 370SSE+-OEM1/P3TSSE
+ *     iTCO chipset: ICH2
+ *
+ *     Code contributed by: R. Seretny <lkpatches@xxxxxxxxx>
+ *     Documentation obtained by R. Seretny from SuperMicro Technical Support
+ *
+ *     To enable Watchdog function:
+ *         BIOS setup -> Power -> TCO Logic SMI Enable -> Within5Minutes
+ *         This setting enables SMI to clear the watchdog expired flag.
+ *         If BIOS or CPU fail which may cause SMI hang, then system will
+ *         reboot. When application starts to use watchdog function,
+ *         application has to take over the control from SMI.
+ *
+ *         For P3TSSE, J36 jumper needs to be removed to enable the Watchdog
+ *         function.
+ *
+ *         Note: The system will reboot when Expire Flag is set TWICE.
+ *         So, if the watchdog timer is 20 seconds, then the maximum hang
+ *         time is about 40 seconds, and the minimum hang time is about
+ *         20.6 seconds.
+ */
+
+static void supermicro_old_pre_start(unsigned long acpibase)
+{
+       unsigned long val32;
+
+       /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+       val32 = inl(SMI_EN);
+       val32 &= 0xffffdfff;    /* Turn off SMI clearing watchdog */
+       outl(val32, SMI_EN);    /* Needed to activate watchdog */
+}
+
+static void supermicro_old_pre_stop(unsigned long acpibase)
+{
+       unsigned long val32;
+
+       /* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
+       val32 = inl(SMI_EN);
+       val32 |= 0x00002000;    /* Turn on SMI clearing watchdog */
+       outl(val32, SMI_EN);    /* Needed to deactivate watchdog */
+}
+
+static void supermicro_old_pre_keepalive(unsigned long acpibase)
+{
+       /* Reload TCO Timer (done in iTCO_wdt_keepalive) + */
+       /* Clear "Expire Flag" (Bit 3 of TC01_STS register) */
+       outb(0x08, TCO1_STS);
+}
+
+/*
+ *     Vendor Support: 2
+ *     Board: Super Micro Computer Inc. P4SBx, P4DPx
+ *     iTCO chipset: ICH4
+ *
+ *     Code contributed by: R. Seretny <lkpatches@xxxxxxxxx>
+ *     Documentation obtained by R. Seretny from SuperMicro Technical Support
+ *
+ *     To enable Watchdog function:
+ *      1. BIOS
+ *       For P4SBx:
+ *       BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature
+ *       For P4DPx:
+ *       BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog
+ *      This setting enables or disables Watchdog function. When enabled, the
+ *      default watchdog timer is set to be 5 minutes (about 4m35s). It is
+ *      enough to load and run the OS. The application (service or driver) has
+ *      to take over the control once OS is running up and before watchdog
+ *      expires.
+ *
+ *      2. JUMPER
+ *       For P4SBx: JP39
+ *       For P4DPx: JP37
+ *       This jumper is used for safety.  Closed is enabled. This jumper
+ *       prevents user enables watchdog in BIOS by accident.
+ *
+ *      To enable Watch Dog function, both BIOS and JUMPER must be enabled.
+ *
+ *     The documentation lists motherboards P4SBx and P4DPx series as of
+ *     20-March-2002. However, this code works flawlessly with much newer
+ *     motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82).
+ *
+ *     The original iTCO driver as written does not actually reset the
+ *     watchdog timer on these machines, as a result they reboot after five
+ *     minutes.
+ *
+ *     NOTE: You may leave the Watchdog function disabled in the SuperMicro
+ *     BIOS to avoid a "boot-race"... This driver will enable watchdog
+ *     functionality even if it's disabled in the BIOS once the /dev/watchdog
+ *     file is opened.
+ */
+
+/* I/O Port's */
+#define SM_REGINDEX    0x2e    /* SuperMicro ICH4+ Register Index */
+#define SM_DATAIO      0x2f    /* SuperMicro ICH4+ Register Data I/O */
+
+/* Control Register's */
+#define SM_CTLPAGESW   0x07    /* SuperMicro ICH4+ Control Page Switch */
+#define SM_CTLPAGE     0x08    /* SuperMicro ICH4+ Control Page Num */
+
+#define SM_WATCHENABLE 0x30    /* Watchdog enable: Bit 0: 0=off, 1=on */
+
+#define SM_WATCHPAGE   0x87    /* Watchdog unlock control page */
+
+#define SM_ENDWATCH    0xAA    /* Watchdog lock control page */
+
+#define SM_COUNTMODE   0xf5    /* Watchdog count mode select */
+                               /* (Bit 3: 0 = seconds, 1 = minutes */
+
+#define SM_WATCHTIMER  0xf6    /* 8-bits, Watchdog timer counter (RW) */
+
+#define SM_RESETCONTROL        0xf7    /* Watchdog reset control */
+                               /* Bit 6: timer is reset by kbd interrupt */
+                               /* Bit 7: timer is reset by mouse interrupt */
+
+static void supermicro_new_unlock_watchdog(void)
+{
+       /* Write 0x87 to port 0x2e twice */
+       outb(SM_WATCHPAGE, SM_REGINDEX);
+       outb(SM_WATCHPAGE, SM_REGINDEX);
+       /* Switch to watchdog control page */
+       outb(SM_CTLPAGESW, SM_REGINDEX);
+       outb(SM_CTLPAGE, SM_DATAIO);
+}
+
+static void supermicro_new_lock_watchdog(void)
+{
+       outb(SM_ENDWATCH, SM_REGINDEX);
+}
+
+static void supermicro_new_pre_start(unsigned int heartbeat)
+{
+       unsigned int val;
+
+       supermicro_new_unlock_watchdog();
+
+       /* Watchdog timer setting needs to be in seconds*/
+       outb(SM_COUNTMODE, SM_REGINDEX);
+       val = inb(SM_DATAIO);
+       val &= 0xF7;
+       outb(val, SM_DATAIO);
+
+       /* Write heartbeat interval to WDOG */
+       outb(SM_WATCHTIMER, SM_REGINDEX);
+       outb((heartbeat & 255), SM_DATAIO);
+
+       /* Make sure keyboard/mouse interrupts don't interfere */
+       outb(SM_RESETCONTROL, SM_REGINDEX);
+       val = inb(SM_DATAIO);
+       val &= 0x3f;
+       outb(val, SM_DATAIO);
+
+       /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */
+       outb(SM_WATCHENABLE, SM_REGINDEX);
+       val = inb(SM_DATAIO);
+       val |= 0x01;
+       outb(val, SM_DATAIO);
+
+       supermicro_new_lock_watchdog();
+}
+
+static void supermicro_new_pre_stop(void)
+{
+       unsigned int val;
+
+       supermicro_new_unlock_watchdog();
+
+       /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */
+       outb(SM_WATCHENABLE, SM_REGINDEX);
+       val = inb(SM_DATAIO);
+       val &= 0xFE;
+       outb(val, SM_DATAIO);
+
+       supermicro_new_lock_watchdog();
+}
+
+static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
+{
+       supermicro_new_unlock_watchdog();
+
+       /* reset watchdog timeout to heartveat value */
+       outb(SM_WATCHTIMER, SM_REGINDEX);
+       outb((heartbeat & 255), SM_DATAIO);
+
+       supermicro_new_lock_watchdog();
+}
+
+/*
+ *     Generic Support Functions
+ */
+
+void iTCO_vendor_pre_start(unsigned long acpibase,
+                          unsigned int heartbeat)
+{
+       if (vendorsupport == SUPERMICRO_OLD_BOARD)
+               supermicro_old_pre_start(acpibase);
+       else if (vendorsupport == SUPERMICRO_NEW_BOARD)
+               supermicro_new_pre_start(heartbeat);
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_start);
+
+void iTCO_vendor_pre_stop(unsigned long acpibase)
+{
+       if (vendorsupport == SUPERMICRO_OLD_BOARD)
+               supermicro_old_pre_stop(acpibase);
+       else if (vendorsupport == SUPERMICRO_NEW_BOARD)
+               supermicro_new_pre_stop();
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_stop);
+
+void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat)
+{
+       if (vendorsupport == SUPERMICRO_OLD_BOARD)
+               supermicro_old_pre_keepalive(acpibase);
+       else if (vendorsupport == SUPERMICRO_NEW_BOARD)
+               supermicro_new_pre_set_heartbeat(heartbeat);
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_keepalive);
+
+void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat)
+{
+       if (vendorsupport == SUPERMICRO_NEW_BOARD)
+               supermicro_new_pre_set_heartbeat(heartbeat);
+}
+EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat);
+
+int iTCO_vendor_check_noreboot_on(void)
+{
+       switch (vendorsupport) {
+       case SUPERMICRO_OLD_BOARD:
+               return 0;
+       default:
+               return 1;
+       }
+}
+EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on);
+
+static int __init iTCO_vendor_init_module(void)
+{
+       printk(KERN_INFO PFX "vendor-support=%d\n", vendorsupport);
+       return 0;
+}
+
+static void __exit iTCO_vendor_exit_module(void)
+{
+       printk(KERN_INFO PFX "Module Unloaded\n");
+}
+
+module_init(iTCO_vendor_init_module);
+module_exit(iTCO_vendor_exit_module);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@xxxxxxxxx>, R. Seretny 
<lkpatches@xxxxxxxxx>");
+MODULE_DESCRIPTION("Intel TCO Vendor Specific WatchDog Timer Driver Support");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+
diff -r 6b3493131a97 -r 36535669d2ec drivers/char/watchdog/iTCO_wdt.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/char/watchdog/iTCO_wdt.c  Tue Nov 24 17:25:44 2009 +0000
@@ -0,0 +1,857 @@
+/*
+ *     intel TCO Watchdog Driver (Used in i82801 and i63xxESB chipsets)
+ *
+ *     (c) Copyright 2006-2009 Wim Van Sebroeck <wim@xxxxxxxxx>.
+ *
+ *     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 Wim Van Sebroeck nor Iguana vzw. admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ *     The TCO watchdog is implemented in the following I/O controller hubs:
+ *     (See the intel documentation on http://developer.intel.com.)
+ *     82801AA  (ICH)       : document number 290655-003, 290677-014,
+ *     82801AB  (ICHO)      : document number 290655-003, 290677-014,
+ *     82801BA  (ICH2)      : document number 290687-002, 298242-027,
+ *     82801BAM (ICH2-M)    : document number 290687-002, 298242-027,
+ *     82801CA  (ICH3-S)    : document number 290733-003, 290739-013,
+ *     82801CAM (ICH3-M)    : document number 290716-001, 290718-007,
+ *     82801DB  (ICH4)      : document number 290744-001, 290745-025,
+ *     82801DBM (ICH4-M)    : document number 252337-001, 252663-008,
+ *     82801E   (C-ICH)     : document number 273599-001, 273645-002,
+ *     82801EB  (ICH5)      : document number 252516-001, 252517-028,
+ *     82801ER  (ICH5R)     : document number 252516-001, 252517-028,
+ *     6300ESB  (6300ESB)   : document number 300641-004, 300884-013,
+ *     82801FB  (ICH6)      : document number 301473-002, 301474-026,
+ *     82801FR  (ICH6R)     : document number 301473-002, 301474-026,
+ *     82801FBM (ICH6-M)    : document number 301473-002, 301474-026,
+ *     82801FW  (ICH6W)     : document number 301473-001, 301474-026,
+ *     82801FRW (ICH6RW)    : document number 301473-001, 301474-026,
+ *     631xESB  (631xESB)   : document number 313082-001, 313075-006,
+ *     632xESB  (632xESB)   : document number 313082-001, 313075-006,
+ *     82801GB  (ICH7)      : document number 307013-003, 307014-024,
+ *     82801GR  (ICH7R)     : document number 307013-003, 307014-024,
+ *     82801GDH (ICH7DH)    : document number 307013-003, 307014-024,
+ *     82801GBM (ICH7-M)    : document number 307013-003, 307014-024,
+ *     82801GHM (ICH7-M DH) : document number 307013-003, 307014-024,
+ *     82801GU  (ICH7-U)    : document number 307013-003, 307014-024,
+ *     82801HB  (ICH8)      : document number 313056-003, 313057-017,
+ *     82801HR  (ICH8R)     : document number 313056-003, 313057-017,
+ *     82801HBM (ICH8M)     : document number 313056-003, 313057-017,
+ *     82801HH  (ICH8DH)    : document number 313056-003, 313057-017,
+ *     82801HO  (ICH8DO)    : document number 313056-003, 313057-017,
+ *     82801HEM (ICH8M-E)   : document number 313056-003, 313057-017,
+ *     82801IB  (ICH9)      : document number 316972-004, 316973-012,
+ *     82801IR  (ICH9R)     : document number 316972-004, 316973-012,
+ *     82801IH  (ICH9DH)    : document number 316972-004, 316973-012,
+ *     82801IO  (ICH9DO)    : document number 316972-004, 316973-012,
+ *     82801IBM (ICH9M)     : document number 316972-004, 316973-012,
+ *     82801IEM (ICH9M-E)   : document number 316972-004, 316973-012,
+ *     82801JIB (ICH10)     : document number 319973-002, 319974-002,
+ *     82801JIR (ICH10R)    : document number 319973-002, 319974-002,
+ *     82801JD  (ICH10D)    : document number 319973-002, 319974-002,
+ *     82801JDO (ICH10DO)   : document number 319973-002, 319974-002
+ */
+
+/*
+ *     Includes, defines, variables, module parameters, ...
+ */
+
+/* Module and version information */
+#define DRV_NAME       "iTCO_wdt"
+#define DRV_VERSION    "1.05"
+#define PFX            DRV_NAME ": "
+
+/* Includes */
+#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/miscdevice.h>          /* For MODULE_ALIAS_MISCDEV
+                                                       (WATCHDOG_MINOR) */
+#include <linux/watchdog.h>            /* For the watchdog specific items */
+#include <linux/init.h>                        /* For __init/__exit/... */
+#include <linux/fs.h>                  /* For file operations */
+#include <linux/platform_device.h>     /* For platform_driver framework */
+#include <linux/pci.h>                 /* For pci functions */
+#include <linux/ioport.h>              /* For io-port access */
+#include <linux/spinlock.h>            /* For spin_lock/spin_unlock/... */
+#include <linux/uaccess.h>             /* For copy_to_user/put_user/... */
+#include <linux/io.h>                  /* For inb/outb/... */
+
+#include "iTCO_vendor.h"
+
+/* TCO related info */
+enum iTCO_chipsets {
+       TCO_ICH = 0,    /* ICH */
+       TCO_ICH0,       /* ICH0 */
+       TCO_ICH2,       /* ICH2 */
+       TCO_ICH2M,      /* ICH2-M */
+       TCO_ICH3,       /* ICH3-S */
+       TCO_ICH3M,      /* ICH3-M */
+       TCO_ICH4,       /* ICH4 */
+       TCO_ICH4M,      /* ICH4-M */
+       TCO_CICH,       /* C-ICH */
+       TCO_ICH5,       /* ICH5 & ICH5R */
+       TCO_6300ESB,    /* 6300ESB */
+       TCO_ICH6,       /* ICH6 & ICH6R */
+       TCO_ICH6M,      /* ICH6-M */
+       TCO_ICH6W,      /* ICH6W & ICH6RW */
+       TCO_631XESB,    /* 631xESB/632xESB */
+       TCO_ICH7,       /* ICH7 & ICH7R */
+       TCO_ICH7DH,     /* ICH7DH */
+       TCO_ICH7M,      /* ICH7-M & ICH7-U */
+       TCO_ICH7MDH,    /* ICH7-M DH */
+       TCO_ICH8,       /* ICH8 & ICH8R */
+       TCO_ICH8DH,     /* ICH8DH */
+       TCO_ICH8DO,     /* ICH8DO */
+       TCO_ICH8M,      /* ICH8M */
+       TCO_ICH8ME,     /* ICH8M-E */
+       TCO_ICH9,       /* ICH9 */
+       TCO_ICH9R,      /* ICH9R */
+       TCO_ICH9DH,     /* ICH9DH */
+       TCO_ICH9DO,     /* ICH9DO */
+       TCO_ICH9M,      /* ICH9M */
+       TCO_ICH9ME,     /* ICH9M-E */
+       TCO_ICH10,      /* ICH10 */
+       TCO_ICH10R,     /* ICH10R */
+       TCO_ICH10D,     /* ICH10D */
+       TCO_ICH10DO,    /* ICH10DO */
+};
+
+static struct {
+       char *name;
+       unsigned int iTCO_version;
+} iTCO_chipset_info[] __devinitdata = {
+       {"ICH", 1},
+       {"ICH0", 1},
+       {"ICH2", 1},
+       {"ICH2-M", 1},
+       {"ICH3-S", 1},
+       {"ICH3-M", 1},
+       {"ICH4", 1},
+       {"ICH4-M", 1},
+       {"C-ICH", 1},
+       {"ICH5 or ICH5R", 1},
+       {"6300ESB", 1},
+       {"ICH6 or ICH6R", 2},
+       {"ICH6-M", 2},
+       {"ICH6W or ICH6RW", 2},
+       {"631xESB/632xESB", 2},
+       {"ICH7 or ICH7R", 2},
+       {"ICH7DH", 2},
+       {"ICH7-M or ICH7-U", 2},
+       {"ICH7-M DH", 2},
+       {"ICH8 or ICH8R", 2},
+       {"ICH8DH", 2},
+       {"ICH8DO", 2},
+       {"ICH8M", 2},
+       {"ICH8M-E", 2},
+       {"ICH9", 2},
+       {"ICH9R", 2},
+       {"ICH9DH", 2},
+       {"ICH9DO", 2},
+       {"ICH9M", 2},
+       {"ICH9M-E", 2},
+       {"ICH10", 2},
+       {"ICH10R", 2},
+       {"ICH10D", 2},
+       {"ICH10DO", 2},
+       {NULL, 0}
+};
+
+#define ITCO_PCI_DEVICE(dev, data)     \
+       .vendor = PCI_VENDOR_ID_INTEL,  \
+       .device = dev,                  \
+       .subvendor = PCI_ANY_ID,        \
+       .subdevice = PCI_ANY_ID,        \
+       .class = 0,                     \
+       .class_mask = 0,                \
+       .driver_data = data
+
+/*
+ * This data only exists for exporting the supported PCI ids
+ * via MODULE_DEVICE_TABLE.  We do not actually register a
+ * pci_driver, because the I/O Controller Hub has also other
+ * functions that probably will be registered by other drivers.
+ */
+static struct pci_device_id iTCO_wdt_pci_tbl[] = {
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0,        TCO_ICH)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0,        TCO_ICH0)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0,        TCO_ICH2)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10,       TCO_ICH2M)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0,        TCO_ICH3)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12,       TCO_ICH3M)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0,        TCO_ICH4)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12,       TCO_ICH4M)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0,         TCO_CICH)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0,        TCO_ICH5)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1,            TCO_6300ESB)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0,           TCO_ICH6)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1,           TCO_ICH6M)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2,           TCO_ICH6W)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0,           TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2671,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2672,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2673,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2674,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2675,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2676,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2677,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2678,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x2679,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x267a,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x267b,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x267c,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x267d,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x267e,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(0x267f,                               TCO_631XESB)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0,           TCO_ICH7)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30,          TCO_ICH7DH)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1,           TCO_ICH7M)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31,          TCO_ICH7MDH)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0,           TCO_ICH8)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2,           TCO_ICH8DH)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3,           TCO_ICH8DO)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4,           TCO_ICH8M)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1,           TCO_ICH8ME)},
+       { ITCO_PCI_DEVICE(0x2918,                               TCO_ICH9)},
+       { ITCO_PCI_DEVICE(0x2916,                               TCO_ICH9R)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2,           TCO_ICH9DH)},
+       { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4,           TCO_ICH9DO)},
+       { ITCO_PCI_DEVICE(0x2919,                               TCO_ICH9M)},
+       { ITCO_PCI_DEVICE(0x2917,                               TCO_ICH9ME)},
+       { ITCO_PCI_DEVICE(0x3a18,                               TCO_ICH10)},
+       { ITCO_PCI_DEVICE(0x3a16,                               TCO_ICH10R)},
+       { ITCO_PCI_DEVICE(0x3a1a,                               TCO_ICH10D)},
+       { ITCO_PCI_DEVICE(0x3a14,                               TCO_ICH10DO)},
+       { 0, },                 /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
+
+/* Address definitions for the TCO */
+/* TCO base address */
+#define TCOBASE                iTCO_wdt_private.ACPIBASE + 0x60
+/* SMI Control and Enable Register */
+#define SMI_EN         iTCO_wdt_private.ACPIBASE + 0x30
+
+#define TCO_RLD                TCOBASE + 0x00  /* TCO Timer Reload and Curr. 
Value */
+#define TCOv1_TMR      TCOBASE + 0x01  /* TCOv1 Timer Initial Value    */
+#define TCO_DAT_IN     TCOBASE + 0x02  /* TCO Data In Register         */
+#define TCO_DAT_OUT    TCOBASE + 0x03  /* TCO Data Out Register        */
+#define TCO1_STS       TCOBASE + 0x04  /* TCO1 Status Register         */
+#define TCO2_STS       TCOBASE + 0x06  /* TCO2 Status Register         */
+#define TCO1_CNT       TCOBASE + 0x08  /* TCO1 Control Register        */
+#define TCO2_CNT       TCOBASE + 0x0a  /* TCO2 Control Register        */
+#define TCOv2_TMR      TCOBASE + 0x12  /* TCOv2 Timer Initial Value    */
+
+/* internal variables */
+static unsigned long is_active;
+static char expect_release;
+static struct {                /* this is private data for the iTCO_wdt device 
*/
+       /* TCO version/generation */
+       unsigned int iTCO_version;
+       /* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
+       unsigned long ACPIBASE;
+       /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
+       unsigned long __iomem *gcs;
+       /* the lock for io operations */
+       spinlock_t io_lock;
+       /* the PCI-device */
+       struct pci_dev *pdev;
+} iTCO_wdt_private;
+
+/* the watchdog platform device */
+static struct platform_device *iTCO_wdt_platform_device;
+
+/* module parameters */
+#define WATCHDOG_HEARTBEAT 30  /* 30 sec default heartbeat */
+static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39 
(TCO v1) or 613 (TCO v2), default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+       "Watchdog cannot be stopped once started (default="
+                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Some TCO specific functions
+ */
+
+static inline unsigned int seconds_to_ticks(int seconds)
+{
+       /* the internal timer is stored as ticks which decrement
+        * every 0.6 seconds */
+       return (seconds * 10) / 6;
+}
+
+static void iTCO_wdt_set_NO_REBOOT_bit(void)
+{
+       u32 val32;
+
+       /* Set the NO_REBOOT bit: this disables reboots */
+       if (iTCO_wdt_private.iTCO_version == 2) {
+               val32 = readl(iTCO_wdt_private.gcs);
+               val32 |= 0x00000020;
+               writel(val32, iTCO_wdt_private.gcs);
+       } else if (iTCO_wdt_private.iTCO_version == 1) {
+               pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
+               val32 |= 0x00000002;
+               pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
+       }
+}
+
+static int iTCO_wdt_unset_NO_REBOOT_bit(void)
+{
+       int ret = 0;
+       u32 val32;
+
+       /* Unset the NO_REBOOT bit: this enables reboots */
+       if (iTCO_wdt_private.iTCO_version == 2) {
+               val32 = readl(iTCO_wdt_private.gcs);
+               val32 &= 0xffffffdf;
+               writel(val32, iTCO_wdt_private.gcs);
+
+               val32 = readl(iTCO_wdt_private.gcs);
+               if (val32 & 0x00000020)
+                       ret = -EIO;
+       } else if (iTCO_wdt_private.iTCO_version == 1) {
+               pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
+               val32 &= 0xfffffffd;
+               pci_write_config_dword(iTCO_wdt_private.pdev, 0xd4, val32);
+
+               pci_read_config_dword(iTCO_wdt_private.pdev, 0xd4, &val32);
+               if (val32 & 0x00000002)
+                       ret = -EIO;
+       }
+
+       return ret; /* returns: 0 = OK, -EIO = Error */
+}
+
+static int iTCO_wdt_start(void)
+{
+       unsigned int val;
+
+       spin_lock(&iTCO_wdt_private.io_lock);
+
+       iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat);
+
+       /* disable chipset's NO_REBOOT bit */
+       if (iTCO_wdt_unset_NO_REBOOT_bit()) {
+               spin_unlock(&iTCO_wdt_private.io_lock);
+               printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot 
disabled by hardware\n");
+               return -EIO;
+       }
+
+       /* Force the timer to its reload value by writing to the TCO_RLD
+          register */
+       if (iTCO_wdt_private.iTCO_version == 2)
+               outw(0x01, TCO_RLD);
+       else if (iTCO_wdt_private.iTCO_version == 1)
+               outb(0x01, TCO_RLD);
+
+       /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
+       val = inw(TCO1_CNT);
+       val &= 0xf7ff;
+       outw(val, TCO1_CNT);
+       val = inw(TCO1_CNT);
+       spin_unlock(&iTCO_wdt_private.io_lock);
+
+       if (val & 0x0800)
+               return -1;
+       return 0;
+}
+
+static int iTCO_wdt_stop(void)
+{
+       unsigned int val;
+
+       spin_lock(&iTCO_wdt_private.io_lock);
+
+       iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE);
+
+       /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
+       val = inw(TCO1_CNT);
+       val |= 0x0800;
+       outw(val, TCO1_CNT);
+       val = inw(TCO1_CNT);
+
+       /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+       iTCO_wdt_set_NO_REBOOT_bit();
+
+       spin_unlock(&iTCO_wdt_private.io_lock);
+
+       if ((val & 0x0800) == 0)
+               return -1;
+       return 0;
+}
+
+static int iTCO_wdt_keepalive(void)
+{
+       spin_lock(&iTCO_wdt_private.io_lock);
+
+       iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat);
+
+       /* Reload the timer by writing to the TCO Timer Counter register */
+       if (iTCO_wdt_private.iTCO_version == 2)
+               outw(0x01, TCO_RLD);
+       else if (iTCO_wdt_private.iTCO_version == 1)
+               outb(0x01, TCO_RLD);
+
+       spin_unlock(&iTCO_wdt_private.io_lock);
+       return 0;
+}
+
+static int iTCO_wdt_set_heartbeat(int t)
+{
+       unsigned int val16;
+       unsigned char val8;
+       unsigned int tmrval;
+
+       tmrval = seconds_to_ticks(t);
+       /* from the specs: */
+       /* "Values of 0h-3h are ignored and should not be attempted" */
+       if (tmrval < 0x04)
+               return -EINVAL;
+       if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) ||
+           ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f)))
+               return -EINVAL;
+
+       iTCO_vendor_pre_set_heartbeat(tmrval);
+
+       /* Write new heartbeat to watchdog */
+       if (iTCO_wdt_private.iTCO_version == 2) {
+               spin_lock(&iTCO_wdt_private.io_lock);
+               val16 = inw(TCOv2_TMR);
+               val16 &= 0xfc00;
+               val16 |= tmrval;
+               outw(val16, TCOv2_TMR);
+               val16 = inw(TCOv2_TMR);
+               spin_unlock(&iTCO_wdt_private.io_lock);
+
+               if ((val16 & 0x3ff) != tmrval)
+                       return -EINVAL;
+       } else if (iTCO_wdt_private.iTCO_version == 1) {
+               spin_lock(&iTCO_wdt_private.io_lock);
+               val8 = inb(TCOv1_TMR);
+               val8 &= 0xc0;
+               val8 |= (tmrval & 0xff);
+               outb(val8, TCOv1_TMR);
+               val8 = inb(TCOv1_TMR);
+               spin_unlock(&iTCO_wdt_private.io_lock);
+
+               if ((val8 & 0x3f) != tmrval)
+                       return -EINVAL;
+       }
+
+       heartbeat = t;
+       return 0;
+}
+
+static int iTCO_wdt_get_timeleft(int *time_left)
+{
+       unsigned int val16;
+       unsigned char val8;
+
+       /* read the TCO Timer */
+       if (iTCO_wdt_private.iTCO_version == 2) {
+               spin_lock(&iTCO_wdt_private.io_lock);
+               val16 = inw(TCO_RLD);
+               val16 &= 0x3ff;
+               spin_unlock(&iTCO_wdt_private.io_lock);
+
+               *time_left = (val16 * 6) / 10;
+       } else if (iTCO_wdt_private.iTCO_version == 1) {
+               spin_lock(&iTCO_wdt_private.io_lock);
+               val8 = inb(TCO_RLD);
+               val8 &= 0x3f;
+               spin_unlock(&iTCO_wdt_private.io_lock);
+
+               *time_left = (val8 * 6) / 10;
+       } else
+               return -EINVAL;
+       return 0;
+}
+
+/*
+ *     /dev/watchdog handling
+ */
+
+static int iTCO_wdt_open(struct inode *inode, struct file *file)
+{
+       /* /dev/watchdog can only be opened once */
+       if (test_and_set_bit(0, &is_active))
+               return -EBUSY;
+
+       /*
+        *      Reload and activate timer
+        */
+       iTCO_wdt_start();
+       return nonseekable_open(inode, file);
+}
+
+static int iTCO_wdt_release(struct inode *inode, struct file *file)
+{
+       /*
+        *      Shut off the timer.
+        */
+       if (expect_release == 42) {
+               iTCO_wdt_stop();
+       } else {
+               printk(KERN_CRIT PFX
+                       "Unexpected close, not stopping watchdog!\n");
+               iTCO_wdt_keepalive();
+       }
+       clear_bit(0, &is_active);
+       expect_release = 0;
+       return 0;
+}
+
+static ssize_t iTCO_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;
+
+                       /* note: just in case someone wrote the magic
+                          character five months ago... */
+                       expect_release = 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_release = 42;
+                       }
+               }
+
+               /* someone wrote to us, we should reload the timer */
+               iTCO_wdt_keepalive();
+       }
+       return len;
+}
+
+static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       int new_options, retval = -EINVAL;
+       int new_heartbeat;
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       static struct watchdog_info ident = {
+               .options =              WDIOF_SETTIMEOUT |
+                                       WDIOF_KEEPALIVEPING |
+                                       WDIOF_MAGICCLOSE,
+               .firmware_version =     0,
+               .identity =             DRV_NAME,
+       };
+
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+       case WDIOC_GETSTATUS:
+       case WDIOC_GETBOOTSTATUS:
+               return put_user(0, p);
+
+       case WDIOC_SETOPTIONS:
+       {
+               if (get_user(new_options, p))
+                       return -EFAULT;
+
+               if (new_options & WDIOS_DISABLECARD) {
+                       iTCO_wdt_stop();
+                       retval = 0;
+               }
+               if (new_options & WDIOS_ENABLECARD) {
+                       iTCO_wdt_keepalive();
+                       iTCO_wdt_start();
+                       retval = 0;
+               }
+               return retval;
+       }
+       case WDIOC_KEEPALIVE:
+               iTCO_wdt_keepalive();
+               return 0;
+
+       case WDIOC_SETTIMEOUT:
+       {
+               if (get_user(new_heartbeat, p))
+                       return -EFAULT;
+               if (iTCO_wdt_set_heartbeat(new_heartbeat))
+                       return -EINVAL;
+               iTCO_wdt_keepalive();
+               /* Fall */
+       }
+       case WDIOC_GETTIMEOUT:
+               return put_user(heartbeat, p);
+       case WDIOC_GETTIMELEFT:
+       {
+               int time_left;
+               if (iTCO_wdt_get_timeleft(&time_left))
+                       return -EINVAL;
+               return put_user(time_left, p);
+       }
+       default:
+               return -ENOTTY;
+       }
+}
+
+/*
+ *     Kernel Interfaces
+ */
+
+static const struct file_operations iTCO_wdt_fops = {
+       .owner =                THIS_MODULE,
+       .llseek =               no_llseek,
+       .write =                iTCO_wdt_write,
+       .unlocked_ioctl =       iTCO_wdt_ioctl,
+       .open =                 iTCO_wdt_open,
+       .release =              iTCO_wdt_release,
+};
+
+static struct miscdevice iTCO_wdt_miscdev = {
+       .minor =        WATCHDOG_MINOR,
+       .name =         "watchdog",
+       .fops =         &iTCO_wdt_fops,
+};
+
+/*
+ *     Init & exit routines
+ */
+
+static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
+               const struct pci_device_id *ent, struct platform_device *dev)
+{
+       int ret;
+       u32 base_address;
+       unsigned long RCBA;
+       unsigned long val32;
+
+       /*
+        *      Find the ACPI/PM base I/O address which is the base
+        *      for the TCO registers (TCOBASE=ACPIBASE + 0x60)
+        *      ACPIBASE is bits [15:7] from 0x40-0x43
+        */
+       pci_read_config_dword(pdev, 0x40, &base_address);
+       base_address &= 0x0000ff80;
+       if (base_address == 0x00000000) {
+               /* Something's wrong here, ACPIBASE has to be set */
+               printk(KERN_ERR PFX "failed to get TCOBASE address\n");
+               pci_dev_put(pdev);
+               return -ENODEV;
+       }
+       iTCO_wdt_private.iTCO_version =
+                       iTCO_chipset_info[ent->driver_data].iTCO_version;
+       iTCO_wdt_private.ACPIBASE = base_address;
+       iTCO_wdt_private.pdev = pdev;
+
+       /* Get the Memory-Mapped GCS register, we need it for the
+          NO_REBOOT flag (TCO v2). To get access to it you have to
+          read RCBA from PCI Config space 0xf0 and use it as base.
+          GCS = RCBA + ICH6_GCS(0x3410). */
+       if (iTCO_wdt_private.iTCO_version == 2) {
+               pci_read_config_dword(pdev, 0xf0, &base_address);
+               RCBA = base_address & 0xffffc000;
+               iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4);
+       }
+
+       /* Check chipset's NO_REBOOT bit */
+       if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
+               printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot 
disabled by hardware\n");
+               ret = -ENODEV;  /* Cannot reset NO_REBOOT bit */
+               goto out;
+       }
+
+       /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+       iTCO_wdt_set_NO_REBOOT_bit();
+
+       /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
+       if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
+               printk(KERN_ERR PFX
+                       "I/O address 0x%04lx already in use\n", SMI_EN);
+               ret = -EIO;
+               goto out;
+       }
+       /* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
+       val32 = inl(SMI_EN);
+       val32 &= 0xffffdfff;    /* Turn off SMI clearing watchdog */
+       outl(val32, SMI_EN);
+
+       /* The TCO I/O registers reside in a 32-byte range pointed to
+          by the TCOBASE value */
+       if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
+               printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n",
+                       TCOBASE);
+               ret = -EIO;
+               goto unreg_smi_en;
+       }
+
+       printk(KERN_INFO PFX
+               "Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
+                       iTCO_chipset_info[ent->driver_data].name,
+                       iTCO_chipset_info[ent->driver_data].iTCO_version,
+                       TCOBASE);
+
+       /* Clear out the (probably old) status */
+       outb(8, TCO1_STS);      /* Clear the Time Out Status bit */
+       outb(2, TCO2_STS);      /* Clear SECOND_TO_STS bit */
+       outb(4, TCO2_STS);      /* Clear BOOT_STS bit */
+
+       /* Make sure the watchdog is not running */
+       iTCO_wdt_stop();
+
+       /* Check that the heartbeat value is within it's range;
+          if not reset to the default */
+       if (iTCO_wdt_set_heartbeat(heartbeat)) {
+               iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT);
+               printk(KERN_INFO PFX "heartbeat value must be 2 < heartbeat < 
39 (TCO v1) or 613 (TCO v2), using %d\n",
+                                                       heartbeat);
+       }
+
+       ret = misc_register(&iTCO_wdt_miscdev);
+       if (ret != 0) {
+               printk(KERN_ERR PFX
+                       "cannot register miscdev on minor=%d (err=%d)\n",
+                                                       WATCHDOG_MINOR, ret);
+               goto unreg_region;
+       }
+
+       printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
+                                                       heartbeat, nowayout);
+
+       return 0;
+
+unreg_region:
+       release_region(TCOBASE, 0x20);
+unreg_smi_en:
+       release_region(SMI_EN, 4);
+out:
+       if (iTCO_wdt_private.iTCO_version == 2)
+               iounmap(iTCO_wdt_private.gcs);
+       pci_dev_put(iTCO_wdt_private.pdev);
+       iTCO_wdt_private.ACPIBASE = 0;
+       return ret;
+}
+
+static void __devexit iTCO_wdt_cleanup(void)
+{
+       /* Stop the timer before we leave */
+       if (!nowayout)
+               iTCO_wdt_stop();
+
+       /* Deregister */
+       misc_deregister(&iTCO_wdt_miscdev);
+       release_region(TCOBASE, 0x20);
+       release_region(SMI_EN, 4);
+       if (iTCO_wdt_private.iTCO_version == 2)
+               iounmap(iTCO_wdt_private.gcs);
+       pci_dev_put(iTCO_wdt_private.pdev);
+       iTCO_wdt_private.ACPIBASE = 0;
+}
+
+static int __devinit iTCO_wdt_probe(struct platform_device *dev)
+{
+       int found = 0;
+       struct pci_dev *pdev = NULL;
+       const struct pci_device_id *ent;
+
+       spin_lock_init(&iTCO_wdt_private.io_lock);
+
+       for_each_pci_dev(pdev) {
+               ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
+               if (ent) {
+                       if (!(iTCO_wdt_init(pdev, ent, dev))) {
+                               found++;
+                               break;
+                       }
+               }
+       }
+
+       if (!found) {
+               printk(KERN_INFO PFX "No card detected\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int __devexit iTCO_wdt_remove(struct platform_device *dev)
+{
+       if (iTCO_wdt_private.ACPIBASE)
+               iTCO_wdt_cleanup();
+
+       return 0;
+}
+
+static void iTCO_wdt_shutdown(struct platform_device *dev)
+{
+       iTCO_wdt_stop();
+}
+
+#define iTCO_wdt_suspend NULL
+#define iTCO_wdt_resume  NULL
+
+static struct platform_driver iTCO_wdt_driver = {
+       .probe          = iTCO_wdt_probe,
+       .remove         = __devexit_p(iTCO_wdt_remove),
+       .shutdown       = iTCO_wdt_shutdown,
+       .suspend        = iTCO_wdt_suspend,
+       .resume         = iTCO_wdt_resume,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = DRV_NAME,
+       },
+};
+
+static int __init iTCO_wdt_init_module(void)
+{
+       int err;
+
+       printk(KERN_INFO PFX "Intel TCO WatchDog Timer Driver v%s\n",
+               DRV_VERSION);
+
+       err = platform_driver_register(&iTCO_wdt_driver);
+       if (err)
+               return err;
+
+       iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME,
+                                                               -1, NULL, 0);
+       if (IS_ERR(iTCO_wdt_platform_device)) {
+               err = PTR_ERR(iTCO_wdt_platform_device);
+               goto unreg_platform_driver;
+       }
+
+       return 0;
+
+unreg_platform_driver:
+       platform_driver_unregister(&iTCO_wdt_driver);
+       return err;
+}
+
+static void __exit iTCO_wdt_cleanup_module(void)
+{
+       platform_device_unregister(iTCO_wdt_platform_device);
+       platform_driver_unregister(&iTCO_wdt_driver);
+       printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
+}
+
+module_init(iTCO_wdt_init_module);
+module_exit(iTCO_wdt_cleanup_module);
+
+MODULE_AUTHOR("Wim Van Sebroeck <wim@xxxxxxxxx>");
+MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [linux-2.6.18-xen] Intel TCO Timer/Watchdog driver backported, Xen patchbot-linux-2.6.18-xen <=