diff options
Diffstat (limited to 'target/linux/rdc/patches-2.6.30/110-rdc321x_watchdog_fix.patch')
-rw-r--r-- | target/linux/rdc/patches-2.6.30/110-rdc321x_watchdog_fix.patch | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/target/linux/rdc/patches-2.6.30/110-rdc321x_watchdog_fix.patch b/target/linux/rdc/patches-2.6.30/110-rdc321x_watchdog_fix.patch new file mode 100644 index 0000000..3b1d8aa --- /dev/null +++ b/target/linux/rdc/patches-2.6.30/110-rdc321x_watchdog_fix.patch @@ -0,0 +1,315 @@ +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 <linux/watchdog.h> + #include <linux/io.h> + #include <linux/uaccess.h> ++#include <linux/pci.h> ++#include <linux/delay.h> + #include <linux/mfd/rdc321x.h> + +-#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); + |