|
@@ -137,7 +137,6 @@
|
|
|
|
|
|
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);
|