diff options
23 files changed, 2293 insertions, 438 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index d329cdc5dfb9..7825533927bc 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -283,4 +283,24 @@ config MACHZ_WDT The module is called machzwd.o. If you want to compile it as a module, say M here and read <file:Documentation/modules.txt>. +config W83877F_WDT + tristate "W83877F Computer Watchdog" + depends on WATCHDOG + +config SC520_WDT + tristate "AMD Elan SC520 processor Watchdog" + depends on WATCHDOG + +config ALIM7101_WDT + tristate "ALi M7101 PMU Computer Watchdog" + depends on WATCHDOG + +config SC1200_WDT + tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" + depends on WATCHDOG + +config WAFER_WDT + tristate "ICP Wafer 5823 Single Board Computer Watchdog" + depends on WATCHDOG + endmenu diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index cdd69eb962ad..51d904e418f4 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -23,3 +23,8 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SH_WDT) += shwdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o +obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o +obj-$(CONFIG_SC520_WDT) += sc520_wdt.o +obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o +obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o +obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o diff --git a/drivers/char/watchdog/acquirewdt.c b/drivers/char/watchdog/acquirewdt.c index 3a0ecbcf2033..b509fb731454 100644 --- a/drivers/char/watchdog/acquirewdt.c +++ b/drivers/char/watchdog/acquirewdt.c @@ -45,6 +45,7 @@ static int acq_is_open; static spinlock_t acq_lock; +static int expect_close = 0; /* * You must set these - there is no sane way to probe for this board. @@ -79,8 +80,20 @@ static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_ if (ppos != &file->f_pos) return -ESPIPE; - if(count) - { + if(count) { + if (!nowayout) { + 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; + } + } acq_ping(); return 1; } @@ -97,9 +110,11 @@ static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - static struct watchdog_info ident= + static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING, 1, "Acquire WDT" + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "Acquire WDT" }; switch(cmd) @@ -126,39 +141,35 @@ static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int acq_open(struct inode *inode, struct file *file) { - switch(minor(inode->i_rdev)) - { - case WATCHDOG_MINOR: - spin_lock(&acq_lock); - if(acq_is_open) - { - spin_unlock(&acq_lock); - return -EBUSY; - } - if (nowayout) { - MOD_INC_USE_COUNT; - } - /* - * Activate - */ - - acq_is_open=1; - inb_p(WDT_START); + if ((minor(inode->i_rdev) == WATCHDOG_MINOR)) { + spin_lock(&acq_lock); + if(acq_is_open) { spin_unlock(&acq_lock); - return 0; - default: - return -ENODEV; + return -EBUSY; + } + if (nowayout) + MOD_INC_USE_COUNT; + + /* Activate */ + acq_is_open=1; + inb_p(WDT_START); + spin_unlock(&acq_lock); + return 0; + + } else { + return -ENODEV; } } static int acq_close(struct inode *inode, struct file *file) { - if(minor(inode->i_rdev)==WATCHDOG_MINOR) - { + if(minor(inode->i_rdev)==WATCHDOG_MINOR) { spin_lock(&acq_lock); - if (!nowayout) { + if (expect_close) inb_p(WDT_STOP); - } + else + printk(KERN_CRIT "WDT closed unexpectedly. WDT will not stop!\n"); + acq_is_open=0; spin_unlock(&acq_lock); } @@ -173,10 +184,9 @@ static int acq_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if(code==SYS_DOWN || code==SYS_HALT) - { /* Turn the card off */ inb_p(WDT_STOP); - } + return NOTIFY_DONE; } @@ -196,9 +206,9 @@ static struct file_operations acq_fops = { static struct miscdevice acq_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &acq_fops + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &acq_fops }; @@ -207,11 +217,11 @@ static struct miscdevice acq_miscdev= * turn the timebomb registers off. */ -static struct notifier_block acq_notifier= +static struct notifier_block acq_notifier = { - acq_notify_sys, - NULL, - 0 + .notifier_call = acq_notify_sys, + .next = NULL, + .priority = 0 }; static int __init acq_init(void) @@ -221,17 +231,15 @@ static int __init acq_init(void) spin_lock_init(&acq_lock); if (misc_register(&acq_miscdev)) return -ENODEV; - if (!request_region(WDT_STOP, 1, "Acquire WDT")) - { + if (!request_region(WDT_STOP, 1, "Acquire WDT")) { misc_deregister(&acq_miscdev); return -EIO; - } - if (!request_region(WDT_START, 1, "Acquire WDT")) - { + } + if (!request_region(WDT_START, 1, "Acquire WDT")) { release_region(WDT_STOP, 1); misc_deregister(&acq_miscdev); return -EIO; - } + } register_reboot_notifier(&acq_notifier); return 0; diff --git a/drivers/char/watchdog/advantechwdt.c b/drivers/char/watchdog/advantechwdt.c index 226e3a75d08a..f994c8fc5e69 100644 --- a/drivers/char/watchdog/advantechwdt.c +++ b/drivers/char/watchdog/advantechwdt.c @@ -47,6 +47,7 @@ #include <linux/smp_lock.h> static int advwdt_is_open; +static char adv_expect_close; static spinlock_t advwdt_lock; /* @@ -99,10 +100,22 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) return -ESPIPE; if (count) { + if (!nowayout) { + size_t i; + + adv_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf+i)) + return -EFAULT; + if (c == 'V') + adv_expect_close = 42; + } + } advwdt_ping(); - return 1; } - return 0; + return count; } static ssize_t @@ -116,9 +129,11 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING, 1, "Advantech WDT" + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "Advantech WDT" }; - + switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) @@ -143,26 +158,23 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int advwdt_open(struct inode *inode, struct file *file) { - switch (minor(inode->i_rdev)) { - case WATCHDOG_MINOR: - spin_lock(&advwdt_lock); - if (advwdt_is_open) { - spin_unlock(&advwdt_lock); - return -EBUSY; - } - if (nowayout) { - MOD_INC_USE_COUNT; - } - /* - * Activate - */ - - advwdt_is_open = 1; - advwdt_ping(); + if (minor(inode->i_rdev) == WATCHDOG_MINOR) { + spin_lock(&advwdt_lock); + if (advwdt_is_open) { spin_unlock(&advwdt_lock); - return 0; - default: - return -ENODEV; + return -EBUSY; + } + + if (nowayout) + MOD_INC_USE_COUNT; + + /* Activate */ + advwdt_is_open = 1; + advwdt_ping(); + spin_unlock(&advwdt_lock); + return 0; + } else { + return -ENODEV; } } @@ -171,9 +183,9 @@ advwdt_close(struct inode *inode, struct file *file) { if (minor(inode->i_rdev) == WATCHDOG_MINOR) { spin_lock(&advwdt_lock); - if (!nowayout) { + if (!nowayout) inb_p(WDT_STOP); - } + advwdt_is_open = 0; spin_unlock(&advwdt_lock); } @@ -188,10 +200,10 @@ static int advwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { - if (code == SYS_DOWN || code == SYS_HALT) { + if (code == SYS_DOWN || code == SYS_HALT) /* Turn the WDT off */ inb_p(WDT_STOP); - } + return NOTIFY_DONE; } @@ -209,9 +221,9 @@ static struct file_operations advwdt_fops = { }; static struct miscdevice advwdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &advwdt_fops + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &advwdt_fops }; /* @@ -220,9 +232,9 @@ static struct miscdevice advwdt_miscdev = { */ static struct notifier_block advwdt_notifier = { - advwdt_notify_sys, - NULL, - 0 + .notifier_call = advwdt_notify_sys, + .next = NULL, + .priority = 0 }; static void __init @@ -276,5 +288,3 @@ module_exit(advwdt_exit); MODULE_LICENSE("GPL"); -/* end of advantechwdt.c */ - diff --git a/drivers/char/watchdog/alim7101_wdt.c b/drivers/char/watchdog/alim7101_wdt.c new file mode 100644 index 000000000000..653ae2d767fe --- /dev/null +++ b/drivers/char/watchdog/alim7101_wdt.c @@ -0,0 +1,345 @@ +/* + * ALi M7101 PMU Computer Watchdog Timer driver for Linux 2.4.x + * + * Based on w83877f_wdt.c by Scott Jennings <management@oro.net> + * and the Cobalt kernel WDT timer driver by Tim Hockin + * <thockin@cobaltnet.com> + * + * (c)2002 Steve Hill <steve@navaho.co.uk> + * + * Theory of operation: + * A Watchdog Timer (WDT) is a hardware circuit that can + * reset the computer system in case of a software fault. + * You probably knew that already. + * + * Usually a userspace daemon will notify the kernel WDT driver + * via the /proc/watchdog special device file that userspace is + * still alive, at regular intervals. When such a notification + * occurs, the driver will usually tell the hardware watchdog + * that everything is in order, and that the watchdog should wait + * for yet another little while to reset the system. + * If userspace fails (RAM error, kernel bug, whatever), the + * notifications cease to occur, and the hardware watchdog will + * reset the system (causing a reboot) after the timeout occurs. + * + * This WDT driver is different from most other Linux WDT + * drivers in that the driver will ping the watchdog by itself, + * because this particular WDT has a very short timeout (1.6 + * seconds) and it would be insane to count on any userspace + * daemon always getting scheduled within that time frame. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/fcntl.h> +#include <linux/smp_lock.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/pci.h> + +#define OUR_NAME "alim7101_wdt" + +#define WDT_ENABLE 0x9C +#define WDT_DISABLE 0x8C + +#define ALI_7101_WDT 0x92 +#define ALI_WDT_ARM 0x01 + +/* + * We're going to use a 1 second timeout. + * If we reset the watchdog every ~250ms we should be safe. */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + */ + +#define WDT_HEARTBEAT (HZ * 30) + +static void wdt_timer_ping(unsigned long); +static struct timer_list timer; +static unsigned long next_heartbeat; +static unsigned long wdt_is_open; +static int wdt_expect_close; +static struct pci_dev *alim7101_pmu; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + char tmp; + + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT (this is actually a disarm/arm sequence) */ + pci_read_config_byte(alim7101_pmu, 0x92, &tmp); + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); + } else { + printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + } + /* Re-set the timer interval */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); +} + +/* + * Utility routines + */ + +static void wdt_change(int writeval) +{ + char tmp; + + pci_read_config_byte(alim7101_pmu, 0x92, &tmp); + if (writeval == WDT_ENABLE) + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); + else + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + WDT_HEARTBEAT; + + /* We must enable before we kick off the timer in case the timer + occurs as we ping it */ + + wdt_change(WDT_ENABLE); + + /* Start the timer */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); + + + printk(OUR_NAME ": Watchdog timer is now enabled.\n"); +} + +static void wdt_turnoff(void) +{ + /* Stop the timer */ + del_timer_sync(&timer); + wdt_change(WDT_DISABLE); + printk(OUR_NAME ": Watchdog timer is now disabled...\n"); +} + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos) +{ + /* We can't seek */ + if(ppos != &file->f_pos) + return -ESPIPE; + + /* See if we got the magic character */ + if(count) { + if (!nowayout) { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* now scan */ + for (ofs = 0; ofs != count; ofs++) { + char c; + if (get_user(c, buf+ofs)) + return -EFAULT; + if (c == 'V') + wdt_expect_close = 1; + } + } + /* someone wrote to us, we should restart timer */ + next_heartbeat = jiffies + WDT_HEARTBEAT; + return 1; + }; + return 0; +} + +static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos) +{ + /* No can do */ + return -EINVAL; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* Good, fire up the show */ + wdt_startup(); + return 0; +} + +static int fop_close(struct inode * inode, struct file * file) +{ + if(wdt_expect_close) + wdt_turnoff(); + else + printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); + + clear_bit(0, &wdt_is_open); + return 0; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = + { + .options = WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "ALiM7101" + }; + + switch(cmd) + { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; + case WDIOC_KEEPALIVE: + next_heartbeat = jiffies + WDT_HEARTBEAT; + return 0; + default: + return -ENOTTY; + } +} + +static struct file_operations wdt_fops = { + .owner= THIS_MODULE, + .llseek= no_llseek, + .read= fop_read, + .write= fop_write, + .open= fop_open, + .release= fop_close, + .ioctl= fop_ioctl +}; + +static struct miscdevice wdt_miscdev = { + .minor=WATCHDOG_MINOR, + .name="watchdog", + .fops=&wdt_fops +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + + if (code==SYS_RESTART) { + /* + * Cobalt devices have no way of rebooting themselves other than + * getting the watchdog to pull reset, so we restart the watchdog on + * reboot with no heartbeat + */ + wdt_change(WDT_ENABLE); + printk(OUR_NAME ": Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n"); + } + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier= +{ + .notifier_call = wdt_notify_sys, + .next = 0, + .priority = 0 +}; + +static void __exit alim7101_wdt_unload(void) +{ + wdt_turnoff(); + /* Deregister */ + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); +} + +static int __init alim7101_wdt_init(void) +{ + int rc = -EBUSY; + struct pci_dev *ali1543_south; + char tmp; + + printk(KERN_INFO OUR_NAME ": Steve Hill <steve@navaho.co.uk>.\n"); + alim7101_pmu = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,NULL); + if (!alim7101_pmu) { + printk(KERN_INFO OUR_NAME ": ALi M7101 PMU not present - WDT not set\n"); + return -EBUSY; + } + + /* Set the WDT in the PMU to 1 second */ + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02); + + ali1543_south = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + if (!ali1543_south) { + printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge not present - WDT not set\n"); + return -EBUSY; + } + pci_read_config_byte(ali1543_south, 0x5e, &tmp); + if ((tmp & 0x1e) != 0x12) { + printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n"); + return -EBUSY; + } + + init_timer(&timer); + timer.function = wdt_timer_ping; + timer.data = 1; + + rc = misc_register(&wdt_miscdev); + if (rc) + return rc; + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) { + misc_deregister(&wdt_miscdev); + return rc; + } + + printk(KERN_INFO OUR_NAME ": WDT driver for ALi M7101 initialised.\n"); + return 0; +} + +module_init(alim7101_wdt_init); +module_exit(alim7101_wdt_unload); + +MODULE_AUTHOR("Steve Hill"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c index 6ce0e3c7dfed..46c015c372a9 100644 --- a/drivers/char/watchdog/eurotechwdt.c +++ b/drivers/char/watchdog/eurotechwdt.c @@ -7,23 +7,23 @@ * Based on wdt.c. * Original copyright messages: * - * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved. - * http://www.redhat.com + * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved. + * http://www.redhat.com * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. * - * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>* + * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> * - * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * Added timeout module option to override default + * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * Added timeout module option to override default */ #include <linux/config.h> @@ -49,17 +49,18 @@ static int eurwdt_is_open; static spinlock_t eurwdt_lock; +static char eur_expect_close; /* - * You must set these - there is no sane way to probe for this board. - * You can use wdt=x,y to set these now. + * You must set these - there is no sane way to probe for this board. + * You can use wdt=x,y to set these now. */ static int io = 0x3f0; static int irq = 10; static char *ev = "int"; -#define WDT_TIMEOUT 60 /* 1 minute */ +#define WDT_TIMEOUT 60 /* 1 minute */ static int timeout = WDT_TIMEOUT; MODULE_PARM(timeout,"i"); @@ -80,10 +81,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON #define WDT_CTRL_REG 0x30 #define WDT_OUTPIN_CFG 0xe2 - #define WDT_EVENT_INT 0x00 - #define WDT_EVENT_REBOOT 0x08 +#define WDT_EVENT_INT 0x00 +#define WDT_EVENT_REBOOT 0x08 #define WDT_UNIT_SEL 0xf1 - #define WDT_UNIT_SECS 0x80 +#define WDT_UNIT_SECS 0x80 #define WDT_TIMEOUT_VAL 0xf2 #define WDT_TIMER_CFG 0xf3 @@ -91,27 +92,27 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON #ifndef MODULE /** - * eurwdt_setup: - * @str: command line string + * eurwdt_setup: + * @str: command line string * - * Setup options. The board isn't really probe-able so we have to - * get the user to tell us the configuration. Sane people build it - * modular but the others come here. + * Setup options. The board isn't really probe-able so we have to + * get the user to tell us the configuration. Sane people build it + * modular but the others come here. */ static int __init eurwdt_setup(char *str) { - int ints[4]; + int ints[4]; - str = get_options (str, ARRAY_SIZE(ints), ints); +str = get_options (str, ARRAY_SIZE(ints), ints); - if (ints[0] > 0) { - io = ints[1]; - if (ints[0] > 1) - irq = ints[2]; - } + if (ints[0] > 0) { + io = ints[1]; + if (ints[0] > 1) + irq = ints[2]; + } - return 1; + return 1; } __setup("wdt=", eurwdt_setup); @@ -127,7 +128,7 @@ MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `reboot')"); /* - * Programming support + * Programming support */ static void __init eurwdt_validate_timeout(void) @@ -135,265 +136,276 @@ static void __init eurwdt_validate_timeout(void) if (timeout < 0 || timeout > 255) { timeout = WDT_TIMEOUT; printk(KERN_INFO "eurwdt: timeout must be 0 < x < 255, using %d\n", - timeout); + timeout); } } static inline void eurwdt_write_reg(u8 index, u8 data) { - outb(index, io); - outb(data, io+1); + outb(index, io); + outb(data, io+1); } static inline void eurwdt_lock_chip(void) { - outb(0xaa, io); + outb(0xaa, io); } static inline void eurwdt_unlock_chip(void) { - outb(0x55, io); - eurwdt_write_reg(0x07, 0x08); /* set the logical device */ + outb(0x55, io); + eurwdt_write_reg(0x07, 0x08); /* set the logical device */ } static inline void eurwdt_set_timeout(int timeout) { - eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout); + eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout); } static inline void eurwdt_disable_timer(void) { - eurwdt_set_timeout(0); + eurwdt_set_timeout(0); } static void eurwdt_activate_timer(void) { - eurwdt_disable_timer(); - eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */ - eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? - WDT_EVENT_INT : WDT_EVENT_REBOOT); - /* Setting interrupt line */ - if (irq == 2 || irq > 15 || irq < 0) { - printk(KERN_ERR ": invalid irq number\n"); - irq = 0; /* if invalid we disable interrupt */ - } - if (irq == 0) - printk(KERN_INFO ": interrupt disabled\n"); - eurwdt_write_reg(WDT_TIMER_CFG, irq<<4); - - eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */ - eurwdt_set_timeout(0); /* the default timeout */ + eurwdt_disable_timer(); + eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */ + eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT); + + /* Setting interrupt line */ + if (irq == 2 || irq > 15 || irq < 0) { + printk(KERN_ERR ": invalid irq number\n"); + irq = 0; /* if invalid we disable interrupt */ + } + if (irq == 0) + printk(KERN_INFO ": interrupt disabled\n"); + + eurwdt_write_reg(WDT_TIMER_CFG, irq<<4); + + eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */ + eurwdt_set_timeout(0); /* the default timeout */ } /* - * Kernel methods. + * Kernel methods. */ void eurwdt_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - printk(KERN_CRIT "timeout WDT timeout\n"); + printk(KERN_CRIT "timeout WDT timeout\n"); #ifdef ONLY_TESTING - printk(KERN_CRIT "Would Reboot.\n"); + printk(KERN_CRIT "Would Reboot.\n"); #else - printk(KERN_CRIT "Initiating system reboot.\n"); - machine_restart(NULL); + printk(KERN_CRIT "Initiating system reboot.\n"); + machine_restart(NULL); #endif } /** - * eurwdt_ping: + * eurwdt_ping: * - * Reload counter one with the watchdog timeout. + * Reload counter one with the watchdog timeout. */ static void eurwdt_ping(void) { - /* Write the watchdog default value */ - eurwdt_set_timeout(timeout); + /* Write the watchdog default value */ + eurwdt_set_timeout(timeout); } /** - * eurwdt_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed + * eurwdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. */ static ssize_t eurwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - /* Can't seek (pwrite) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; - - if (count) { - eurwdt_ping(); /* the default timeout */ - return 1; - } + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (count) { + if (!nowayout) { + size_t i; + + eur_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if(get_user(c, buf+i)) + return -EFAULT; + if (c == 'V') + eur_expect_close = 42; + } + } + eurwdt_ping(); /* the default timeout */ + return 1; + } - return 0; + return 0; } /** - * eurwdt_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer + * eurwdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. */ static int eurwdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { - static struct watchdog_info ident = { - .options = WDIOF_CARDRESET, - .firmware_version = 1, - .identity = "WDT Eurotech CPU-1220/1410", - }; + static struct watchdog_info ident = { + .options = WDIOF_CARDRESET, + .firmware_version = 1, + .identity = "WDT Eurotech CPU-1220/1410", + }; - int time; + int time; - switch(cmd) { - default: - return -ENOTTY; + switch(cmd) { + default: + return -ENOTTY; - case WDIOC_GETSUPPORT: - return copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; - case WDIOC_GETBOOTSTATUS: - return put_user(0, (int *) arg); + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *) arg); - case WDIOC_KEEPALIVE: - eurwdt_ping(); - return 0; + case WDIOC_KEEPALIVE: + eurwdt_ping(); + return 0; - case WDIOC_SETTIMEOUT: - if (copy_from_user(&time, (int *) arg, sizeof(int))) - return -EFAULT; + case WDIOC_SETTIMEOUT: + if (copy_from_user(&time, (int *) arg, sizeof(int))) + return -EFAULT; - /* Sanity check */ - if (time < 0 || time > 255) - return -EINVAL; + /* Sanity check */ + if (time < 0 || time > 255) + return -EINVAL; - timeout = time; - eurwdt_set_timeout(time); - return 0; - } + timeout = time; + eurwdt_set_timeout(time); + return 0; + } } /** - * eurwdt_open: - * @inode: inode of device - * @file: file handle to device + * eurwdt_open: + * @inode: inode of device + * @file: file handle to device * - * The misc device has been opened. The watchdog device is single - * open and on opening we load the counter. + * The misc device has been opened. The watchdog device is single + * open and on opening we load the counter. */ static int eurwdt_open(struct inode *inode, struct file *file) { - switch (minor(inode->i_rdev)) { - case WATCHDOG_MINOR: - spin_lock(&eurwdt_lock); - if (eurwdt_is_open) { - spin_unlock(&eurwdt_lock); - return -EBUSY; - } - if (nowayout) { - MOD_INC_USE_COUNT; - } - - eurwdt_is_open = 1; - - /* Activate the WDT */ - eurwdt_activate_timer(); - - spin_unlock(&eurwdt_lock); - - MOD_INC_USE_COUNT; - - return 0; - - case TEMP_MINOR: - return 0; - - default: - return -ENODEV; - } + switch (minor(inode->i_rdev)) { + case WATCHDOG_MINOR: + spin_lock(&eurwdt_lock); + if (eurwdt_is_open) { + spin_unlock(&eurwdt_lock); + return -EBUSY; + } + if (nowayout) + MOD_INC_USE_COUNT; + + eurwdt_is_open = 1; + + /* Activate the WDT */ + eurwdt_activate_timer(); + + spin_unlock(&eurwdt_lock); + + MOD_INC_USE_COUNT; + return 0; + + case TEMP_MINOR: + return 0; + + default: + return -ENODEV; + } } /** - * eurwdt_release: - * @inode: inode to board - * @file: file handle to board + * eurwdt_release: + * @inode: inode to board + * @file: file handle to board * - * The watchdog has a configurable API. There is a religious dispute - * between people who want their watchdog to be able to shut down and - * those who want to be sure if the watchdog manager dies the machine - * reboots. In the former case we disable the counters, in the latter - * case you have to open it again very soon. + * The watchdog has a configurable API. There is a religious dispute + * between people who want their watchdog to be able to shut down and + * those who want to be sure if the watchdog manager dies the machine + * reboots. In the former case we disable the counters, in the latter + * case you have to open it again very soon. */ static int eurwdt_release(struct inode *inode, struct file *file) { - if (minor(inode->i_rdev) == WATCHDOG_MINOR) { - if (!nowayout) { - eurwdt_disable_timer(); - } - eurwdt_is_open = 0; + if (minor(inode->i_rdev) == WATCHDOG_MINOR) { + if (!nowayout) + eurwdt_disable_timer(); - MOD_DEC_USE_COUNT; - } + eurwdt_is_open = 0; + MOD_DEC_USE_COUNT; + } - return 0; + return 0; } /** - * eurwdt_notify_sys: - * @this: our notifier block - * @code: the event being reported - * @unused: unused + * eurwdt_notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused * - * Our notifier is called on system shutdowns. We want to turn the card - * off at reboot otherwise the machine will reboot again during memory - * test or worse yet during the following fsck. This would suck, in fact - * trust me - if it happens it does suck. + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. */ static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) + void *unused) { - if (code == SYS_DOWN || code == SYS_HALT) { - /* Turn the card off */ - eurwdt_disable_timer(); - } + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the card off */ + eurwdt_disable_timer(); + } - return NOTIFY_DONE; + return NOTIFY_DONE; } /* - * Kernel Interfaces + * Kernel Interfaces */ static struct file_operations eurwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = eurwdt_write, - .ioctl = eurwdt_ioctl, - .open = eurwdt_open, - .release = eurwdt_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = eurwdt_write, + .ioctl = eurwdt_ioctl, + .open = eurwdt_open, + .release = eurwdt_release, }; static struct miscdevice eurwdt_miscdev = { @@ -403,8 +415,8 @@ static struct miscdevice eurwdt_miscdev = { }; /* - * The WDT card needs to learn about soft shutdowns in order to - * turn the timebomb registers off. + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. */ static struct notifier_block eurwdt_notifier = { @@ -412,85 +424,85 @@ static struct notifier_block eurwdt_notifier = { }; /** - * cleanup_module: + * cleanup_module: * - * Unload the watchdog. You cannot do this with any file handles open. - * If your watchdog is set to continue ticking on close and you unload - * it, well it keeps ticking. We won't get the interrupt but the board - * will not touch PC memory so all is fine. You just have to load a new - * module in 60 seconds or reboot. + * Unload the watchdog. You cannot do this with any file handles open. + * If your watchdog is set to continue ticking on close and you unload + * it, well it keeps ticking. We won't get the interrupt but the board + * will not touch PC memory so all is fine. You just have to load a new + * module in 60 seconds or reboot. */ static void __exit eurwdt_exit(void) { - eurwdt_lock_chip(); + eurwdt_lock_chip(); - misc_deregister(&eurwdt_miscdev); + misc_deregister(&eurwdt_miscdev); - unregister_reboot_notifier(&eurwdt_notifier); - release_region(io, 2); - free_irq(irq, NULL); + unregister_reboot_notifier(&eurwdt_notifier); + release_region(io, 2); + free_irq(irq, NULL); } /** - * eurwdt_init: + * eurwdt_init: * - * Set up the WDT watchdog board. After grabbing the resources - * we require we need also to unlock the device. - * The open() function will actually kick the board off. + * Set up the WDT watchdog board. After grabbing the resources + * we require we need also to unlock the device. + * The open() function will actually kick the board off. */ static int __init eurwdt_init(void) { - int ret; - - eurwdt_validate_timeout(); - ret = misc_register(&eurwdt_miscdev); - if (ret) { - printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", - WATCHDOG_MINOR); - goto out; - } - - ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL); - if(ret) { - printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); - goto outmisc; - } - - if (!request_region(io, 2, "eurwdt")) { - printk(KERN_ERR "eurwdt: IO %X is not free.\n", io); - ret = -EBUSY; - goto outirq; - } - - ret = register_reboot_notifier(&eurwdt_notifier); - if (ret) { - printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret); - goto outreg; - } - - eurwdt_unlock_chip(); - - ret = 0; - printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" - " - timeout event: %s\n", - io, irq, (!strcmp("int", ev) ? "int" : "reboot")); - - spin_lock_init(&eurwdt_lock); - - out: - return ret; - - outreg: - release_region(io, 2); - - outirq: - free_irq(irq, NULL); - - outmisc: - misc_deregister(&eurwdt_miscdev); - goto out; + int ret; + + eurwdt_validate_timeout(); + ret = misc_register(&eurwdt_miscdev); + if (ret) { + printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", + WATCHDOG_MINOR); + goto out; + } + + ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL); + if(ret) { + printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); + goto outmisc; + } + + if (!request_region(io, 2, "eurwdt")) { + printk(KERN_ERR "eurwdt: IO %X is not free.\n", io); + ret = -EBUSY; + goto outirq; + } + + ret = register_reboot_notifier(&eurwdt_notifier); + if (ret) { + printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret); + goto outreg; + } + + eurwdt_unlock_chip(); + + ret = 0; + printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" + " - timeout event: %s\n", + io, irq, (!strcmp("int", ev) ? "int" : "reboot")); + + spin_lock_init(&eurwdt_lock); + +out: + return ret; + +outreg: + release_region(io, 2); + +outirq: + free_irq(irq, NULL); + +outmisc: + misc_deregister(&eurwdt_miscdev); + goto out; } module_init(eurwdt_init); diff --git a/drivers/char/watchdog/i810-tco.c b/drivers/char/watchdog/i810-tco.c index 3697dd0f81b3..f24465c0a8d5 100644 --- a/drivers/char/watchdog/i810-tco.c +++ b/drivers/char/watchdog/i810-tco.c @@ -244,7 +244,9 @@ static int i810tco_ioctl (struct inode *inode, struct file *file, int options, retval = -EINVAL; static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, .firmware_version = 0, .identity = "i810 TCO timer", }; diff --git a/drivers/char/watchdog/ib700wdt.c b/drivers/char/watchdog/ib700wdt.c index 448c7be4e717..68a9454a6dae 100644 --- a/drivers/char/watchdog/ib700wdt.c +++ b/drivers/char/watchdog/ib700wdt.c @@ -54,6 +54,7 @@ static int ibwdt_is_open; static spinlock_t ibwdt_lock; +static int expect_close = 0; /* * @@ -136,6 +137,21 @@ ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) return -ESPIPE; if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + 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; + } + } ibwdt_ping(); return 1; } @@ -153,7 +169,9 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING, 1, "IB700 WDT" + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "IB700 WDT" }; switch (cmd) { @@ -180,42 +198,38 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int ibwdt_open(struct inode *inode, struct file *file) { - switch (minor(inode->i_rdev)) { - case WATCHDOG_MINOR: - spin_lock(&ibwdt_lock); - if (ibwdt_is_open) { - spin_unlock(&ibwdt_lock); - return -EBUSY; - } - if (nowayout) { - MOD_INC_USE_COUNT; - } - /* - * Activate - */ - - ibwdt_is_open = 1; - ibwdt_ping(); + if (minor(inode->i_rdev) == WATCHDOG_MINOR) { + spin_lock(&ibwdt_lock); + if (ibwdt_is_open) { spin_unlock(&ibwdt_lock); - return 0; - default: - return -ENODEV; + return -EBUSY; + } + if (nowayout) + MOD_INC_USE_COUNT; + + /* Activate */ + ibwdt_is_open = 1; + ibwdt_ping(); + spin_unlock(&ibwdt_lock); + return 0; + } else { + return -ENODEV; } } static int ibwdt_close(struct inode *inode, struct file *file) { - lock_kernel(); if (minor(inode->i_rdev) == WATCHDOG_MINOR) { spin_lock(&ibwdt_lock); - if (!nowayout) { + if (expect_close) outb_p(timeout_val, WDT_STOP); - } + else + printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n"); + ibwdt_is_open = 0; spin_unlock(&ibwdt_lock); } - unlock_kernel(); return 0; } @@ -248,9 +262,9 @@ static struct file_operations ibwdt_fops = { }; static struct miscdevice ibwdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &ibwdt_fops + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ibwdt_fops }; /* @@ -259,9 +273,9 @@ static struct miscdevice ibwdt_miscdev = { */ static struct notifier_block ibwdt_notifier = { - ibwdt_notify_sys, - NULL, - 0 + .notifier_call = ibwdt_notify_sys, + .next = NULL, + .priority = 0 }; static int __init diff --git a/drivers/char/watchdog/indydog.c b/drivers/char/watchdog/indydog.c new file mode 100644 index 000000000000..c41dac5b6b07 --- /dev/null +++ b/drivers/char/watchdog/indydog.c @@ -0,0 +1,182 @@ +/* + * IndyDog 0.2 A Hardware Watchdog Device for SGI IP22 + * + * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * based on softdog.c by Alan Cox <alan@redhat.com> + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <asm/sgi/sgimc.h> + +static unsigned long indydog_alive; +static struct sgimc_misc_ctrl *mcmisc_regs; +static int expect_close = 0; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +static void indydog_ping() +{ + mcmisc_regs->watchdogt = 0; +} + + +/* + * Allow only one person to hold it open + */ + +static int indydog_open(struct inode *inode, struct file *file) +{ + u32 mc_ctrl0; + + if( test_and_set_bit(0,&indydog_alive) ) + return -EBUSY; + + if (nowayout) + MOD_INC_USE_COUNT; + + /* + * Activate timer + */ + mcmisc_regs = (struct sgimc_misc_ctrl *)(KSEG1+0x1fa00000); + + mc_ctrl0 = mcmisc_regs->cpuctrl0 | SGIMC_CCTRL0_WDOG; + mcmisc_regs->cpuctrl0 = mc_ctrl0; + indydog_ping(); + + printk("Started watchdog timer.\n"); + return 0; +} + +static int indydog_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + + if (expect_close) { + u32 mc_ctrl0 = mcmisc_regs->cpuctrl0; + mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; + mcmisc_regs->cpuctrl0 = mc_ctrl0; + printk("Stopped watchdog timer.\n"); + } else { + printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n"); + } + clear_bit(0,&indydog_alive); + return 0; +} + +static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* Refresh the timer. */ + if (len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } + indydog_ping(); + return 1; + } + return 0; +} + +static int indydog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE, + .identity = "Hardware Watchdog for SGI IP22", + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0,(int *)arg); + case WDIOC_KEEPALIVE: + indydog_ping(); + return 0; + } +} + +static struct file_operations indydog_fops = { + .owner = THIS_MODULE, + .write = indydog_write, + .ioctl = indydog_ioctl, + .open = indydog_open, + .release= indydog_release, +}; + +static struct miscdevice indydog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &indydog_fops, +}; + +static const char banner[] __initdata = KERN_INFO "Hardware Watchdog Timer for SGI IP22: 0.2\n"; + +static int __init watchdog_init(void) +{ + int ret; + + ret = misc_register(&indydog_miscdev); + + if (ret) + return ret; + + printk(banner); + + return 0; +} + +static void __exit watchdog_exit(void) +{ + misc_deregister(&indydog_miscdev); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c index ab3f538d2d72..3d78c2f37ec6 100644 --- a/drivers/char/watchdog/machzwd.c +++ b/drivers/char/watchdog/machzwd.c @@ -117,7 +117,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON #define PFX "machzwd" static struct watchdog_info zf_info = { - .options = WDIOF_KEEPALIVEPING, + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "ZF-Logic watchdog" }; @@ -314,10 +314,10 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, /* See if we got the magic character */ if(count){ -/* - * no need to check for close confirmation - * no way to disable watchdog ;) - */ + /* + * no need to check for close confirmation + * no way to disable watchdog ;) + */ if (!nowayout) { size_t ofs; @@ -328,7 +328,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, zf_expect_close = 0; /* now scan */ - for(ofs = 0; ofs != count; ofs++){ + for (ofs = 0; ofs != count; ofs++){ char c; if (get_user(c, buf + ofs)) return -EFAULT; @@ -338,6 +338,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, } } } + /* * Well, anyhow someone wrote to us, * we should return that favour @@ -395,9 +396,9 @@ static int zf_open(struct inode *inode, struct file *file) return -EBUSY; } - if (nowayout) { + if (nowayout) MOD_INC_USE_COUNT; - } + zf_is_open = 1; spin_unlock(&zf_lock); diff --git a/drivers/char/watchdog/mixcomwd.c b/drivers/char/watchdog/mixcomwd.c index 6ee6576e6528..f19a5d9230ef 100644 --- a/drivers/char/watchdog/mixcomwd.c +++ b/drivers/char/watchdog/mixcomwd.c @@ -62,6 +62,7 @@ static long mixcomwd_opened; /* long req'd for setbit --RR */ static int watchdog_port; static int mixcomwd_timer_alive; static struct timer_list mixcomwd_timer = TIMER_INITIALIZER(NULL, 0, 0); +static int expect_close = 0; #ifdef CONFIG_WATCHDOG_NOWAYOUT static int nowayout = 1; @@ -109,8 +110,7 @@ static int mixcomwd_open(struct inode *inode, struct file *file) static int mixcomwd_release(struct inode *inode, struct file *file) { - - if (!nowayout) { + if (expect_close) { if(mixcomwd_timer_alive) { printk(KERN_ERR "mixcomwd: release called while internal timer alive"); return -EBUSY; @@ -121,7 +121,10 @@ static int mixcomwd_release(struct inode *inode, struct file *file) mixcomwd_timer.data=0; mixcomwd_timer_alive=1; add_timer(&mixcomwd_timer); + } else { + printk(KERN_CRIT "mixcomwd: WDT device closed unexpectedly. WDT will not stop!\n"); } + clear_bit(0,&mixcomwd_opened); return 0; } @@ -135,6 +138,20 @@ static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, l if(len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } mixcomwd_ping(); return 1; } @@ -145,8 +162,10 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int status; - static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog" + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "MixCOM watchdog" }; switch(cmd) diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index 4fdd439aa06b..be2bcd00c5fa 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c @@ -40,6 +40,8 @@ * fairly useless proc entry. * 990610 removed said useless proc code for the merge <alan> * 000403 Removed last traces of proc code. <davej> + * 011214 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com> + * Added timeout module option to override default */ #include <linux/module.h> @@ -76,7 +78,7 @@ */ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; -#define WD_VER "1.10 (06/05/99)" +#define WD_VER "1.12 (12/14/2001)" /* * It should be noted that PCWD_REVISION_B was removed because A and B @@ -88,7 +90,23 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; #define PCWD_REVISION_A 1 #define PCWD_REVISION_C 2 -#define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */ +#define WD_TIMEOUT 4 /* 2 seconds for a timeout */ +static int timeout_val = WD_TIMEOUT; +static int timeout = 2; +static int expect_close = 0; + +MODULE_PARM(timeout,"i"); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=2)"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + /* * These are the defines for the PC Watchdog card, revision A. @@ -121,7 +139,7 @@ static int __init pcwd_checkcard(void) if (prev_card_dat == 0xFF) return 0; - while(count < WD_TIMEOUT) { + while(count < timeout_val) { /* Read the raw card data from the port, and strip off the first 4 bits */ @@ -256,7 +274,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, else cdat = inb(current_readport + 1 ); spin_unlock(&io_lock); - rv = 0; + rv = WDIOF_MAGICCLOSE; if (revision == PCWD_REVISION_A) { @@ -385,8 +403,22 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, if (ppos != &file->f_pos) return -ESPIPE; - if (len) - { + if (len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } pcwd_send_heartbeat(); return 1; } @@ -395,28 +427,26 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, static int pcwd_open(struct inode *ino, struct file *filep) { - switch (minor(ino->i_rdev)) - { - case WATCHDOG_MINOR: - if ( !atomic_dec_and_test(&open_allowed) ) - { + switch (minor(ino->i_rdev)) { + case WATCHDOG_MINOR: + if (!atomic_dec_and_test(&open_allowed) ) { atomic_inc( &open_allowed ); - return -EBUSY; - } - MOD_INC_USE_COUNT; - /* Enable the port */ - if (revision == PCWD_REVISION_C) - { - spin_lock(&io_lock); - outb_p(0x00, current_readport + 3); - spin_unlock(&io_lock); - } - return(0); - case TEMP_MINOR: - return(0); - default: - return (-ENODEV); - } + return -EBUSY; + } + MOD_INC_USE_COUNT; + /* Enable the port */ + if (revision == PCWD_REVISION_C) { + spin_lock(&io_lock); + outb_p(0x00, current_readport + 3); + spin_unlock(&io_lock); + } + return(0); + + case TEMP_MINOR: + return(0); + default: + return (-ENODEV); + } } static ssize_t pcwd_read(struct file *file, char *buf, size_t count, @@ -448,18 +478,17 @@ static ssize_t pcwd_read(struct file *file, char *buf, size_t count, static int pcwd_close(struct inode *ino, struct file *filep) { - if (minor(ino->i_rdev)==WATCHDOG_MINOR) - { -#ifndef CONFIG_WATCHDOG_NOWAYOUT - /* Disable the board */ - if (revision == PCWD_REVISION_C) { - spin_lock(&io_lock); - outb_p(0xA5, current_readport + 3); - outb_p(0xA5, current_readport + 3); - spin_unlock(&io_lock); + if (minor(ino->i_rdev)==WATCHDOG_MINOR) { + if (expect_close) { + /* Disable the board */ + if (revision == PCWD_REVISION_C) { + spin_lock(&io_lock); + outb_p(0xA5, current_readport + 3); + outb_p(0xA5, current_readport + 3); + spin_unlock(&io_lock); + } + atomic_inc( &open_allowed ); } - atomic_inc( &open_allowed ); -#endif } return 0; } @@ -560,9 +589,15 @@ static struct miscdevice temp_miscdev = { &pcwd_fops }; +static void __init pcwd_validate_timeout(void) +{ + timeout_val = timeout * 2; +} + static int __init pcwatchdog_init(void) { int i, found = 0; + pcwd_validate_timeout(); spin_lock_init(&io_lock); revision = PCWD_REVISION_A; diff --git a/drivers/char/watchdog/sbc60xxwdt.c b/drivers/char/watchdog/sbc60xxwdt.c index e6799e4ee4ae..4b61d1a8db40 100644 --- a/drivers/char/watchdog/sbc60xxwdt.c +++ b/drivers/char/watchdog/sbc60xxwdt.c @@ -244,9 +244,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { static struct watchdog_info ident= { - 0, - 1, - "SB60xx" + .options = WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "SB60xx" }; switch(cmd) diff --git a/drivers/char/watchdog/sc1200wdt.c b/drivers/char/watchdog/sc1200wdt.c new file mode 100644 index 000000000000..005cdaa5c4cf --- /dev/null +++ b/drivers/char/watchdog/sc1200wdt.c @@ -0,0 +1,479 @@ +/* + * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver + * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>, + * All Rights Reserved. + * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The author(s) of this software shall not be held liable for damages + * of any nature resulting due to the use of this software. This + * software is provided AS-IS with no warranties. + * + * Changelog: + * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware. + * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox. + * 20020222 Zwane Mwaikambo Added probing. + * 20020225 Zwane Mwaikambo Added ISAPNP support. + * 20020412 Rob Radez Broke out start/stop functions + * <rob@osinvestor.com> Return proper status instead of temperature warning + * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls + * Fix CONFIG_WATCHDOG_NOWAYOUT + * 20020530 Joel Becker Add Matt Domsch's nowayout module option + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <asm/semaphore.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/isapnp.h> +#include <linux/pci.h> + +#define SC1200_MODULE_VER "build 20020303" +#define SC1200_MODULE_NAME "sc1200wdt" +#define PFX SC1200_MODULE_NAME ": " + +#define MAX_TIMEOUT 255 /* 255 minutes */ +#define PMIR (io) /* Power Management Index Register */ +#define PMDR (io+1) /* Power Management Data Register */ + +/* Data Register indexes */ +#define FER1 0x00 /* Function enable register 1 */ +#define FER2 0x01 /* Function enable register 2 */ +#define PMC1 0x02 /* Power Management Ctrl 1 */ +#define PMC2 0x03 /* Power Management Ctrl 2 */ +#define PMC3 0x04 /* Power Management Ctrl 3 */ +#define WDTO 0x05 /* Watchdog timeout register */ +#define WDCF 0x06 /* Watchdog config register */ +#define WDST 0x07 /* Watchdog status register */ + +/* WDCF bitfields - which devices assert WDO */ +#define KBC_IRQ 0x01 /* Keyboard Controller */ +#define MSE_IRQ 0x02 /* Mouse */ +#define UART1_IRQ 0x03 /* Serial0 */ +#define UART2_IRQ 0x04 /* Serial1 */ +/* 5 -7 are reserved */ + +static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER; +static int timeout = 1; +static int io = -1; +static int io_len = 2; /* for non plug and play */ +struct semaphore open_sem; +static char expect_close; +spinlock_t sc1200wdt_lock; /* io port access serialisation */ + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE +static int isapnp = 1; +static struct pci_dev *wdt_dev; + +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); +#endif + +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "io port"); +MODULE_PARM(timeout, "i"); +MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + + + +/* Read from Data Register */ +static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data) +{ + spin_lock(&sc1200wdt_lock); + outb_p(index, PMIR); + *data = inb(PMDR); + spin_unlock(&sc1200wdt_lock); +} + + +/* Write to Data Register */ +static inline void sc1200wdt_write_data(unsigned char index, unsigned char data) +{ + spin_lock(&sc1200wdt_lock); + outb_p(index, PMIR); + outb(data, PMDR); + spin_unlock(&sc1200wdt_lock); +} + + +static void sc1200wdt_start(void) +{ + unsigned char reg; + + sc1200wdt_read_data(WDCF, ®); + /* assert WDO when any of the following interrupts are triggered too */ + reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); + sc1200wdt_write_data(WDCF, reg); + /* set the timeout and get the ball rolling */ + sc1200wdt_write_data(WDTO, timeout); +} + + +static void sc1200wdt_stop(void) +{ + sc1200wdt_write_data(WDTO, 0); +} + + +/* This returns the status of the WDO signal, inactive high. */ +static inline int sc1200wdt_status(void) +{ + unsigned char ret; + + sc1200wdt_read_data(WDST, &ret); + /* If the bit is inactive, the watchdog is enabled, so return + * KEEPALIVEPING which is a bit of a kludge because there's nothing + * else for enabled/disabled status + */ + return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */ +} + + +static int sc1200wdt_open(struct inode *inode, struct file *file) +{ + /* allow one at a time */ + if (down_trylock(&open_sem)) + return -EBUSY; + + if (timeout > MAX_TIMEOUT) + timeout = MAX_TIMEOUT; + + sc1200wdt_start(); + printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); + + return 0; +} + + +static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int new_timeout; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "PC87307/PC97307" + }; + + switch (cmd) { + default: + return -ENOTTY; /* Keep Pavel Machek amused ;) */ + + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident)) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + return put_user(sc1200wdt_status(), (int *)arg); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + + case WDIOC_KEEPALIVE: + sc1200wdt_write_data(WDTO, timeout); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, (int *)arg)) + return -EFAULT; + + /* the API states this is given in secs */ + new_timeout /= 60; + if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) + return -EINVAL; + + timeout = new_timeout; + sc1200wdt_write_data(WDTO, timeout); + /* fall through and return the new timeout */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout * 60, (int *)arg); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + sc1200wdt_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + sc1200wdt_start(); + retval = 0; + } + + return retval; + } + } +} + + +static int sc1200wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + sc1200wdt_stop(); + printk(KERN_INFO PFX "Watchdog disabled\n"); + } else { + sc1200wdt_write_data(WDTO, timeout); + printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout); + } + up(&open_sem); + expect_close = 0; + + return 0; +} + + +static ssize_t sc1200wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (len) { + if (!nowayout) { + size_t i; + + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + + sc1200wdt_write_data(WDTO, timeout); + return len; + } + + return 0; +} + + +static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + sc1200wdt_stop(); + + return NOTIFY_DONE; +} + + +static struct notifier_block sc1200wdt_notifier = +{ + notifier_call: sc1200wdt_notify_sys +}; + +static struct file_operations sc1200wdt_fops = +{ + owner: THIS_MODULE, + write: sc1200wdt_write, + ioctl: sc1200wdt_ioctl, + open: sc1200wdt_open, + release: sc1200wdt_release +}; + +static struct miscdevice sc1200wdt_miscdev = +{ + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &sc1200wdt_fops, +}; + + +static int __init sc1200wdt_probe(void) +{ + /* The probe works by reading the PMC3 register's default value of 0x0e + * there is one caveat, if the device disables the parallel port or any + * of the UARTs we won't be able to detect it. + * Nb. This could be done with accuracy by reading the SID registers, but + * we don't have access to those io regions. + */ + + unsigned char reg; + + sc1200wdt_read_data(PMC3, ®); + reg &= 0x0f; /* we don't want the UART busy bits */ + return (reg == 0x0e) ? 0 : -ENODEV; +} + + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + +static int __init sc1200wdt_isapnp_probe(void) +{ + int ret; + + /* The WDT is logical device 8 on the main device */ + wdt_dev = isapnp_find_dev(NULL, ISAPNP_VENDOR('N','S','C'), ISAPNP_FUNCTION(0x08), NULL); + if (!wdt_dev) + return -ENODEV; + + if (wdt_dev->prepare(wdt_dev) < 0) { + printk(KERN_ERR PFX "ISA PnP found device that could not be autoconfigured\n"); + return -EAGAIN; + } + + if (!(pci_resource_flags(wdt_dev, 0) & IORESOURCE_IO)) { + printk(KERN_ERR PFX "ISA PnP could not find io ports\n"); + return -ENODEV; + } + + ret = wdt_dev->activate(wdt_dev); + if (ret && (ret != -EBUSY)) + return -ENOMEM; + + /* io port resource overriding support? */ + io = pci_resource_start(wdt_dev, 0); + io_len = pci_resource_len(wdt_dev, 0); + + printk(KERN_DEBUG PFX "ISA PnP found device at io port %#x/%d\n", io, io_len); + return 0; +} + +#endif /* CONFIG_ISAPNP */ + + +static int __init sc1200wdt_init(void) +{ + int ret; + + printk(banner); + + spin_lock_init(&sc1200wdt_lock); + sema_init(&open_sem, 1); + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + if (isapnp) { + ret = sc1200wdt_isapnp_probe(); + if (ret) + goto out_clean; + } +#endif + + if (io == -1) { + printk(KERN_ERR PFX "io parameter must be specified\n"); + ret = -EINVAL; + goto out_clean; + } + + if (!request_region(io, io_len, SC1200_MODULE_NAME)) { + printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); + ret = -EBUSY; + goto out_pnp; + } + + ret = sc1200wdt_probe(); + if (ret) + goto out_io; + + ret = register_reboot_notifier(&sc1200wdt_notifier); + if (ret) { + printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret); + goto out_io; + } + + ret = misc_register(&sc1200wdt_miscdev); + if (ret) { + printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); + goto out_rbt; + } + + /* ret = 0 */ + +out_clean: + return ret; + +out_rbt: + unregister_reboot_notifier(&sc1200wdt_notifier); + +out_io: + release_region(io, io_len); + +out_pnp: +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + if (isapnp && wdt_dev) + wdt_dev->deactivate(wdt_dev); +#endif + goto out_clean; +} + + +static void __exit sc1200wdt_exit(void) +{ + misc_deregister(&sc1200wdt_miscdev); + unregister_reboot_notifier(&sc1200wdt_notifier); + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + if(isapnp && wdt_dev) + wdt_dev->deactivate(wdt_dev); +#endif + + release_region(io, io_len); +} + + +#ifndef MODULE +static int __init sc1200wdt_setup(char *str) +{ + int ints[4]; + + str = get_options (str, ARRAY_SIZE(ints), ints); + + if (ints[0] > 0) { + io = ints[1]; + if (ints[0] > 1) + timeout = ints[2]; + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + if (ints[0] > 2) + isapnp = ints[3]; +#endif + } + + return 1; +} + +__setup("sc1200wdt=", sc1200wdt_setup); +#endif /* MODULE */ + + +module_init(sc1200wdt_init); +module_exit(sc1200wdt_exit); + +MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>"); +MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; + diff --git a/drivers/char/watchdog/sc520_wdt.c b/drivers/char/watchdog/sc520_wdt.c new file mode 100644 index 000000000000..1d30c4ef4f29 --- /dev/null +++ b/drivers/char/watchdog/sc520_wdt.c @@ -0,0 +1,394 @@ +/* + * AMD Elan SC520 processor Watchdog Timer driver for Linux 2.4.x + * + * Based on acquirewdt.c by Alan Cox, + * and sbc60xxwdt.c by Jakob Oestergaard <jakob@ostenfeld.dk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The authors do NOT admit liability nor provide warranty for + * any of this software. This material is provided "AS-IS" in + * the hope that it may be useful for others. + * + * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net> + * 9/27 - 2001 [Initial release] + * + * Additional fixes Alan Cox + * - Fixed formatting + * - Removed debug printks + * - Fixed SMP built kernel deadlock + * - Switched to private locks not lock_kernel + * - Used ioremap/writew/readw + * - Added NOWAYOUT support + * + * Theory of operation: + * A Watchdog Timer (WDT) is a hardware circuit that can + * reset the computer system in case of a software fault. + * You probably knew that already. + * + * Usually a userspace daemon will notify the kernel WDT driver + * via the /proc/watchdog special device file that userspace is + * still alive, at regular intervals. When such a notification + * occurs, the driver will usually tell the hardware watchdog + * that everything is in order, and that the watchdog should wait + * for yet another little while to reset the system. + * If userspace fails (RAM error, kernel bug, whatever), the + * notifications cease to occur, and the hardware watchdog will + * reset the system (causing a reboot) after the timeout occurs. + * + * This WDT driver is different from most other Linux WDT + * drivers in that the driver will ping the watchdog by itself, + * because this particular WDT has a very short timeout (1.6 + * seconds) and it would be insane to count on any userspace + * daemon always getting scheduled within that time frame. + * + * This driver uses memory mapped IO, and spinlock. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/fcntl.h> +#include <linux/smp_lock.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/init.h> + +/* + * The SC520 can timeout anywhere from 492us to 32.21s. + * If we reset the watchdog every ~250ms we should be safe. + */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + */ + +#define WDT_HEARTBEAT (HZ * 30) + +/* + * AMD Elan SC520 timeout value is 492us times a power of 2 (0-7) + * + * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s + * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s + */ + +#define TIMEOUT_EXPONENT ( 1 << 3 ) /* 0x08 = 2.01s */ + +/* #define MMCR_BASE_DEFAULT 0xfffef000 */ +#define MMCR_BASE_DEFAULT ((__u16 *)0xffffe) +#define OFFS_WDTMRCTL ((unsigned int)0xcb0) +#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */ +#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */ + +#define OUR_NAME "sc520_wdt" + +#define WRT_DOG(data) *wdtmrctl=data + +static __u16 *wdtmrctl; + +static void wdt_timer_ping(unsigned long); +static struct timer_list timer; +static unsigned long next_heartbeat; +static unsigned long wdt_is_open; +static int wdt_expect_close; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +static spinlock_t wdt_spinlock; +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT */ + spin_lock(&wdt_spinlock); + writew(0xAAAA, wdtmrctl); + writew(0x5555, wdtmrctl); + spin_unlock(&wdt_spinlock); + + /* Re-set the timer interval */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); + } else { + printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + } +} + +/* + * Utility routines + */ + +static void wdt_config(int writeval) +{ + __u16 dummy; + unsigned long flags; + + /* buy some time (ping) */ + spin_lock_irqsave(&wdt_spinlock, flags); + dummy=readw(wdtmrctl); /* ensure write synchronization */ + writew(0xAAAA, wdtmrctl); + writew(0x5555, wdtmrctl); + /* make WDT configuration register writable one time */ + writew(0x3333, wdtmrctl); + writew(0xCCCC, wdtmrctl); + /* write WDT configuration register */ + writew(writeval, wdtmrctl); + spin_unlock_irqrestore(&wdt_spinlock, flags); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + WDT_HEARTBEAT; + + /* Start the timer */ + timer.expires = jiffies + WDT_INTERVAL; + add_timer(&timer); + + wdt_config(WDT_ENB | WDT_WRST_ENB | TIMEOUT_EXPONENT); + printk(OUR_NAME ": Watchdog timer is now enabled.\n"); +} + +static void wdt_turnoff(void) +{ + if (!nowayout) { + /* Stop the timer */ + del_timer(&timer); + wdt_config(0); + printk(OUR_NAME ": Watchdog timer is now disabled...\n"); + } +} + + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos) +{ + /* We can't seek */ + if(ppos != &file->f_pos) + return -ESPIPE; + + /* See if we got the magic character */ + if(count) + { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* now scan */ + for(ofs = 0; ofs != count; ofs++) { + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if(c == 'V') + wdt_expect_close = 1; + } + + /* Well, anyhow someone wrote to us, we should return that favour */ + next_heartbeat = jiffies + WDT_HEARTBEAT; + return 1; + } + return 0; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + switch(minor(inode->i_rdev)) + { + case WATCHDOG_MINOR: + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* Good, fire up the show */ + wdt_startup(); + if (nowayout) + MOD_INC_USE_COUNT; + + return 0; + default: + return -ENODEV; + } +} + +static int fop_close(struct inode * inode, struct file * file) +{ + if(minor(inode->i_rdev) == WATCHDOG_MINOR) + { + if(wdt_expect_close) + wdt_turnoff(); + else { + del_timer(&timer); + printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); + } + } + clear_bit(0, &wdt_is_open); + return 0; +} + +static long long fop_llseek(struct file *file, long long offset, int origin) +{ + return -ESPIPE; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + static struct watchdog_info ident= + { + .options = WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "SC520" + }; + + switch(cmd) + { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; + case WDIOC_KEEPALIVE: + next_heartbeat = jiffies + WDT_HEARTBEAT; + return 0; + } +} + +static struct file_operations wdt_fops = { + owner: THIS_MODULE, + llseek: fop_llseek, + write: fop_write, + open: fop_open, + release: fop_close, + ioctl: fop_ioctl +}; + +static struct miscdevice wdt_miscdev = { + WATCHDOG_MINOR, + "watchdog", + &wdt_fops +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier= +{ + wdt_notify_sys, + 0, + 0 +}; + +static void __exit sc520_wdt_unload(void) +{ + wdt_turnoff(); + + /* Deregister */ + misc_deregister(&wdt_miscdev); + iounmap(wdtmrctl); + unregister_reboot_notifier(&wdt_notifier); +} + +static int __init sc520_wdt_init(void) +{ + int rc = -EBUSY; + unsigned long cbar; + + spin_lock_init(&wdt_spinlock); + + init_timer(&timer); + timer.function = wdt_timer_ping; + timer.data = 0; + + rc = misc_register(&wdt_miscdev); + if (rc) + goto err_out_region2; + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) + goto err_out_miscdev; + + /* get the Base Address Register */ + cbar = inl_p(0xfffc); + printk(OUR_NAME ": CBAR: 0x%08lx\n", cbar); + /* check if MMCR aliasing bit is set */ + if (cbar & 0x80000000) { + printk(OUR_NAME ": MMCR Aliasing enabled.\n"); + wdtmrctl = (__u16 *)(cbar & 0x3fffffff); + } else { + printk(OUR_NAME "!!! WARNING !!!\n" + "\t MMCR Aliasing found NOT enabled!\n" + "\t Using default value of: %p\n" + "\t This has not been tested!\n" + "\t Please email Scott Jennings <smj@oro.net>\n" + "\t and Bill Jennings <bj@oro.net> if it works!\n" + , MMCR_BASE_DEFAULT + ); + wdtmrctl = MMCR_BASE_DEFAULT; + } + + wdtmrctl = (__u16 *)((char *)wdtmrctl + OFFS_WDTMRCTL); + wdtmrctl = ioremap((unsigned long)wdtmrctl, 2); + printk(KERN_INFO OUR_NAME ": WDT driver for SC520 initialised.\n"); + + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out_region2: + return rc; +} + +module_init(sc520_wdt_init); +module_exit(sc520_wdt_unload); + +MODULE_AUTHOR("Scott and Bill Jennings"); +MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c index 8761e4a92736..2f32ebc5708e 100644 --- a/drivers/char/watchdog/shwdt.c +++ b/drivers/char/watchdog/shwdt.c @@ -334,21 +334,21 @@ static struct file_operations sh_wdt_fops = { }; static struct watchdog_info sh_wdt_info = { - WDIOF_KEEPALIVEPING, - 1, - "SH WDT", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "SH WDT", }; static struct notifier_block sh_wdt_notifier = { - sh_wdt_notify_sys, - NULL, - 0 + .notifier_call = sh_wdt_notify_sys, + .next = NULL, + .priority = 0 }; static struct miscdevice sh_wdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &sh_wdt_fops, + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops &sh_wdt_fops, }; /** diff --git a/drivers/char/watchdog/softdog.c b/drivers/char/watchdog/softdog.c index 6894bb034fc1..7abbabad53fc 100644 --- a/drivers/char/watchdog/softdog.c +++ b/drivers/char/watchdog/softdog.c @@ -47,6 +47,7 @@ #define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ +static int expect_close = 0; static int soft_margin = TIMER_MARGIN; /* in seconds */ MODULE_PARM(soft_margin,"i"); @@ -79,7 +80,7 @@ static int timer_alive; static void watchdog_fire(unsigned long data) { #ifdef ONLY_TESTING - printk(KERN_CRIT "SOFTDOG: Would Reboot.\n"); + printk(KERN_CRIT "SOFTDOG: Would Reboot.\n"); #else printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n"); machine_restart(NULL); @@ -114,6 +115,8 @@ static int softdog_release(struct inode *inode, struct file *file) */ if(!nowayout) { del_timer(&watchdog_ticktock); + } else { + printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly. WDT will not stop!\n"); } timer_alive=0; return 0; @@ -129,6 +132,21 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo * Refresh the timer. */ if(len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); return 1; } @@ -139,6 +157,7 @@ static int softdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .identity = "Software Watchdog", }; switch (cmd) { diff --git a/drivers/char/watchdog/w83877f_wdt.c b/drivers/char/watchdog/w83877f_wdt.c index 66749672d39c..152c5cf41311 100644 --- a/drivers/char/watchdog/w83877f_wdt.c +++ b/drivers/char/watchdog/w83877f_wdt.c @@ -251,9 +251,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { static struct watchdog_info ident= { - 0, - 1, - "W83877F" + .options = WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "W83877F" }; switch(cmd) diff --git a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c new file mode 100644 index 000000000000..dc611c96835e --- /dev/null +++ b/drivers/char/watchdog/wafer5823wdt.c @@ -0,0 +1,262 @@ +/* + * ICP Wafer 5823 Single Board Computer WDT driver for Linux 2.4.x + * http://www.icpamerica.com/wafer_5823.php + * May also work on other similar models + * + * (c) Copyright 2002 Justin Cormack <justin@street-vision.com> + * + * Release 0.02 + * + * Based on advantechwdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/spinlock.h> + +static unsigned long wafwdt_is_open; +static spinlock_t wafwdt_lock; +static int expect_close = 0; + +/* + * You must set these - there is no sane way to probe for this board. + * + * To enable, write the timeout value in seconds (1 to 255) to I/O + * port WDT_START, then read the port to start the watchdog. To pat + * the dog, read port WDT_STOP to stop the timer, then read WDT_START + * to restart it again. + */ + +#define WDT_START 0x443 +#define WDT_STOP 0x843 + +#define WD_TIMO 60 /* 1 minute */ +static int wd_margin = WD_TIMO; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); + +static void wafwdt_ping(void) +{ + /* pat watchdog */ + spin_lock(&wafwdt_lock); + inb_p(WDT_STOP); + inb_p(WDT_START); + spin_unlock(&wafwdt_lock); +} + +static void wafwdt_start(void) +{ + /* start up watchdog */ + outb_p(wd_margin, WDT_START); + inb_p(WDT_START); +} + +static void +wafwdt_stop(void) +{ + /* stop watchdog */ + inb_p(WDT_STOP); +} + +static ssize_t wafwdt_write(struct file *file, const char *buf, size_t count, loff_t * ppos) +{ + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + 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; + } + } + wafwdt_ping(); + return 1; + } + return 0; +} + +static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "Wafer 5823 WDT" + }; + int one=1; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user + ((struct watchdog_info *) arg, &ident, sizeof (ident))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + if (copy_to_user((int *) arg, &one, sizeof (int))) + return -EFAULT; + break; + + case WDIOC_KEEPALIVE: + wafwdt_ping(); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int *)arg)) + return -EFAULT; + if ((new_margin < 1) || (new_margin > 255)) + return -EINVAL; + wd_margin = new_margin; + wafwdt_stop(); + wafwdt_start(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(wd_margin, (int *)arg); + + default: + return -ENOTTY; + } + return 0; +} + +static int wafwdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &wafwdt_is_open)) + return -EBUSY; + wafwdt_start(); + return 0; +} + +static int +wafwdt_close(struct inode *inode, struct file *file) +{ + clear_bit(0, &wafwdt_is_open); + if (expect_close) { + wafwdt_stop(); + } else { + printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n"); + } + return 0; +} + +/* + * Notifier for system down + */ + +static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the WDT off */ + wafwdt_stop(); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static struct file_operations wafwdt_fops = { + owner:THIS_MODULE, + write:wafwdt_write, + ioctl:wafwdt_ioctl, + open:wafwdt_open, + release:wafwdt_close, +}; + +static struct miscdevice wafwdt_miscdev = { + WATCHDOG_MINOR, + "watchdog", + &wafwdt_fops +}; + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wafwdt_notifier = { + wafwdt_notify_sys, + NULL, + 0 +}; + +static int __init wafwdt_init(void) +{ + printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n"); + + spin_lock_init(&wafwdt_lock); + if(!request_region(WDT_STOP, 1, "Wafer 5823 WDT")) + goto error; + if(!request_region(WDT_START, 1, "Wafer 5823 WDT")) + goto error2; + if(misc_register(&wafwdt_miscdev)<0) + goto error3; + register_reboot_notifier(&wafwdt_notifier); + return 0; +error3: + release_region(WDT_START, 1); +error2: + release_region(WDT_STOP, 1); +error: + return -ENODEV; +} + +static void __exit wafwdt_exit(void) +{ + misc_deregister(&wafwdt_miscdev); + unregister_reboot_notifier(&wafwdt_notifier); + release_region(WDT_STOP, 1); + release_region(WDT_START, 1); +} + +module_init(wafwdt_init); +module_exit(wafwdt_exit); + +MODULE_AUTHOR("Justin Cormack"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; + +/* end of wafer5823wdt.c */ diff --git a/drivers/char/watchdog/wdt.c b/drivers/char/watchdog/wdt.c index 33deb303e3d4..f50fd376e0ec 100644 --- a/drivers/char/watchdog/wdt.c +++ b/drivers/char/watchdog/wdt.c @@ -52,6 +52,7 @@ #include <asm/system.h> static unsigned long wdt_is_open; +static int expect_close; /* * You must set these - there is no sane way to probe for this board. @@ -258,8 +259,21 @@ static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_ if (ppos != &file->f_pos) return -ESPIPE; - if(count) - { + if(count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + 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; + } + } wdt_ping(); return 1; } @@ -317,10 +331,11 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { static struct watchdog_info ident= { - WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER - |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT, - 1, - "WDT500/501" + .options = WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER + |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT + |WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "WDT500/501" }; ident.options&=WDT_OPTION_MASK; /* Mask down to the card we have */ @@ -399,9 +414,11 @@ static int wdt_release(struct inode *inode, struct file *file) { if(minor(inode->i_rdev)==WATCHDOG_MINOR) { - if (!nowayout) { + if (expect_close) { inb_p(WDT_DC); /* Disable counters */ wdt_ctr_load(2,0); /* 0 length reset pulses now */ + } else { + printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); } clear_bit(0, &wdt_is_open); } diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c index ce522c581879..28c9e5842917 100644 --- a/drivers/char/watchdog/wdt977.c +++ b/drivers/char/watchdog/wdt977.c @@ -41,6 +41,7 @@ static int timeout = DEFAULT_TIMEOUT*60; /* TO in seconds from user */ static int timeoutM = DEFAULT_TIMEOUT; /* timeout in minutes */ static unsigned long timer_alive; static int testmode; +static int expect_close = 0; MODULE_PARM(timeout, "i"); MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=60"); @@ -196,6 +197,8 @@ static int wdt977_release(struct inode *inode, struct file *file) clear_bit(0,&timer_alive); printk(KERN_INFO "Wdt977 Watchdog: shutdown\n"); + } else { + printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n"); } return 0; } @@ -220,6 +223,21 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo if(count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } + kick_wdog(); return 1; } diff --git a/drivers/char/watchdog/wdt_pci.c b/drivers/char/watchdog/wdt_pci.c index 28374dc4f631..588f369ede0d 100644 --- a/drivers/char/watchdog/wdt_pci.c +++ b/drivers/char/watchdog/wdt_pci.c @@ -73,6 +73,7 @@ #endif static unsigned long wdt_is_open; +static int expect_close = 0; /* * You must set these - there is no sane way to probe for this board. @@ -276,6 +277,16 @@ static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, lo if(count) { + if (!nowayout) { + size_t i; + for (i = 0; i != count; i++) { + char c; + if(get_user(c, buf+i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } wdtpci_ping(); return 1; } @@ -332,9 +343,10 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd unsigned long arg) { static struct watchdog_info ident = { - .options = WDIOF_OVERHEAT | WDIOF_POWERUNDER | - WDIOF_POWEROVER | WDIOF_EXTERN1 | - WDIOF_EXTERN2 | WDIOF_FANFAULT, + .options = WDIOF_OVERHEAT | WDIOF_POWERUNDER | + WDIOF_POWEROVER | WDIOF_EXTERN1 | + WDIOF_EXTERN2 | WDIOF_FANFAULT | + WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = "WDT500/501PCI", }; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 391779c8b965..e208b6f07d67 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -39,6 +39,7 @@ struct watchdog_info { #define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */ #define WDIOF_POWEROVER 0x0040 /* Power over voltage */ #define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ +#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */ #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ |
