]> Pileus Git - ~andy/linux/blobdiff - drivers/watchdog/it87_wdt.c
Merge branch 'for-linus' of git://git.open-osd.org/linux-open-osd
[~andy/linux] / drivers / watchdog / it87_wdt.c
index b1bc72f9a20940f1be3d341f29b5fc9567fcede6..a2d9a1266a23b30e645e4e682bfbabb0b86e2d50 100644 (file)
 
 static unsigned int base, gpact, ciract, max_units, chip_type;
 static unsigned long wdt_status;
-static DEFINE_SPINLOCK(spinlock);
 
 static int nogameport = DEFAULT_NOGAMEPORT;
 static int exclusive  = DEFAULT_EXCLUSIVE;
@@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
 
 /* Superio Chip */
 
-static inline void superio_enter(void)
+static inline int superio_enter(void)
 {
+       /*
+        * Try to reserve REG and REG + 1 for exclusive access.
+        */
+       if (!request_muxed_region(REG, 2, WATCHDOG_NAME))
+               return -EBUSY;
+
        outb(0x87, REG);
        outb(0x01, REG);
        outb(0x55, REG);
        outb(0x55, REG);
+       return 0;
 }
 
 static inline void superio_exit(void)
 {
        outb(0x02, REG);
        outb(0x02, VAL);
+       release_region(REG, 2);
 }
 
 static inline void superio_select(int ldn)
@@ -255,12 +262,11 @@ static void wdt_keepalive(void)
        set_bit(WDTS_KEEPALIVE, &wdt_status);
 }
 
-static void wdt_start(void)
+static int wdt_start(void)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&spinlock, flags);
-       superio_enter();
+       int ret = superio_enter();
+       if (ret)
+               return ret;
 
        superio_select(GPIO);
        if (test_bit(WDTS_USE_GP, &wdt_status))
@@ -270,15 +276,15 @@ static void wdt_start(void)
        wdt_update_timeout();
 
        superio_exit();
-       spin_unlock_irqrestore(&spinlock, flags);
+
+       return 0;
 }
 
-static void wdt_stop(void)
+static int wdt_stop(void)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&spinlock, flags);
-       superio_enter();
+       int ret = superio_enter();
+       if (ret)
+               return ret;
 
        superio_select(GPIO);
        superio_outb(0x00, WDTCTRL);
@@ -288,7 +294,7 @@ static void wdt_stop(void)
                superio_outb(0x00, WDTVALMSB);
 
        superio_exit();
-       spin_unlock_irqrestore(&spinlock, flags);
+       return 0;
 }
 
 /**
@@ -303,8 +309,6 @@ static void wdt_stop(void)
 
 static int wdt_set_timeout(int t)
 {
-       unsigned long flags;
-
        if (t < 1 || t > max_units * 60)
                return -EINVAL;
 
@@ -313,14 +317,15 @@ static int wdt_set_timeout(int t)
        else
                timeout = t;
 
-       spin_lock_irqsave(&spinlock, flags);
        if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
-               superio_enter();
+               int ret = superio_enter();
+               if (ret)
+                       return ret;
+
                superio_select(GPIO);
                wdt_update_timeout();
                superio_exit();
        }
-       spin_unlock_irqrestore(&spinlock, flags);
        return 0;
 }
 
@@ -339,12 +344,12 @@ static int wdt_set_timeout(int t)
 
 static int wdt_get_status(int *status)
 {
-       unsigned long flags;
-
        *status = 0;
        if (testmode) {
-               spin_lock_irqsave(&spinlock, flags);
-               superio_enter();
+               int ret = superio_enter();
+               if (ret)
+                       return ret;
+
                superio_select(GPIO);
                if (superio_inb(WDTCTRL) & WDT_ZERO) {
                        superio_outb(0x00, WDTCTRL);
@@ -353,7 +358,6 @@ static int wdt_get_status(int *status)
                }
 
                superio_exit();
-               spin_unlock_irqrestore(&spinlock, flags);
        }
        if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
                *status |= WDIOF_KEEPALIVEPING;
@@ -379,9 +383,17 @@ static int wdt_open(struct inode *inode, struct file *file)
        if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
                return -EBUSY;
        if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+               int ret;
                if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
                        __module_get(THIS_MODULE);
-               wdt_start();
+
+               ret = wdt_start();
+               if (ret) {
+                       clear_bit(WDTS_LOCKED, &wdt_status);
+                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
+                       clear_bit(WDTS_DEV_OPEN, &wdt_status);
+                       return ret;
+               }
        }
        return nonseekable_open(inode, file);
 }
@@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file)
 {
        if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
                if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
-                       wdt_stop();
+                       int ret = wdt_stop();
+                       if (ret) {
+                               /*
+                                * Stop failed. Just keep the watchdog alive
+                                * and hope nothing bad happens.
+                                */
+                               set_bit(WDTS_EXPECTED, &wdt_status);
+                               wdt_keepalive();
+                               return ret;
+                       }
                        clear_bit(WDTS_TIMER_RUN, &wdt_status);
                } else {
                        wdt_keepalive();
@@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                    &ident, sizeof(ident)) ? -EFAULT : 0;
 
        case WDIOC_GETSTATUS:
-               wdt_get_status(&status);
+               rc = wdt_get_status(&status);
+               if (rc)
+                       return rc;
                return put_user(status, uarg.i);
 
        case WDIOC_GETBOOTSTATUS:
@@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                switch (new_options) {
                case WDIOS_DISABLECARD:
-                       if (test_bit(WDTS_TIMER_RUN, &wdt_status))
-                               wdt_stop();
+                       if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+                               rc = wdt_stop();
+                               if (rc)
+                                       return rc;
+                       }
                        clear_bit(WDTS_TIMER_RUN, &wdt_status);
                        return 0;
 
                case WDIOS_ENABLECARD:
-                       if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
-                               wdt_start();
+                       if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+                               rc = wdt_start();
+                               if (rc) {
+                                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
+                                       return rc;
+                               }
+                       }
                        return 0;
 
                default:
@@ -560,16 +591,17 @@ static int __init it87_wdt_init(void)
        int rc = 0;
        int try_gameport = !nogameport;
        u8  chip_rev;
-       unsigned long flags;
+       int gp_rreq_fail = 0;
 
        wdt_status = 0;
 
-       spin_lock_irqsave(&spinlock, flags);
-       superio_enter();
+       rc = superio_enter();
+       if (rc)
+               return rc;
+
        chip_type = superio_inw(CHIPID);
        chip_rev  = superio_inb(CHIPREV) & 0x0f;
        superio_exit();
-       spin_unlock_irqrestore(&spinlock, flags);
 
        switch (chip_type) {
        case IT8702_ID:
@@ -603,8 +635,9 @@ static int __init it87_wdt_init(void)
                return -ENODEV;
        }
 
-       spin_lock_irqsave(&spinlock, flags);
-       superio_enter();
+       rc = superio_enter();
+       if (rc)
+               return rc;
 
        superio_select(GPIO);
        superio_outb(WDT_TOV1, WDTCFG);
@@ -620,21 +653,16 @@ static int __init it87_wdt_init(void)
                }
                gpact = superio_inb(ACTREG);
                superio_outb(0x01, ACTREG);
-               superio_exit();
-               spin_unlock_irqrestore(&spinlock, flags);
                if (request_region(base, 1, WATCHDOG_NAME))
                        set_bit(WDTS_USE_GP, &wdt_status);
                else
-                       rc = -EIO;
-       } else {
-               superio_exit();
-               spin_unlock_irqrestore(&spinlock, flags);
+                       gp_rreq_fail = 1;
        }
 
        /* If we haven't Gameport support, try to get CIR support */
        if (!test_bit(WDTS_USE_GP, &wdt_status)) {
                if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
-                       if (rc == -EIO)
+                       if (gp_rreq_fail)
                                printk(KERN_ERR PFX
                                        "I/O Address 0x%04x and 0x%04x"
                                        " already in use\n", base, CIR_BASE);
@@ -646,21 +674,16 @@ static int __init it87_wdt_init(void)
                        goto err_out;
                }
                base = CIR_BASE;
-               spin_lock_irqsave(&spinlock, flags);
-               superio_enter();
 
                superio_select(CIR);
                superio_outw(base, BASEREG);
                superio_outb(0x00, CIR_ILS);
                ciract = superio_inb(ACTREG);
                superio_outb(0x01, ACTREG);
-               if (rc == -EIO) {
+               if (gp_rreq_fail) {
                        superio_select(GAMEPORT);
                        superio_outb(gpact, ACTREG);
                }
-
-               superio_exit();
-               spin_unlock_irqrestore(&spinlock, flags);
        }
 
        if (timeout < 1 || timeout > max_units * 60) {
@@ -704,6 +727,7 @@ static int __init it87_wdt_init(void)
                "nogameport=%d)\n", chip_type, chip_rev, timeout,
                nowayout, testmode, exclusive, nogameport);
 
+       superio_exit();
        return 0;
 
 err_out_reboot:
@@ -711,49 +735,37 @@ err_out_reboot:
 err_out_region:
        release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
        if (!test_bit(WDTS_USE_GP, &wdt_status)) {
-               spin_lock_irqsave(&spinlock, flags);
-               superio_enter();
                superio_select(CIR);
                superio_outb(ciract, ACTREG);
-               superio_exit();
-               spin_unlock_irqrestore(&spinlock, flags);
        }
 err_out:
        if (try_gameport) {
-               spin_lock_irqsave(&spinlock, flags);
-               superio_enter();
                superio_select(GAMEPORT);
                superio_outb(gpact, ACTREG);
-               superio_exit();
-               spin_unlock_irqrestore(&spinlock, flags);
        }
 
+       superio_exit();
        return rc;
 }
 
 static void __exit it87_wdt_exit(void)
 {
-       unsigned long flags;
-       int nolock;
-
-       nolock = !spin_trylock_irqsave(&spinlock, flags);
-       superio_enter();
-       superio_select(GPIO);
-       superio_outb(0x00, WDTCTRL);
-       superio_outb(0x00, WDTCFG);
-       superio_outb(0x00, WDTVALLSB);
-       if (max_units > 255)
-               superio_outb(0x00, WDTVALMSB);
-       if (test_bit(WDTS_USE_GP, &wdt_status)) {
-               superio_select(GAMEPORT);
-               superio_outb(gpact, ACTREG);
-       } else {
-               superio_select(CIR);
-               superio_outb(ciract, ACTREG);
+       if (superio_enter() == 0) {
+               superio_select(GPIO);
+               superio_outb(0x00, WDTCTRL);
+               superio_outb(0x00, WDTCFG);
+               superio_outb(0x00, WDTVALLSB);
+               if (max_units > 255)
+                       superio_outb(0x00, WDTVALMSB);
+               if (test_bit(WDTS_USE_GP, &wdt_status)) {
+                       superio_select(GAMEPORT);
+                       superio_outb(gpact, ACTREG);
+               } else {
+                       superio_select(CIR);
+                       superio_outb(ciract, ACTREG);
+               }
+               superio_exit();
        }
-       superio_exit();
-       if (!nolock)
-               spin_unlock_irqrestore(&spinlock, flags);
 
        misc_deregister(&wdt_miscdev);
        unregister_reboot_notifier(&wdt_notifier);