Index: linux-2.6.30.10/drivers/watchdog/rdc321x_wdt.c =================================================================== --- linux-2.6.30.10.orig/drivers/watchdog/rdc321x_wdt.c 2010-04-28 11:11:44.000000000 +0200 +++ linux-2.6.30.10/drivers/watchdog/rdc321x_wdt.c 2010-04-28 11:20:51.000000000 +0200 @@ -36,111 +36,99 @@ #include #include #include +#include +#include #include -#define RDC_WDT_MASK 0x80000000 /* Mask */ +#define RDC321X_WDT_REG 0x00000044 + #define RDC_WDT_EN 0x00800000 /* Enable bit */ -#define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */ -#define RDC_WDT_RST 0x00100000 /* Reset bit */ -#define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */ -#define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */ -#define RDC_WDT_CNT 0x00000001 /* WDT count */ +#define RDC_WDT_WDTIRQ 0x00400000 /* Create WDT IRQ before CPU reset */ +#define RDC_WDT_NMIIRQ 0x00200000 /* Create NMI IRQ before CPU reset */ +#define RDC_WDT_RST 0x00100000 /* Reset wdt */ +#define RDC_WDT_NIF 0x00080000 /* NMI interrupt occured */ +#define RDC_WDT_WIF 0x00040000 /* WDT interrupt occured */ +#define RDC_WDT_IRT 0x00000700 /* IRQ Routing table */ +#define RDC_WDT_CNT 0x0000007F /* WDT count */ -#define RDC_CLS_TMR 0x80003844 /* Clear timer */ +/* default counter value (2.34 s) */ +#define RDC_WDT_DFLT_CNT 0x00000040 -#define RDC_WDT_INTERVAL (HZ/10+1) +#define RDC_WDT_SETUP (RDC_WDT_EN | RDC_WDT_NMIIRQ | RDC_WDT_RST | RDC_WDT_DFLT_CNT) static int ticks = 1000; /* some device data */ static struct { - struct completion stop; - int running; struct timer_list timer; - int queue; - int default_ticks; - unsigned long inuse; - spinlock_t lock; + int seconds_left; + int total_seconds; + bool inuse; + bool running; + bool close_expected; + struct pci_dev *sb_pdev; int base_reg; } rdc321x_wdt_device; -/* generic helper functions */ +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .identity = "RDC321x WDT", +}; + -static void rdc321x_wdt_trigger(unsigned long unused) +/* generic helper functions */ +static void rdc321x_wdt_timer(unsigned long unused) { - unsigned long flags; - u32 val; + if (!rdc321x_wdt_device.running) { + pci_write_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, 0); + return; + } - if (rdc321x_wdt_device.running) - ticks--; + rdc321x_wdt_device.seconds_left--; - /* keep watchdog alive */ - spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); - pci_read_config_dword(rdc321x_wdt_device.sb_pdev, - rdc321x_wdt_device.base_reg, &val); - val |= RDC_WDT_EN; - pci_write_config_dword(rdc321x_wdt_device.sb_pdev, - rdc321x_wdt_device.base_reg, val); - spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + if (rdc321x_wdt_device.seconds_left < 1) + return; - /* requeue?? */ - if (rdc321x_wdt_device.queue && ticks) - mod_timer(&rdc321x_wdt_device.timer, - jiffies + RDC_WDT_INTERVAL); - else { - /* ticks doesn't matter anyway */ - complete(&rdc321x_wdt_device.stop); - } + pci_write_config_dword(rdc321x_wdt_device.sb_pdev, + rdc321x_wdt_device.base_reg, RDC_WDT_SETUP); + mod_timer(&rdc321x_wdt_device.timer, HZ * 2 + jiffies); } static void rdc321x_wdt_reset(void) { - ticks = rdc321x_wdt_device.default_ticks; + rdc321x_wdt_device.seconds_left = rdc321x_wdt_device.total_seconds; } static void rdc321x_wdt_start(void) { - unsigned long flags; - - if (!rdc321x_wdt_device.queue) { - rdc321x_wdt_device.queue = 1; - - /* Clear the timer */ - spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); - pci_write_config_dword(rdc321x_wdt_device.sb_pdev, - rdc321x_wdt_device.base_reg, RDC_CLS_TMR); - - /* Enable watchdog and set the timeout to 81.92 us */ - pci_write_config_dword(rdc321x_wdt_device.sb_pdev, - rdc321x_wdt_device.base_reg, - RDC_WDT_EN | RDC_WDT_CNT); - spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + if (rdc321x_wdt_device.running) + return; - mod_timer(&rdc321x_wdt_device.timer, - jiffies + RDC_WDT_INTERVAL); - } + rdc321x_wdt_device.seconds_left = rdc321x_wdt_device.total_seconds; + rdc321x_wdt_device.running = true; + rdc321x_wdt_timer(0); - /* if process dies, counter is not decremented */ - rdc321x_wdt_device.running++; + return; } static int rdc321x_wdt_stop(void) { - if (rdc321x_wdt_device.running) - rdc321x_wdt_device.running = 0; + if (WATCHDOG_NOWAYOUT) + return -ENOSYS; - ticks = rdc321x_wdt_device.default_ticks; + rdc321x_wdt_device.running = false; - return -EIO; + return 0; } /* filesystem operations */ static int rdc321x_wdt_open(struct inode *inode, struct file *file) { - if (test_and_set_bit(0, &rdc321x_wdt_device.inuse)) + if (xchg(&rdc321x_wdt_device.inuse, true)) return -EBUSY; return nonseekable_open(inode, file); @@ -148,7 +136,16 @@ static int rdc321x_wdt_release(struct inode *inode, struct file *file) { - clear_bit(0, &rdc321x_wdt_device.inuse); + int ret; + + if (rdc321x_wdt_device.close_expected) { + ret = rdc321x_wdt_stop(); + if (ret) + return ret; + } + + rdc321x_wdt_device.inuse = false; + return 0; } @@ -156,30 +153,29 @@ unsigned long arg) { void __user *argp = (void __user *)arg; - u32 value; - static struct watchdog_info ident = { - .options = WDIOF_CARDRESET, - .identity = "RDC321x WDT", - }; - unsigned long flags; + int value; switch (cmd) { case WDIOC_KEEPALIVE: rdc321x_wdt_reset(); break; - case WDIOC_GETSTATUS: - /* Read the value from the DATA register */ - spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); - pci_read_config_dword(rdc321x_wdt_device.sb_pdev, - rdc321x_wdt_device.base_reg, &value); - spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); - if (copy_to_user(argp, &value, sizeof(u32))) - return -EFAULT; - break; case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; break; + case WDIOC_SETTIMEOUT: + if (copy_from_user(&rdc321x_wdt_device.total_seconds, argp, sizeof(int))) + return -EFAULT; + rdc321x_wdt_device.seconds_left = rdc321x_wdt_device.total_seconds; + break; + case WDIOC_GETTIMEOUT: + if (copy_to_user(argp, &rdc321x_wdt_device.total_seconds, sizeof(int))) + return -EFAULT; + break; + case WDIOC_GETTIMELEFT: + if (copy_to_user(argp, &rdc321x_wdt_device.seconds_left, sizeof(int))) + return -EFAULT; + break; case WDIOC_SETOPTIONS: if (copy_from_user(&value, argp, sizeof(int))) return -EFAULT; @@ -194,17 +190,34 @@ } break; default: - return -ENOTTY; + return -EINVAL; } + return 0; } static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + size_t i; + if (!count) return -EIO; + rdc321x_wdt_device.close_expected = false; + + for (i = 0; i != count; i++) { + char c; + + if (get_user(c, buf + i)) + return -EFAULT; + + if (c == 'V') { + rdc321x_wdt_device.close_expected = true; + break; + } + } + rdc321x_wdt_reset(); return count; @@ -246,27 +259,18 @@ rdc321x_wdt_device.sb_pdev = pdata->sb_pdev; rdc321x_wdt_device.base_reg = r->start; + rdc321x_wdt_device.running = false; + rdc321x_wdt_device.close_expected = false; + rdc321x_wdt_device.inuse = 0; + setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_timer, 0); + rdc321x_wdt_device.total_seconds = 100; + err = misc_register(&rdc321x_wdt_misc); if (err < 0) { dev_err(&pdev->dev, "misc_register failed\n"); return err; } - spin_lock_init(&rdc321x_wdt_device.lock); - - /* Reset the watchdog */ - pci_write_config_dword(rdc321x_wdt_device.sb_pdev, - rdc321x_wdt_device.base_reg, RDC_WDT_RST); - - init_completion(&rdc321x_wdt_device.stop); - rdc321x_wdt_device.queue = 0; - - clear_bit(0, &rdc321x_wdt_device.inuse); - - setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0); - - rdc321x_wdt_device.default_ticks = ticks; - dev_info(&pdev->dev, "watchdog init success\n"); return 0; @@ -274,10 +278,11 @@ static int rdc321x_wdt_remove(struct platform_device *pdev) { - if (rdc321x_wdt_device.queue) { - rdc321x_wdt_device.queue = 0; - wait_for_completion(&rdc321x_wdt_device.stop); - } + if (rdc321x_wdt_device.inuse) + rdc321x_wdt_device.inuse = 0; + + while (timer_pending(&rdc321x_wdt_device.timer)) + msleep(100); misc_deregister(&rdc321x_wdt_misc);