Merge ../linux-2.6-watchdog-mm
[linux-drm-fsl-dcu.git] / drivers / char / watchdog / iTCO_wdt.c
index cbdfbf00cfb7b0f85c8578f7920ff6abd90dc740..7eac922df8678cecbcde093a53f7da2d3c886257 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
  */
 
 
 /* Module and version information */
 #define DRV_NAME        "iTCO_wdt"
-#define DRV_VERSION     "1.00"
-#define DRV_RELDATE     "18-Jun-2006"
+#define DRV_VERSION     "1.01"
+#define DRV_RELDATE     "11-Nov-2006"
 #define PFX            DRV_NAME ": "
 
 /* Includes */
-#include <linux/config.h>              /* For CONFIG_WATCHDOG_NOWAYOUT/... */
 #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) */
@@ -57,8 +60,6 @@
 #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/notifier.h>            /* For notifier support */
-#include <linux/reboot.h>              /* For reboot_notifier stuff */
 #include <linux/init.h>                        /* For __init/__exit/... */
 #include <linux/fs.h>                  /* For file operations */
 #include <linux/platform_device.h>     /* For platform_driver framework */
@@ -88,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 {
@@ -111,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}
 };
 
@@ -138,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);
@@ -179,6 +189,21 @@ static int nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, int, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
 
+/* 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
+
 /*
  * Some TCO specific functions
  */
@@ -239,6 +264,8 @@ static int iTCO_wdt_start(void)
 
        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()) {
                printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
@@ -263,6 +290,8 @@ static int iTCO_wdt_stop(void)
 
        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;
@@ -283,6 +312,8 @@ 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);
@@ -309,6 +340,8 @@ static int iTCO_wdt_set_heartbeat(int t)
            ((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);
@@ -358,7 +391,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;
 }
 
@@ -429,7 +463,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 = {
@@ -489,6 +522,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;
 
@@ -496,22 +531,8 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file,
                }
 
                default:
-                       return -ENOIOCTLCMD;
-       }
-}
-
-/*
- *     Notify system
- */
-
-static int iTCO_wdt_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
-{
-       if (code==SYS_DOWN || code==SYS_HALT) {
-               /* Turn the WDT off */
-               iTCO_wdt_stop();
+                       return -ENOTTY;
        }
-
-       return NOTIFY_DONE;
 }
 
 /*
@@ -533,10 +554,6 @@ static struct miscdevice iTCO_wdt_miscdev = {
        .fops =         &iTCO_wdt_fops,
 };
 
-static struct notifier_block iTCO_wdt_notifier = {
-       .notifier_call =        iTCO_wdt_notify_sys,
-};
-
 /*
  *     Init & exit routines
  */
@@ -558,6 +575,7 @@ static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent,
        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;
@@ -574,7 +592,7 @@ static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent,
        }
 
        /* Check chipset's NO_REBOOT bit */
-       if (iTCO_wdt_unset_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;
@@ -622,18 +640,11 @@ static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent,
                        heartbeat);
        }
 
-       ret = register_reboot_notifier(&iTCO_wdt_notifier);
-       if (ret != 0) {
-               printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
-                       ret);
-               goto unreg_region;
-       }
-
        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_notifier;
+               goto unreg_region;
        }
 
        printk (KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
@@ -641,13 +652,12 @@ static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent,
 
        return 0;
 
-unreg_notifier:
-       unregister_reboot_notifier(&iTCO_wdt_notifier);
 unreg_region:
        release_region (TCOBASE, 0x20);
 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;
 }
@@ -660,10 +670,11 @@ static void iTCO_wdt_cleanup(void)
 
        /* Deregister */
        misc_deregister(&iTCO_wdt_miscdev);
-       unregister_reboot_notifier(&iTCO_wdt_notifier);
        release_region(TCOBASE, 0x20);
        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 iTCO_wdt_probe(struct platform_device *dev)