memcg: fix behavior under memory.limit equals to memsw.limit
[safe/jmp/linux-2.6] / drivers / watchdog / i6300esb.c
index ca44fd9..7ba0b11 100644 (file)
@@ -9,18 +9,18 @@
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  *
- *      based on i810-tco.c which is in turn based on softdog.c
+ *     based on i810-tco.c which is in turn based on softdog.c
  *
- *     The timer is implemented in the following I/O controller hubs:
- *     (See the intel documentation on http://developer.intel.com.)
- *     6300ESB chip : document number 300641-003
+ *     The timer is implemented in the following I/O controller hubs:
+ *     (See the intel documentation on http://developer.intel.com.)
+ *     6300ESB chip : document number 300641-004
  *
  *  2004YYZZ Ross Biro
  *     Initial version 0.01
  *  2004YYZZ Ross Biro
- *     Version 0.02
+ *     Version 0.02
  *  20050210 David Härdeman <david@2gen.com>
- *      Ported driver to kernel 2.6
+ *     Ported driver to kernel 2.6
  */
 
 /*
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
-#include <linux/reboot.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
 
 /* Module and version information */
-#define ESB_VERSION "0.03"
+#define ESB_VERSION "0.04"
 #define ESB_MODULE_NAME "i6300ESB timer"
 #define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
 #define PFX ESB_MODULE_NAME ": "
 #define ESB_LOCK_REG    0x68            /* WDT lock register                 */
 
 /* Memory mapped registers */
-#define ESB_TIMER1_REG  BASEADDR + 0x00 /* Timer1 value after each reset     */
-#define ESB_TIMER2_REG  BASEADDR + 0x04 /* Timer2 value after each reset     */
-#define ESB_GINTSR_REG  BASEADDR + 0x08 /* General Interrupt Status Register */
-#define ESB_RELOAD_REG  BASEADDR + 0x0c /* Reload register                   */
+#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset     */
+#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset     */
+#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */
+#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register                   */
 
 /* Lock register bits */
-#define ESB_WDT_FUNC    ( 0x01 << 2 )   /* Watchdog functionality            */
-#define ESB_WDT_ENABLE  ( 0x01 << 1 )   /* Enable WDT                        */
-#define ESB_WDT_LOCK    ( 0x01 << 0 )   /* Lock (nowayout)                   */
+#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
+#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
+#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
 
 /* Config register bits */
-#define ESB_WDT_REBOOT  ( 0x01 << 5 )   /* Enable reboot on timeout          */
-#define ESB_WDT_FREQ    ( 0x01 << 2 )   /* Decrement frequency               */
-#define ESB_WDT_INTTYPE ( 0x11 << 0 )   /* Interrupt type on timer1 timeout  */
+#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
+#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
+#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
 
 /* Reload register bits */
-#define ESB_WDT_RELOAD ( 0x01 << 8 )    /* prevent timeout                   */
+#define ESB_WDT_TIMEOUT (0x01 << 9)    /* Watchdog timed out                */
+#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
 
 /* Magic constants */
 #define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
@@ -82,16 +82,22 @@ static unsigned long timer_alive;
 static struct pci_dev *esb_pci;
 static unsigned short triggered; /* The status of the watchdog upon boot */
 static char esb_expect_close;
+static struct platform_device *esb_platform_device;
 
 /* module parameters */
-#define WATCHDOG_HEARTBEAT 30   /* 30 sec default heartbeat (1<heartbeat<2*1023) */
+/* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
+#define WATCHDOG_HEARTBEAT 30
 static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
 module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<heartbeat<2046, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+MODULE_PARM_DESC(heartbeat,
+               "Watchdog heartbeat in seconds. (1<heartbeat<2046, 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) ")");
+MODULE_PARM_DESC(nowayout,
+               "Watchdog cannot be stopped once started (default="
+                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
 /*
  * Some i6300ESB specific functions
@@ -103,19 +109,24 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" _
  * reload register. After this the appropriate registers can be written
  * to once before they need to be unlocked again.
  */
-static inline void esb_unlock_registers(void) {
-        writeb(ESB_UNLOCK1, ESB_RELOAD_REG);
-        writeb(ESB_UNLOCK2, ESB_RELOAD_REG);
+static inline void esb_unlock_registers(void)
+{
+       writeb(ESB_UNLOCK1, ESB_RELOAD_REG);
+       writeb(ESB_UNLOCK2, ESB_RELOAD_REG);
 }
 
-static void esb_timer_start(void)
+static int esb_timer_start(void)
 {
        u8 val;
 
+       spin_lock(&esb_lock);
+       esb_unlock_registers();
+       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
        /* Enable or Enable + Lock? */
-       val = 0x02 | (nowayout ? 0x01 : 0x00);
-
-        pci_write_config_byte(esb_pci, ESB_LOCK_REG, val);
+       val = ESB_WDT_ENABLE | (nowayout ? ESB_WDT_LOCK : 0x00);
+       pci_write_config_byte(esb_pci, ESB_LOCK_REG, val);
+       spin_unlock(&esb_lock);
+       return 0;
 }
 
 static int esb_timer_stop(void)
@@ -132,7 +143,7 @@ static int esb_timer_stop(void)
        spin_unlock(&esb_lock);
 
        /* Returns 0 if the timer was disabled, non-zero otherwise */
-       return (val & 0x01);
+       return val & ESB_WDT_ENABLE;
 }
 
 static void esb_timer_keepalive(void)
@@ -140,7 +151,7 @@ static void esb_timer_keepalive(void)
        spin_lock(&esb_lock);
        esb_unlock_registers();
        writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
-        /* FIXME: Do we need to flush anything here? */
+       /* FIXME: Do we need to flush anything here? */
        spin_unlock(&esb_lock);
 }
 
@@ -165,9 +176,9 @@ static int esb_timer_set_heartbeat(int time)
 
        /* Write timer 2 */
        esb_unlock_registers();
-        writel(val, ESB_TIMER2_REG);
+       writel(val, ESB_TIMER2_REG);
 
-        /* Reload */
+       /* Reload */
        esb_unlock_registers();
        writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
 
@@ -179,54 +190,42 @@ static int esb_timer_set_heartbeat(int time)
        return 0;
 }
 
-static int esb_timer_read (void)
-{
-               u32 count;
-
-       /* This isn't documented, and doesn't take into
-         * acount which stage is running, but it looks
-         * like a 20 bit count down, so we might as well report it.
-         */
-        pci_read_config_dword(esb_pci, 0x64, &count);
-        return (int)count;
-}
-
 /*
- *     /dev/watchdog handling
+ *     /dev/watchdog handling
  */
 
-static int esb_open (struct inode *inode, struct file *file)
+static int esb_open(struct inode *inode, struct file *file)
 {
-        /* /dev/watchdog can only be opened once */
-        if (test_and_set_bit(0, &timer_alive))
-                return -EBUSY;
+       /* /dev/watchdog can only be opened once */
+       if (test_and_set_bit(0, &timer_alive))
+               return -EBUSY;
 
-        /* Reload and activate timer */
-        esb_timer_keepalive ();
-        esb_timer_start ();
+       /* Reload and activate timer */
+       esb_timer_start();
 
        return nonseekable_open(inode, file);
 }
 
-static int esb_release (struct inode *inode, struct file *file)
+static int esb_release(struct inode *inode, struct file *file)
 {
-        /* Shut off the timer. */
-        if (esb_expect_close == 42) {
-                esb_timer_stop ();
-        } else {
-                printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
-                esb_timer_keepalive ();
-        }
-        clear_bit(0, &timer_alive);
-        esb_expect_close = 0;
-        return 0;
+       /* Shut off the timer. */
+       if (esb_expect_close == 42)
+               esb_timer_stop();
+       else {
+               printk(KERN_CRIT PFX
+                               "Unexpected close, not stopping watchdog!\n");
+               esb_timer_keepalive();
+       }
+       clear_bit(0, &timer_alive);
+       esb_expect_close = 0;
+       return 0;
 }
 
-static ssize_t esb_write (struct file *file, const char __user *data,
-                         size_t len, loff_t * ppos)
+static ssize_t esb_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 (len) {
                if (!nowayout) {
                        size_t i;
 
@@ -234,10 +233,11 @@ static ssize_t esb_write (struct file *file, const char __user *data,
                         * five months ago... */
                        esb_expect_close = 0;
 
-                       /* scan to see whether or not we got the magic character */
+                       /* 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))
+                               if (get_user(c, data + i))
                                        return -EFAULT;
                                if (c == 'V')
                                        esb_expect_close = 42;
@@ -245,92 +245,70 @@ static ssize_t esb_write (struct file *file, const char __user *data,
                }
 
                /* someone wrote to us, we should reload the timer */
-               esb_timer_keepalive ();
+               esb_timer_keepalive();
        }
        return len;
 }
 
-static int esb_ioctl (struct inode *inode, struct file *file,
-                     unsigned int cmd, unsigned long arg)
+static long esb_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 |
+               .options =              WDIOF_SETTIMEOUT |
                                        WDIOF_KEEPALIVEPING |
                                        WDIOF_MAGICCLOSE,
-               .firmware_version =     0,
-               .identity =             ESB_MODULE_NAME,
+               .firmware_version =     0,
+               .identity =             ESB_MODULE_NAME,
        };
 
        switch (cmd) {
-               case WDIOC_GETSUPPORT:
-                       return copy_to_user(argp, &ident,
-                                           sizeof (ident)) ? -EFAULT : 0;
-
-               case WDIOC_GETSTATUS:
-                       return put_user (esb_timer_read(), p);
-
-               case WDIOC_GETBOOTSTATUS:
-                       return put_user (triggered, p);
-
-                case WDIOC_KEEPALIVE:
-                        esb_timer_keepalive ();
-                        return 0;
-
-                case WDIOC_SETOPTIONS:
-                {
-                        if (get_user (new_options, p))
-                                return -EFAULT;
+       case WDIOC_GETSUPPORT:
+               return copy_to_user(argp, &ident,
+                                       sizeof(ident)) ? -EFAULT : 0;
 
-                        if (new_options & WDIOS_DISABLECARD) {
-                                esb_timer_stop ();
-                                retval = 0;
-                        }
+       case WDIOC_GETSTATUS:
+               return put_user(0, p);
 
-                        if (new_options & WDIOS_ENABLECARD) {
-                                esb_timer_keepalive ();
-                                esb_timer_start ();
-                                retval = 0;
-                        }
+       case WDIOC_GETBOOTSTATUS:
+               return put_user(triggered, p);
 
-                        return retval;
-                }
+       case WDIOC_SETOPTIONS:
+       {
+               if (get_user(new_options, p))
+                       return -EFAULT;
 
-                case WDIOC_SETTIMEOUT:
-                {
-                        if (get_user(new_heartbeat, p))
-                                return -EFAULT;
-
-                        if (esb_timer_set_heartbeat(new_heartbeat))
-                            return -EINVAL;
-
-                        esb_timer_keepalive ();
-                        /* Fall */
-                }
-
-                case WDIOC_GETTIMEOUT:
-                        return put_user(heartbeat, p);
-
-                default:
-                        return -ENOTTY;
-        }
-}
-
-/*
- *      Notify system
- */
-
-static int esb_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
-{
-        if (code==SYS_DOWN || code==SYS_HALT) {
-                /* Turn the WDT off */
-                esb_timer_stop ();
-        }
+               if (new_options & WDIOS_DISABLECARD) {
+                       esb_timer_stop();
+                       retval = 0;
+               }
 
-        return NOTIFY_DONE;
+               if (new_options & WDIOS_ENABLECARD) {
+                       esb_timer_start();
+                       retval = 0;
+               }
+               return retval;
+       }
+       case WDIOC_KEEPALIVE:
+               esb_timer_keepalive();
+               return 0;
+
+       case WDIOC_SETTIMEOUT:
+       {
+               if (get_user(new_heartbeat, p))
+                       return -EFAULT;
+               if (esb_timer_set_heartbeat(new_heartbeat))
+                       return -EINVAL;
+               esb_timer_keepalive();
+               /* Fall */
+       }
+       case WDIOC_GETTIMEOUT:
+               return put_user(heartbeat, p);
+       default:
+               return -ENOTTY;
+       }
 }
 
 /*
@@ -338,22 +316,18 @@ static int esb_notify_sys (struct notifier_block *this, unsigned long code, void
  */
 
 static const struct file_operations esb_fops = {
-        .owner =        THIS_MODULE,
-        .llseek =       no_llseek,
-        .write =        esb_write,
-        .ioctl =        esb_ioctl,
-        .open =         esb_open,
-        .release =      esb_release,
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .write = esb_write,
+       .unlocked_ioctl = esb_ioctl,
+       .open = esb_open,
+       .release = esb_release,
 };
 
 static struct miscdevice esb_miscdev = {
-        .minor =        WATCHDOG_MINOR,
-        .name =         "watchdog",
-        .fops =         &esb_fops,
-};
-
-static struct notifier_block esb_notifier = {
-        .notifier_call =        esb_notify_sys,
+       .minor = WATCHDOG_MINOR,
+       .name = "watchdog",
+       .fops = &esb_fops,
 };
 
 /*
@@ -365,155 +339,198 @@ static struct notifier_block esb_notifier = {
  * want to register another driver on the same PCI id.
  */
 static struct pci_device_id esb_pci_tbl[] = {
-        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), },
-        { 0, },                 /* End of list */
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), },
+       { 0, },                 /* End of list */
 };
-MODULE_DEVICE_TABLE (pci, esb_pci_tbl);
+MODULE_DEVICE_TABLE(pci, esb_pci_tbl);
 
 /*
  *      Init & exit routines
  */
 
-static unsigned char __init esb_getdevice (void)
+static unsigned char __devinit esb_getdevice(void)
 {
-       u8 val1;
-       unsigned short val2;
-
-        struct pci_dev *dev = NULL;
-        /*
-         *      Find the PCI device
-         */
-
-        for_each_pci_dev(dev) {
-                if (pci_match_id(esb_pci_tbl, dev)) {
-                        esb_pci = dev;
-                        break;
-                }
+       /*
+        *      Find the PCI device
+        */
+
+       esb_pci = pci_get_device(PCI_VENDOR_ID_INTEL,
+                                       PCI_DEVICE_ID_INTEL_ESB_9, NULL);
+
+       if (!esb_pci)
+               return 0;
+
+       if (pci_enable_device(esb_pci)) {
+               printk(KERN_ERR PFX "failed to enable device\n");
+               goto err_devput;
        }
 
-        if (esb_pci) {
-               if (pci_enable_device(esb_pci)) {
-                       printk (KERN_ERR PFX "failed to enable device\n");
-                       goto err_devput;
-               }
+       if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) {
+               printk(KERN_ERR PFX "failed to request region\n");
+               goto err_disable;
+       }
 
-               if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) {
-                       printk (KERN_ERR PFX "failed to request region\n");
-                       goto err_disable;
-               }
+       BASEADDR = pci_ioremap_bar(esb_pci, 0);
+       if (BASEADDR == NULL) {
+               /* Something's wrong here, BASEADDR has to be set */
+               printk(KERN_ERR PFX "failed to get BASEADDR\n");
+               goto err_release;
+       }
 
-               BASEADDR = ioremap(pci_resource_start(esb_pci, 0),
-                                  pci_resource_len(esb_pci, 0));
-               if (BASEADDR == NULL) {
-                       /* Something's wrong here, BASEADDR has to be set */
-                       printk (KERN_ERR PFX "failed to get BASEADDR\n");
-                        goto err_release;
-                }
-
-               /*
-                * The watchdog has two timers, it can be setup so that the
-                * expiry of timer1 results in an interrupt and the expiry of
-                * timer2 results in a reboot. We set it to not generate
-                * any interrupts as there is not much we can do with it
-                * right now.
-                *
-                * We also enable reboots and set the timer frequency to
-                * the PCI clock divided by 2^15 (approx 1KHz).
-                */
-               pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003);
-
-               /* Check that the WDT isn't already locked */
-               pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
-               if (val1 & ESB_WDT_LOCK)
-                       printk (KERN_WARNING PFX "nowayout already set\n");
-
-               /* Set the timer to watchdog mode and disable it for now */
-               pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
-
-               /* Check if the watchdog was previously triggered */
-               esb_unlock_registers();
-               val2 = readw(ESB_RELOAD_REG);
-               triggered = (val2 & (0x01 << 9) >> 9);
-
-               /* Reset trigger flag and timers */
-               esb_unlock_registers();
-               writew((0x11 << 8), ESB_RELOAD_REG);
-
-               /* Done */
-               return 1;
+       /* Done */
+       return 1;
 
 err_release:
-               pci_release_region(esb_pci, 0);
+       pci_release_region(esb_pci, 0);
 err_disable:
-               pci_disable_device(esb_pci);
+       pci_disable_device(esb_pci);
 err_devput:
-               pci_dev_put(esb_pci);
-       }
+       pci_dev_put(esb_pci);
        return 0;
 }
 
-static int __init watchdog_init (void)
+static void __devinit esb_initdevice(void)
 {
-        int ret;
-
-        /* Check whether or not the hardware watchdog is there */
-        if (!esb_getdevice () || esb_pci == NULL)
-                return -ENODEV;
-
-        /* Check that the heartbeat value is within it's range ; if not reset to the default */
-        if (esb_timer_set_heartbeat (heartbeat)) {
-                esb_timer_set_heartbeat (WATCHDOG_HEARTBEAT);
-                printk(KERN_INFO PFX "heartbeat value must be 1<heartbeat<2046, using %d\n",
-                      heartbeat);
-        }
-
-        ret = register_reboot_notifier(&esb_notifier);
-        if (ret != 0) {
-                printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
-                        ret);
-                goto err_unmap;
-        }
-
-        ret = misc_register(&esb_miscdev);
-        if (ret != 0) {
-                printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
-                        WATCHDOG_MINOR, ret);
-                goto err_notifier;
-        }
-
-        esb_timer_stop ();
-
-        printk (KERN_INFO PFX "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
-                BASEADDR, heartbeat, nowayout);
-
-        return 0;
-
-err_notifier:
-        unregister_reboot_notifier(&esb_notifier);
+       u8 val1;
+       u16 val2;
+
+       /*
+        * Config register:
+        * Bit    5 : 0 = Enable WDT_OUTPUT
+        * Bit    2 : 0 = set the timer frequency to the PCI clock
+        * divided by 2^15 (approx 1KHz).
+        * Bits 1:0 : 11 = WDT_INT_TYPE Disabled.
+        * The watchdog has two timers, it can be setup so that the
+        * expiry of timer1 results in an interrupt and the expiry of
+        * timer2 results in a reboot. We set it to not generate
+        * any interrupts as there is not much we can do with it
+        * right now.
+        */
+       pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003);
+
+       /* Check that the WDT isn't already locked */
+       pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
+       if (val1 & ESB_WDT_LOCK)
+               printk(KERN_WARNING PFX "nowayout already set\n");
+
+       /* Set the timer to watchdog mode and disable it for now */
+       pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
+
+       /* Check if the watchdog was previously triggered */
+       esb_unlock_registers();
+       val2 = readw(ESB_RELOAD_REG);
+       if (val2 & ESB_WDT_TIMEOUT)
+               triggered = WDIOF_CARDRESET;
+
+       /* Reset WDT_TIMEOUT flag and timers */
+       esb_unlock_registers();
+       writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG);
+
+       /* And set the correct timeout value */
+       esb_timer_set_heartbeat(heartbeat);
+}
+
+static int __devinit esb_probe(struct platform_device *dev)
+{
+       int ret;
+
+       /* Check whether or not the hardware watchdog is there */
+       if (!esb_getdevice() || esb_pci == NULL)
+               return -ENODEV;
+
+       /* Check that the heartbeat value is within it's range;
+          if not reset to the default */
+       if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) {
+               heartbeat = WATCHDOG_HEARTBEAT;
+               printk(KERN_INFO PFX
+                       "heartbeat value must be 1<heartbeat<2046, using %d\n",
+                                                               heartbeat);
+       }
+
+       /* Initialize the watchdog and make sure it does not run */
+       esb_initdevice();
+
+       /* Register the watchdog so that userspace has access to it */
+       ret = misc_register(&esb_miscdev);
+       if (ret != 0) {
+               printk(KERN_ERR PFX
+                       "cannot register miscdev on minor=%d (err=%d)\n",
+                                                       WATCHDOG_MINOR, ret);
+               goto err_unmap;
+       }
+       printk(KERN_INFO PFX
+               "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
+                                               BASEADDR, heartbeat, nowayout);
+       return 0;
+
 err_unmap:
        iounmap(BASEADDR);
-/* err_release: */
        pci_release_region(esb_pci, 0);
-/* err_disable: */
        pci_disable_device(esb_pci);
-/* err_devput: */
        pci_dev_put(esb_pci);
-        return ret;
+       return ret;
 }
 
-static void __exit watchdog_cleanup (void)
+static int __devexit esb_remove(struct platform_device *dev)
 {
        /* Stop the timer before we leave */
        if (!nowayout)
-               esb_timer_stop ();
+               esb_timer_stop();
 
        /* Deregister */
        misc_deregister(&esb_miscdev);
-        unregister_reboot_notifier(&esb_notifier);
        iounmap(BASEADDR);
        pci_release_region(esb_pci, 0);
        pci_disable_device(esb_pci);
        pci_dev_put(esb_pci);
+       return 0;
+}
+
+static void esb_shutdown(struct platform_device *dev)
+{
+       esb_timer_stop();
+}
+
+static struct platform_driver esb_platform_driver = {
+       .probe          = esb_probe,
+       .remove         = __devexit_p(esb_remove),
+       .shutdown       = esb_shutdown,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = ESB_MODULE_NAME,
+       },
+};
+
+static int __init watchdog_init(void)
+{
+       int err;
+
+       printk(KERN_INFO PFX "Intel 6300ESB WatchDog Timer Driver v%s\n",
+               ESB_VERSION);
+
+       err = platform_driver_register(&esb_platform_driver);
+       if (err)
+               return err;
+
+       esb_platform_device = platform_device_register_simple(ESB_MODULE_NAME,
+                                                               -1, NULL, 0);
+       if (IS_ERR(esb_platform_device)) {
+               err = PTR_ERR(esb_platform_device);
+               goto unreg_platform_driver;
+       }
+
+       return 0;
+
+unreg_platform_driver:
+       platform_driver_unregister(&esb_platform_driver);
+       return err;
+}
+
+static void __exit watchdog_cleanup(void)
+{
+       platform_device_unregister(esb_platform_device);
+       platform_driver_unregister(&esb_platform_driver);
+       printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
 }
 
 module_init(watchdog_init);