diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fe819b76de56..277ecbc36bcb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -578,6 +578,7 @@ config INTEL_SCU_WATCHDOG config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI + select WATCHDOG_CORE select LPC_ICH ---help--- Hardware driver for the intel TCO timer based watchdog devices. diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 9c2c27c3b424..ceed39f26011 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -47,7 +47,7 @@ /* Module and version information */ #define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.07" +#define DRV_VERSION "1.10" /* Includes */ #include /* For module specific items */ @@ -88,8 +88,6 @@ #define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */ /* internal variables */ -static unsigned long is_active; -static char expect_release; static struct { /* this is private data for the iTCO_wdt device */ /* TCO version/generation */ unsigned int iTCO_version; @@ -106,12 +104,12 @@ static struct { /* this is private data for the iTCO_wdt device */ } iTCO_wdt_private; /* module parameters */ -#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ -static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ +#define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ +static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */ module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. " "5..76 (TCO v1) or 3..614 (TCO v2), default=" - __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); @@ -178,13 +176,13 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) return ret; /* returns: 0 = OK, -EIO = Error */ } -static int iTCO_wdt_start(void) +static int iTCO_wdt_start(struct watchdog_device *wd_dev) { unsigned int val; spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, heartbeat); + iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout); /* disable chipset's NO_REBOOT bit */ if (iTCO_wdt_unset_NO_REBOOT_bit()) { @@ -212,7 +210,7 @@ static int iTCO_wdt_start(void) return 0; } -static int iTCO_wdt_stop(void) +static int iTCO_wdt_stop(struct watchdog_device *wd_dev) { unsigned int val; @@ -236,11 +234,11 @@ static int iTCO_wdt_stop(void) return 0; } -static int iTCO_wdt_keepalive(void) +static int iTCO_wdt_ping(struct watchdog_device *wd_dev) { spin_lock(&iTCO_wdt_private.io_lock); - iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, heartbeat); + iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout); /* Reload the timer by writing to the TCO Timer Counter register */ if (iTCO_wdt_private.iTCO_version == 2) @@ -257,7 +255,7 @@ static int iTCO_wdt_keepalive(void) return 0; } -static int iTCO_wdt_set_heartbeat(int t) +static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) { unsigned int val16; unsigned char val8; @@ -304,14 +302,15 @@ static int iTCO_wdt_set_heartbeat(int t) return -EINVAL; } - heartbeat = t; + wd_dev->timeout = t; return 0; } -static int iTCO_wdt_get_timeleft(int *time_left) +static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) { unsigned int val16; unsigned char val8; + unsigned int time_left = 0; /* read the TCO Timer */ if (iTCO_wdt_private.iTCO_version == 2) { @@ -320,7 +319,7 @@ static int iTCO_wdt_get_timeleft(int *time_left) val16 &= 0x3ff; spin_unlock(&iTCO_wdt_private.io_lock); - *time_left = (val16 * 6) / 10; + time_left = (val16 * 6) / 10; } else if (iTCO_wdt_private.iTCO_version == 1) { spin_lock(&iTCO_wdt_private.io_lock); val8 = inb(TCO_RLD); @@ -329,156 +328,35 @@ static int iTCO_wdt_get_timeleft(int *time_left) val8 += (inb(TCOv1_TMR) & 0x3f); spin_unlock(&iTCO_wdt_private.io_lock); - *time_left = (val8 * 6) / 10; - } else - return -EINVAL; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static int iTCO_wdt_open(struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &is_active)) - return -EBUSY; - - /* - * Reload and activate timer - */ - iTCO_wdt_start(); - return nonseekable_open(inode, file); -} - -static int iTCO_wdt_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - */ - if (expect_release == 42) { - iTCO_wdt_stop(); - } else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - iTCO_wdt_keepalive(); - } - clear_bit(0, &is_active); - expect_release = 0; - return 0; -} - -static ssize_t iTCO_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (len) { - if (!nowayout) { - size_t i; - - /* note: just in case someone wrote the magic - character five months ago... */ - expect_release = 0; - - /* scan to see whether or not we got the - magic character */ - for (i = 0; i != len; i++) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_release = 42; - } - } - - /* someone wrote to us, we should reload the timer */ - iTCO_wdt_keepalive(); - } - return len; -} - -static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int new_options, retval = -EINVAL; - int new_heartbeat; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static const struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = DRV_NAME, - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_SETOPTIONS: - { - if (get_user(new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - iTCO_wdt_stop(); - retval = 0; - } - if (new_options & WDIOS_ENABLECARD) { - iTCO_wdt_keepalive(); - iTCO_wdt_start(); - retval = 0; - } - return retval; - } - case WDIOC_KEEPALIVE: - iTCO_wdt_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: - { - if (get_user(new_heartbeat, p)) - return -EFAULT; - if (iTCO_wdt_set_heartbeat(new_heartbeat)) - return -EINVAL; - iTCO_wdt_keepalive(); - /* Fall */ - } - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - case WDIOC_GETTIMELEFT: - { - int time_left; - if (iTCO_wdt_get_timeleft(&time_left)) - return -EINVAL; - return put_user(time_left, p); - } - default: - return -ENOTTY; + time_left = (val8 * 6) / 10; } + return time_left; } /* * Kernel Interfaces */ -static const struct file_operations iTCO_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = iTCO_wdt_write, - .unlocked_ioctl = iTCO_wdt_ioctl, - .open = iTCO_wdt_open, - .release = iTCO_wdt_release, +static const struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = DRV_NAME, }; -static struct miscdevice iTCO_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &iTCO_wdt_fops, +static const struct watchdog_ops iTCO_wdt_ops = { + .owner = THIS_MODULE, + .start = iTCO_wdt_start, + .stop = iTCO_wdt_stop, + .ping = iTCO_wdt_ping, + .set_timeout = iTCO_wdt_set_timeout, + .get_timeleft = iTCO_wdt_get_timeleft, +}; + +static struct watchdog_device iTCO_wdt_watchdog_dev = { + .info = &ident, + .ops = &iTCO_wdt_ops, }; /* @@ -489,10 +367,10 @@ static void __devexit iTCO_wdt_cleanup(void) { /* Stop the timer before we leave */ if (!nowayout) - iTCO_wdt_stop(); + iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); /* Deregister */ - misc_deregister(&iTCO_wdt_miscdev); + watchdog_unregister_device(&iTCO_wdt_watchdog_dev); /* release resources */ release_region(iTCO_wdt_private.tco_res->start, @@ -605,20 +483,25 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev) outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ + iTCO_wdt_watchdog_dev.bootstatus = 0; + iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; + watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout); + iTCO_wdt_watchdog_dev.parent = dev->dev.parent; + /* Make sure the watchdog is not running */ - iTCO_wdt_stop(); + iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); /* Check that the heartbeat value is within it's range; if not reset to the default */ - if (iTCO_wdt_set_heartbeat(heartbeat)) { - iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); - pr_info("timeout value out of range, using %d\n", heartbeat); + if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) { + iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT); + pr_info("timeout value out of range, using %d\n", + WATCHDOG_TIMEOUT); } - ret = misc_register(&iTCO_wdt_miscdev); + ret = watchdog_register_device(&iTCO_wdt_watchdog_dev); if (ret != 0) { - pr_err("cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); + pr_err("cannot register watchdog device (err=%d)\n", ret); goto unreg_tco; } @@ -659,7 +542,7 @@ static int __devexit iTCO_wdt_remove(struct platform_device *dev) static void iTCO_wdt_shutdown(struct platform_device *dev) { - iTCO_wdt_stop(); + iTCO_wdt_stop(NULL); } static struct platform_driver iTCO_wdt_driver = {