--- linux-2.6.26.5/drivers/watchdog/mpc83xx_wdt.c 2008-09-08 13:40:20.000000000 -0400 +++ linux-2.6.26.5-rb600/drivers/watchdog/mpc83xx_wdt.c 2008-09-20 23:51:05.040468827 -0400 @@ -24,6 +24,13 @@ #include #include #include +#include + +#define RESET_CAUSE_IOCTL _IO('R', 1) + +#define RESET_COLD 0 +#define RESET_WARM 1 +#define RESET_WATCHDOG 2 struct mpc83xx_wdt { __be32 res0; @@ -39,6 +46,8 @@ struct mpc83xx_wdt { }; static struct mpc83xx_wdt __iomem *wd_base; +static unsigned __iomem *rsr = NULL; +#define RSR_WDT_RESET 0x8 static u16 timeout = 0xffff; module_param(timeout, ushort, 0); @@ -48,6 +57,9 @@ static int reset = 1; module_param(reset, bool, 0); MODULE_PARM_DESC(reset, "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset"); +static void mpc83xx_timer(unsigned long data); +static DEFINE_TIMER(autotimer, mpc83xx_timer, 0, 0); + /* * We always prescale, but if someone really doesn't want to they can set this * to 0 @@ -57,6 +69,7 @@ static unsigned int timeout_sec; static unsigned long wdt_is_open; static DEFINE_SPINLOCK(wdt_spinlock); +static int expect_close; static void mpc83xx_wdt_keepalive(void) { @@ -67,11 +80,29 @@ static void mpc83xx_wdt_keepalive(void) spin_unlock(&wdt_spinlock); } +static void mpc83xx_timer(unsigned long data) +{ + mpc83xx_wdt_keepalive(); + mod_timer(&autotimer, jiffies + HZ); +} + static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - if (count) + /* check for a magic close character */ + if (count) { + size_t i; + + expect_close = 0; + for (i = 0; i < count; ++i) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } mpc83xx_wdt_keepalive(); + } return count; } @@ -81,6 +112,8 @@ static int mpc83xx_wdt_open(struct inode if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; + del_timer(&autotimer); + /* Once we start the watchdog we can't stop it */ __module_get(THIS_MODULE); @@ -99,9 +132,13 @@ static int mpc83xx_wdt_open(struct inode static int mpc83xx_wdt_release(struct inode *inode, struct file *file) { + if (expect_close) + mod_timer(&autotimer, jiffies + HZ); + else printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); mpc83xx_wdt_keepalive(); clear_bit(0, &wdt_is_open); + expect_close = 0; return 0; } @@ -117,6 +154,13 @@ static int mpc83xx_wdt_ioctl(struct inod }; switch (cmd) { + case RESET_CAUSE_IOCTL: + if (rsr && (in_be32(rsr) & RSR_WDT_RESET)) { + out_be32(rsr, in_be32(rsr) | RSR_WDT_RESET); + return RESET_WATCHDOG; + } + else + return RESET_WARM; case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: @@ -147,12 +191,31 @@ static struct miscdevice mpc83xx_wdt_mis .fops = &mpc83xx_wdt_fops, }; +static void clean_rsr(void) { + if (rsr) { + iounmap(rsr); + rsr = NULL; + } +} + static int __devinit mpc83xx_wdt_probe(struct platform_device *dev) { struct resource *r; + struct device_node *soc; int ret; unsigned int *freq = dev->dev.platform_data; + /* get reset status register */ + soc = of_find_node_by_type(NULL, "soc"); + if (soc) { + unsigned int size; + static phys_addr_t immrbase = -1; + const void *prop = of_get_property(soc, "reg", &size); + immrbase = of_translate_address(soc, prop); + rsr = (unsigned *) ioremap_nocache(immrbase + 0x910, 0x4); + of_node_put(soc); + } + /* get a pointer to the register memory */ r = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -189,6 +252,7 @@ static int __devinit mpc83xx_wdt_probe(s err_unmap: iounmap(wd_base); + clean_rsr(); err_out: return ret; } @@ -197,6 +261,7 @@ static int __devexit mpc83xx_wdt_remove( { misc_deregister(&mpc83xx_wdt_miscdev); iounmap(wd_base); + clean_rsr(); return 0; }