summaryrefslogtreecommitdiff
path: root/target/linux/rdc/patches-2.6.30/110-rdc321x_watchdog_fix.patch
diff options
context:
space:
mode:
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.patch315
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);
+