From 58b519f3e5e491d5a3e320dc525f58ac439bdde4 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Sun, 21 May 2006 12:48:44 +0200 Subject: [WATCHDOG] add WDIOC_GETTIMELEFT ioctl Some watchdog drivers have the ability to report the remaining time before the system will reboot. With the WDIOC_GETTIMELEFT ioctl you can now read the time left before the watchdog would reboot your system. The following drivers support this new IOCTL: i8xx_tco.c, pcwd_pci.c and pcwd_usb.c . Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton --- drivers/char/watchdog/i8xx_tco.c | 28 +++++++++++++++++++++++++++- drivers/char/watchdog/pcwd_pci.c | 30 +++++++++++++++++++++++++++++- drivers/char/watchdog/pcwd_usb.c | 23 +++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/watchdog/i8xx_tco.c b/drivers/char/watchdog/i8xx_tco.c index fa2ba9ebe42a..bfbdbbf3c2f2 100644 --- a/drivers/char/watchdog/i8xx_tco.c +++ b/drivers/char/watchdog/i8xx_tco.c @@ -205,6 +205,23 @@ static int tco_timer_set_heartbeat (int t) return 0; } +static int tco_timer_get_timeleft (int *time_left) +{ + unsigned char val; + + spin_lock(&tco_lock); + + /* read the TCO Timer */ + val = inb (TCO1_RLD); + val &= 0x3f; + + spin_unlock(&tco_lock); + + *time_left = (int)((val * 6) / 10); + + return 0; +} + /* * /dev/watchdog handling */ @@ -272,6 +289,7 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file, { int new_options, retval = -EINVAL; int new_heartbeat; + int time_left; void __user *argp = (void __user *)arg; int __user *p = argp; static struct watchdog_info ident = { @@ -320,7 +338,7 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file, return -EFAULT; if (tco_timer_set_heartbeat(new_heartbeat)) - return -EINVAL; + return -EINVAL; tco_timer_keepalive (); /* Fall */ @@ -329,6 +347,14 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file, case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); + case WDIOC_GETTIMELEFT: + { + if (tco_timer_get_timeleft(&time_left)) + return -EINVAL; + + return put_user(time_left, p); + } + default: return -ENOIOCTLCMD; } diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c index 2451edbefece..1f40ecefbf72 100644 --- a/drivers/char/watchdog/pcwd_pci.c +++ b/drivers/char/watchdog/pcwd_pci.c @@ -21,7 +21,7 @@ */ /* - * A bells and whistles driver is available from: + * A bells and whistles driver is available from: * http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/ * * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ @@ -390,6 +390,24 @@ static int pcipcwd_get_temperature(int *temperature) return 0; } +static int pcipcwd_get_timeleft(int *time_left) +{ + int msb; + int lsb; + + /* Read the time that's left before rebooting */ + /* Note: if the board is not yet armed then we will read 0xFFFF */ + send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); + + *time_left = (msb << 8) + lsb; + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Time left before next reboot: %d\n", + *time_left); + + return 0; +} + /* * /dev/watchdog handling */ @@ -512,6 +530,16 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); + case WDIOC_GETTIMELEFT: + { + int time_left; + + if (pcipcwd_get_timeleft(&time_left)) + return -EFAULT; + + return put_user(time_left, p); + } + default: return -ENOIOCTLCMD; } diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c index 3fdfda9324fa..0d072bed501d 100644 --- a/drivers/char/watchdog/pcwd_usb.c +++ b/drivers/char/watchdog/pcwd_usb.c @@ -317,6 +317,19 @@ static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temp return 0; } +static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd, int *time_left) +{ + unsigned char msb, lsb; + + /* Read the time that's left before rebooting */ + /* Note: if the board is not yet armed then we will read 0xFFFF */ + usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); + + *time_left = (msb << 8) + lsb; + + return 0; +} + /* * /dev/watchdog handling */ @@ -422,6 +435,16 @@ static int usb_pcwd_ioctl(struct inode *inode, struct file *file, case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); + case WDIOC_GETTIMELEFT: + { + int time_left; + + if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left)) + return -EFAULT; + + return put_user(time_left, p); + } + default: return -ENOIOCTLCMD; } -- cgit v1.2.3 From dfc7bd9c385a888851a2d009ba272099549f98cc Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Sun, 21 May 2006 15:32:59 +0200 Subject: [WATCHDOG] convert AT91RM9200 watchdog to platform driver Converted to a platform driver. Added suspend/resume support - the watchdog is disabled during the sleep states. Original patch from David Brownell. Signed-off-by: Andrew Victor Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton --- drivers/char/watchdog/at91_wdt.c | 82 ++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/char/watchdog/at91_wdt.c b/drivers/char/watchdog/at91_wdt.c index ac83bc4b019a..00080655533d 100644 --- a/drivers/char/watchdog/at91_wdt.c +++ b/drivers/char/watchdog/at91_wdt.c @@ -17,14 +17,15 @@ #include #include #include +#include #include #include #include #include -#define WDT_DEFAULT_TIME 5 /* 5 seconds */ -#define WDT_MAX_TIME 256 /* 256 seconds */ +#define WDT_DEFAULT_TIME 5 /* seconds */ +#define WDT_MAX_TIME 256 /* seconds */ static int wdt_time = WDT_DEFAULT_TIME; static int nowayout = WATCHDOG_NOWAYOUT; @@ -32,8 +33,10 @@ static int nowayout = WATCHDOG_NOWAYOUT; module_param(wdt_time, int, 0); MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); +#ifdef CONFIG_WATCHDOG_NOWAYOUT module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +#endif static unsigned long at91wdt_busy; @@ -138,7 +141,7 @@ static int at91_wdt_ioctl(struct inode *inode, struct file *file, case WDIOC_SETTIMEOUT: if (get_user(new_value, p)) return -EFAULT; - + if (at91_wdt_settimeout(new_value)) return -EINVAL; @@ -196,27 +199,84 @@ static struct miscdevice at91wdt_miscdev = { .fops = &at91wdt_fops, }; -static int __init at91_wdt_init(void) +static int __init at91wdt_probe(struct platform_device *pdev) { int res; - /* Check that the heartbeat value is within range; if not reset to the default */ - if (at91_wdt_settimeout(wdt_time)) { - at91_wdt_settimeout(WDT_DEFAULT_TIME); - printk(KERN_INFO "at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); - } + if (at91wdt_miscdev.dev) + return -EBUSY; + at91wdt_miscdev.dev = &pdev->dev; res = misc_register(&at91wdt_miscdev); if (res) return res; - printk("AT91 Watchdog Timer enabled (%d seconds, nowayout=%d)\n", wdt_time, nowayout); + printk("AT91 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : ""); return 0; } +static int __exit at91wdt_remove(struct platform_device *pdev) +{ + int res; + + res = misc_deregister(&at91wdt_miscdev); + if (!res) + at91wdt_miscdev.dev = NULL; + + return res; +} + +static void at91wdt_shutdown(struct platform_device *pdev) +{ + at91_wdt_stop(); +} + +#ifdef CONFIG_PM + +static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) +{ + at91_wdt_stop(); + return 0; +} + +static int at91wdt_resume(struct platform_device *pdev) +{ + if (at91wdt_busy) + at91_wdt_start(); + return 0; +} + +#else +#define at91wdt_suspend NULL +#define at91wdt_resume NULL +#endif + +static struct platform_driver at91wdt_driver = { + .probe = at91wdt_probe, + .remove = __exit_p(at91wdt_remove), + .shutdown = at91wdt_shutdown, + .suspend = at91wdt_suspend, + .resume = at91wdt_resume, + .driver = { + .name = "at91_wdt", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_wdt_init(void) +{ + /* Check that the heartbeat value is within range; if not reset to the default */ + if (at91_wdt_settimeout(wdt_time)) { + at91_wdt_settimeout(WDT_DEFAULT_TIME); + pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); + } + + return platform_driver_register(&at91wdt_driver); +} + static void __exit at91_wdt_exit(void) { - misc_deregister(&at91wdt_miscdev); + platform_driver_unregister(&at91wdt_driver); } module_init(at91_wdt_init); -- cgit v1.2.3