--- linux-2.6.22.14/drivers/char/watchdog/mpc83xx_wdt.c 2008-01-22 14:02:07.000000000 +0200 +++ linux/drivers/char/watchdog/mpc83xx_wdt.c 2008-01-21 13:42:46.000000000 +0200 @@ -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 spinlock_t 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,14 @@ 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 +155,14 @@ 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_KEEPALIVE: @@ -144,11 +190,30 @@ 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; - int ret; + struct device_node *soc; unsigned int *freq = dev->dev.platform_data; + int ret; + + /* 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 = 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 +254,7 @@ static int __devinit mpc83xx_wdt_probe(s err_unmap: iounmap(wd_base); + clean_rsr(); err_out: return ret; } @@ -197,7 +263,7 @@ static int __devexit mpc83xx_wdt_remove( { misc_deregister(&mpc83xx_wdt_miscdev); iounmap(wd_base); - + clean_rsr(); return 0; }