GIT 07e7d61080217ffc91740d6f3a506c44f3b675d1 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog-mm.git commit Author: Jiri Slaby Date: Sat Nov 10 04:32:45 2007 +0100 [WATCHDOG] Sbus: cpwatchdog, remove SPIN_LOCK_UNLOCKED cpwatchdog, remove SPIN_LOCK_UNLOCKED SPIN_LOCK_UNLOCKED is deprecated, use __SPIN_LOCK_UNLOCKED with an unique name instead Signed-off-by: Jiri Slaby Signed-off-by: Wim Van Sebroeck commit ac34925ab8398e21013341418baa2a08c5b8df48 Author: Jiri Slaby Date: Sat Nov 10 05:58:44 2007 +0100 [WATCHDOG] bfin_wdt, remove SPIN_LOCK_UNLOCKED bfin_wdt, remove SPIN_LOCK_UNLOCKED SPIN_LOCK_UNLOCKED is deprecated, use DEFINE_SPINLOCK instead Signed-off-by: Jiri Slaby Signed-off-by: Mike Frysinger Signed-off-by: Wim Van Sebroeck commit 41d26417ddbebcb6e919a273e83166e19080d22a Author: Samuel Tardieu Date: Sun Nov 4 20:20:23 2007 +0100 [WATCHDOG] Stop looking for device as soon as one is found If no address is given for the W83697HF/HG watchdog IO port, stop looping through possible locations when a watchdog device has been found. Signed-off-by: Samuel Tardieu Signed-off-by: Wim Van Sebroeck commit 6538b10f45452ac016578dc5403fafc44c795970 Author: Wim Van Sebroeck Date: Sun Aug 19 20:08:16 2007 +0000 [WATCHDOG] Convert iTCO_wdt.c to the Uniform Watchdog Device Driver Convert the iTCO_wdt.c driver to the Uniform Watchdog Device Driver framework. Signed-off-by: Wim Van Sebroeck commit 87d2e05b16ad1fa317aef683ab32be4392c9f8b0 Author: Wim Van Sebroeck Date: Sun Aug 19 19:44:24 2007 +0000 [WATCHDOG] Uniform Watchdog Device Driver The Uniform Watchdog Device Driver is a frame-work that contains the common code for all watchdog-driver's. It also introduces a watchdog device structure and the operations that go with it. Signed-off-by: Wim Van Sebroeck commit 061e2f454ad75203924cb70e9e0b9f609ab8505b Author: Hans-Christian Egtvedt Date: Tue Oct 30 14:56:20 2007 +0100 [WATCHDOG] at32ap700x_wdt: add support for boot status and add fix for silicon errata This patch enables the watchdog to read out the reset cause after a boot and provide this to the user. The driver will now also return -EIO if probed when booting from a watchdog reset. This is due to a silicon errata in the AT32AP700x devices. Detailed description and work-arounds can be found in the errata section of the datasheet avilable from http://www.atmel.com/dyn/products/datasheets.asp?family_id=682 Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Wim Van Sebroeck drivers/sbus/char/cpwatchdog.c | 2 +- drivers/watchdog/Kconfig | 5 +- drivers/watchdog/Makefile | 2 + drivers/watchdog/at32ap700x_wdt.c | 69 +++++- drivers/watchdog/bfin_wdt.c | 2 +- drivers/watchdog/core/Kconfig | 33 +++ drivers/watchdog/core/Makefile | 7 + drivers/watchdog/core/watchdog_core.c | 189 +++++++++++++ drivers/watchdog/core/watchdog_dev.c | 465 +++++++++++++++++++++++++++++++++ drivers/watchdog/iTCO_wdt.c | 247 +++++------------- drivers/watchdog/w83697hf_wdt.c | 4 +- include/linux/watchdog.h | 49 ++++ 12 files changed, 885 insertions(+), 189 deletions(-) diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c index 7b5773d..a4e7581 100644 --- a/drivers/sbus/char/cpwatchdog.c +++ b/drivers/sbus/char/cpwatchdog.c @@ -154,7 +154,7 @@ struct wd_device { }; static struct wd_device wd_dev = { - 0, SPIN_LOCK_UNLOCKED, 0, 0, 0, 0, + 0, __SPIN_LOCK_UNLOCKED(wd_dev.lock), 0, 0, 0, 0, }; static struct timer_list wd_timer; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2792bc1..386515e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -37,6 +37,8 @@ config WATCHDOG_NOWAYOUT get killed. If you say Y here, the watchdog cannot be stopped once it has been started. +source "drivers/watchdog/core/Kconfig" + # # General Watchdog drivers # @@ -366,10 +368,11 @@ config I6300ESB_WDT config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI + select WATCHDOG_CORE ---help--- Hardware driver for the intel TCO timer based watchdog devices. These drivers are included in the Intel 82801 I/O Controller - Hub family (from ICH0 up to ICH8) and in the Intel 6300ESB + Hub family (from ICH0 up to ICH9) and in the Intel 63xxESB controller hub. The TCO (Total Cost of Ownership) timer is a watchdog timer diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7d9e573..08090d3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -2,6 +2,8 @@ # Makefile for the WatchDog device drivers. # +obj-$(CONFIG_WATCHDOG) += core/ + # Only one watchdog can succeed. We probe the ISA/PCI/USB based # watchdog-cards first, then the architecture specific watchdog # drivers and then the architecture independant "softdog" driver. diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c index 54a5161..fb5ed64 100644 --- a/drivers/watchdog/at32ap700x_wdt.c +++ b/drivers/watchdog/at32ap700x_wdt.c @@ -6,6 +6,19 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * + * Errata: WDT Clear is blocked after WDT Reset + * + * A watchdog timer event will, after reset, block writes to the WDT_CLEAR + * register, preventing the program to clear the next Watchdog Timer Reset. + * + * If you still want to use the WDT after a WDT reset a small code can be + * insterted at the startup checking the AVR32_PM.rcause register for WDT reset + * and use a GPIO pin to reset the system. This method requires that one of the + * GPIO pins are available and connected externally to the RESET_N pin. After + * the GPIO pin has pulled down the reset line the GPIO will be reset and leave + * the pin tristated with pullup. */ #include @@ -44,6 +57,13 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" #define WDT_CLR 0x04 +#define WDT_RCAUSE 0x10 +#define WDT_RCAUSE_POR 0 +#define WDT_RCAUSE_EXT 2 +#define WDT_RCAUSE_WDT 3 +#define WDT_RCAUSE_JTAG 4 +#define WDT_RCAUSE_SERP 5 + #define WDT_BIT(name) (1 << WDT_##name) #define WDT_BF(name, value) ((value) << WDT_##name) @@ -56,6 +76,7 @@ struct wdt_at32ap700x { void __iomem *regs; spinlock_t io_lock; int timeout; + int boot_status; unsigned long users; struct miscdevice miscdev; }; @@ -126,7 +147,7 @@ static int at32_wdt_close(struct inode *inode, struct file *file) at32_wdt_stop(); } else { dev_dbg(wdt->miscdev.parent, - "Unexpected close, not stopping watchdog!\n"); + "unexpected close, not stopping watchdog!\n"); at32_wdt_pat(); } clear_bit(1, &wdt->users); @@ -154,6 +175,33 @@ static int at32_wdt_settimeout(int time) return 0; } +/* + * Get the watchdog status. + */ +static int at32_wdt_get_status(void) +{ + int rcause; + int status = 0; + + rcause = wdt_readl(wdt, RCAUSE); + + switch (rcause) { + case WDT_BIT(RCAUSE_EXT): + status = WDIOF_EXTERN1; + break; + case WDT_BIT(RCAUSE_WDT): + status = WDIOF_CARDRESET; + break; + case WDT_BIT(RCAUSE_POR): /* fall through */ + case WDT_BIT(RCAUSE_JTAG): /* fall through */ + case WDT_BIT(RCAUSE_SERP): /* fall through */ + default: + break; + } + + return status; +} + static struct watchdog_info at32_wdt_info = { .identity = "at32ap700x watchdog", .options = WDIOF_SETTIMEOUT | @@ -194,10 +242,12 @@ static int at32_wdt_ioctl(struct inode *inode, struct file *file, case WDIOC_GETTIMEOUT: ret = put_user(wdt->timeout, p); break; - case WDIOC_GETSTATUS: /* fall through */ - case WDIOC_GETBOOTSTATUS: + case WDIOC_GETSTATUS: ret = put_user(0, p); break; + case WDIOC_GETBOOTSTATUS: + ret = put_user(wdt->boot_status, p); + break; case WDIOC_SETOPTIONS: ret = get_user(time, p); if (ret) @@ -282,8 +332,19 @@ static int __init at32_wdt_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "could not map I/O memory\n"); goto err_free; } + spin_lock_init(&wdt->io_lock); - wdt->users = 0; + wdt->boot_status = at32_wdt_get_status(); + + /* Work-around for watchdog silicon errata. */ + if (wdt->boot_status & WDIOF_CARDRESET) { + dev_info(&pdev->dev, "CPU must be reset with external " + "reset or POR due to silicon errata.\n"); + ret = -EIO; + goto err_iounmap; + } else { + wdt->users = 0; + } wdt->miscdev.minor = WATCHDOG_MINOR; wdt->miscdev.name = "watchdog"; wdt->miscdev.fops = &at32_wdt_fops; diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c index 309d279..31dc7a6 100644 --- a/drivers/watchdog/bfin_wdt.c +++ b/drivers/watchdog/bfin_wdt.c @@ -71,7 +71,7 @@ static int nowayout = WATCHDOG_NOWAYOUT; static struct watchdog_info bfin_wdt_info; static unsigned long open_check; static char expect_close; -static spinlock_t bfin_wdt_spinlock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(bfin_wdt_spinlock); /** * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive diff --git a/drivers/watchdog/core/Kconfig b/drivers/watchdog/core/Kconfig new file mode 100644 index 0000000..ce8c391 --- /dev/null +++ b/drivers/watchdog/core/Kconfig @@ -0,0 +1,33 @@ +# +# Watchdog device driver core +# + +if WATCHDOG + +config WATCHDOG_CORE + tristate "Uniform Watchdog Device Driver" + depends on EXPERIMENTAL + default m + ---help--- + Say Y here if you want to use the new uniform watchdog device + driver. This driver provides a framework for all watchdog + device drivers and gives them the /dev/watchdog interface (and + later also the sysfs interface). + + At this moment only the iTCO_wdt driver uses this new framework. + + To compile this driver as a module, choose M here: the module will + be called watchdog_core. + +config WATCHDOG_DEBUG_CORE + bool "Uniform Watchdog Device Driver debugging output" + depends on WATCHDOG_CORE + default n + ---help--- + Say Y here if you want the Uniform Watchdog Device Driver to + produce debugging information. Select this if you are having a + problem with the uniform watchdog device driver and want to see + more of what is really happening. + +endif # WATCHDOG + diff --git a/drivers/watchdog/core/Makefile b/drivers/watchdog/core/Makefile new file mode 100644 index 0000000..fc04384 --- /dev/null +++ b/drivers/watchdog/core/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Watchdog Device Drivers generic core. +# + +# The Generic Watchdog Driver +obj-$(CONFIG_WATCHDOG_CORE) += watchdog_core.o watchdog_dev.o + diff --git a/drivers/watchdog/core/watchdog_core.c b/drivers/watchdog/core/watchdog_core.c new file mode 100644 index 0000000..fb05f8f --- /dev/null +++ b/drivers/watchdog/core/watchdog_core.c @@ -0,0 +1,189 @@ +/* + * watchdog.c + * + * (c) Copyright 2007 Wim Van Sebroeck . + * + * This code is generic code that can be shared by all the + * watchdog drivers. + * + * Based on source code of the following authors: + * Alan Cox , + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * 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 Wim Van Sebroeck nor Iguana vzw. admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + */ + +#include /* For module related things/EXPORT_SYMBOL/... */ +#include /* For standard types */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For memory allocations ... */ +#include /* For watchdog specific items */ +#include /* For __init/__exit/... */ + +/* + * Version information + */ +#define DRV_VERSION "0.01" +#define DRV_NAME "watchdog_core" +#define PFX DRV_NAME ": " + +/* + * External functions/procedures + */ +extern int watchdog_dev_register(struct watchdog_device *, struct device *); +extern int watchdog_dev_unregister(struct watchdog_device *); + +/** + * alloc_watchdogdev - allocates a watchdog device + * + * Creates a new watchdog device structure. + * Returns the new structure, or NULL if an error occured. + */ +struct watchdog_device *alloc_watchdogdev(void) +{ + int alloc_size = sizeof(struct watchdog_device); + void *p; + struct watchdog_device *dev; + + /* allocate memory for our device and initialize it */ + p = kzalloc(alloc_size, GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Unable to allocate watchdog device\n"); + return NULL; + } + dev = (struct watchdog_device *) p; + + return dev; +} +EXPORT_SYMBOL(alloc_watchdogdev); + +/** + * free_watchdogdev - free watchdog device + * @dev: watchdog device + * + * This function does the last stage of destroying an allocated + * watchdog device. + */ +int free_watchdogdev(struct watchdog_device *dev) +{ + if (!((dev->watchdog_state == WATCHDOG_UNINITIALIZED) || + (dev->watchdog_state == WATCHDOG_UNREGISTERED))) { + printk(KERN_ERR PFX "Unable to destroy a watchdog device that is still in use\n"); + return -1; + } + + kfree(dev); + return 0; +} +EXPORT_SYMBOL(free_watchdogdev); + +/** + * register_watchdogdevice - register a watchdog device + * @dev: watchdog device + * @parent: parent device for the watchdog class device + * + * This function registers a watchdog device in the kernel so + * that it can be accessed from userspace. + */ +int register_watchdogdevice(struct watchdog_device *dev, struct device *parent) +{ + int ret; + + if (dev == NULL || + dev->watchdog_ops == NULL) + return -ENODATA; + + if (dev->watchdog_ops->start == NULL || + dev->watchdog_ops->stop == NULL || + dev->watchdog_ops->keepalive == NULL) + return -ENODATA; + + if (!((dev->watchdog_state == WATCHDOG_UNINITIALIZED) || + (dev->watchdog_state == WATCHDOG_UNREGISTERED))) { + printk(KERN_ERR PFX "Unable to register a watchdog device that is allready in use\n"); + return -1; + } + + dev->options |= WDIOF_MAGICCLOSE; + if (dev->watchdog_ops->set_heartbeat) { + dev->options |= WDIOF_SETTIMEOUT; + } else { + dev->options &= ~WDIOF_SETTIMEOUT; + } + + ret = watchdog_dev_register(dev, parent); + if (ret) { + printk(KERN_ERR PFX "error registering /dev/watchdog (err=%d)", + ret); + return ret; + } + + dev->watchdog_state = WATCHDOG_REGISTERED; + return 0; +} +EXPORT_SYMBOL(register_watchdogdevice); + +/** + * unregister_watchdogdevice - unregister a watchdog device + * @dev: watchdog device + * + * This function unregisters a watchdog device from the kernel. + */ +int unregister_watchdogdevice(struct watchdog_device *dev) +{ + int ret; + + if (dev == NULL) + return -ENODATA; + + if ((dev->watchdog_state == WATCHDOG_UNINITIALIZED) || + (dev->watchdog_state == WATCHDOG_UNREGISTERED)) { + printk(KERN_ERR PFX "Unable to unregister a watchdog device that has not been registered\n"); + return -ENODEV; + } + + ret = watchdog_dev_unregister(dev); + if (ret) { + printk(KERN_ERR PFX "error unregistering /dev/watchdog (err=%d)", + ret); + return ret; + } + + dev->watchdog_state = WATCHDOG_UNREGISTERED; + return 0; +} +EXPORT_SYMBOL(unregister_watchdogdevice); + +static int __init watchdog_init(void) +{ + printk(KERN_INFO "Uniform watchdog device driver v%s loaded\n", + DRV_VERSION); + return 0; +} + +static void __exit watchdog_exit(void) +{ + printk(KERN_INFO "Uniform watchdog device driver unloaded\n"); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Wim Van Sebroeck "); +MODULE_DESCRIPTION("Uniform Watchdog Device Driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("watchdog"); + diff --git a/drivers/watchdog/core/watchdog_dev.c b/drivers/watchdog/core/watchdog_dev.c new file mode 100644 index 0000000..a0614d6 --- /dev/null +++ b/drivers/watchdog/core/watchdog_dev.c @@ -0,0 +1,465 @@ +/* + * watchdog_dev.c + * + * (c) Copyright 2007 Wim Van Sebroeck . + * + * This source code is part of the generic code that can be used + * by all the watchdog drivers. + * + * This part of the generic code takes care of the following + * misc device: /dev/watchdog. + * + * Based on source code of the following authors: + * Alan Cox , + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * 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 Wim Van Sebroeck nor Iguana vzw. admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + */ + +#include /* For module related things/EXPORT_SYMBOL/... */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For file operations */ +#include /* For watchdog specific items */ +#include /* For handling misc devices */ +#include /* For mutex locking */ +#include /* For __init/__exit/... */ +#include /* For copy_to_user/put_user/... */ + +#ifdef CONFIG_WATCHDOG_DEBUG_CORE +#define trace(format, args...) \ + printk(KERN_INFO "%s(" format ")\n", __FUNCTION__ , ## args) +#define dbg(format, arg...) \ + printk(KERN_DEBUG "%s: " format "\n", __FUNCTION__, ## arg) +#else +#define trace(format, arg...) do { } while (0) +#define dbg(format, arg...) do { } while (0) +#endif + +/* + * Version information + */ +#define DRV_VERSION "0.01" +#define DRV_NAME "watchdog_dev" +#define PFX DRV_NAME ": " + +/* + * Locally used variables + */ + +static struct watchdog_device *watchdogdev; /* the watchdog device behind /dev/watchdog */ +static unsigned long watchdog_dev_open; /* wether or not /dev/watchdog has been opened */ +static char received_magic_char; /* wether or not we received the magic char */ +static DEFINE_MUTEX(watchdog_register_mtx); /* prevent races between register & unregister */ + +/* + * /dev/watchdog operations + */ + +/* + * watchdog_write: writes to the watchdog. + * @file: file from VFS + * @data: user address of data + * @len: length of data + * @ppos: pointer to the file offset + * + * A write to a watchdog device is defined as a keepalive signal. + * Writing the magic 'V' sequence allows the next close to turn + * off the watchdog (if 'nowayout' is not set). + */ + +static ssize_t watchdog_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + trace("%p, %p, %zu, %p", file, data, len, ppos); + + if (!watchdogdev || + !watchdogdev->watchdog_ops || + !watchdogdev->watchdog_ops->keepalive) + return -ENODEV; + + /* See if we got the magic character 'V' and reload the timer */ + if (len) { + if (!watchdogdev->nowayout) { + size_t i; + + /* note: just in case someone wrote the magic character + * five months ago... */ + received_magic_char = 0; + + /* scan to see wether 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') { + received_magic_char = 42; + dbg("received the magic character\n"); + } + } + } + + /* someone wrote to us, so we sent the watchdog a keepalive signal if + * the watchdog is active */ + if (watchdogdev->watchdog_state == WATCHDOG_STARTED) + watchdogdev->watchdog_ops->keepalive(watchdogdev); + } + return len; +} + +/* + * watchdog_ioctl: handle the different ioctl's for the watchdog device. + * @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. + */ + +static int watchdog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int status; + int err; + int new_options; + int new_heartbeat; + int time_left; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = 0, + .firmware_version = 0, + .identity = "Watchdog Device", + }; + + trace("%p, %p, %u, %li", inode, file, cmd, arg); + + if (!watchdogdev || !watchdogdev->watchdog_ops) + return -ENODEV; + + switch (cmd) { + case WDIOC_GETSUPPORT: + { + ident.options = watchdogdev->options; + ident.firmware_version = watchdogdev->firmware; + + strncpy(ident.identity, watchdogdev->name, 31); + ident.identity[32] = 0; + + return copy_to_user(argp, &ident, + sizeof(ident)) ? -EFAULT : 0; + } + + case WDIOC_GETSTATUS: + { + status = 0; + + if (watchdogdev->watchdog_ops->get_status && + watchdogdev->watchdog_ops->get_status(watchdogdev, &status)) + return -EFAULT; + + return put_user(status, p); + } + + case WDIOC_GETBOOTSTATUS: + return put_user(watchdogdev->bootstatus, p); + + case WDIOC_KEEPALIVE: + { + if (!watchdogdev->watchdog_ops->keepalive) + return -EFAULT; + + /* We only sent a keepalive when the watchdog is active */ + if (watchdogdev->watchdog_state == WATCHDOG_STARTED) + watchdogdev->watchdog_ops->keepalive(watchdogdev); + + return 0; + } + + case WDIOC_SETOPTIONS: + { + if (get_user(new_options, p)) + return -EFAULT; + + if (!watchdogdev->watchdog_ops->start || + !watchdogdev->watchdog_ops->stop) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + /* only try to stop the watchdog if it's allready running */ + if (watchdogdev->watchdog_state == WATCHDOG_STARTED) { + err = watchdogdev->watchdog_ops->stop(watchdogdev); + if (err == 0) { + watchdogdev->watchdog_state = WATCHDOG_STOPPED; + } else { + printk(KERN_CRIT PFX "WDIOS_DISABLECARD not successfull! (err=%d)", + err); + return -EFAULT; + } + } + } + + if (new_options & WDIOS_ENABLECARD) { + /* if the watchdog is not allready running, try to start it */ + if (watchdogdev->watchdog_state != WATCHDOG_STARTED) { + err = watchdogdev->watchdog_ops->start(watchdogdev); + if (err == 0) { + watchdogdev->watchdog_state = WATCHDOG_STARTED; + } else { + printk(KERN_CRIT PFX "WDIOS_ENABLECARD not successfull! (err=%d)", + err); + return -EFAULT; + } + } + } + + return 0; + } + + case WDIOC_SETTIMEOUT: + { + if (!watchdogdev->watchdog_ops->set_heartbeat) + return -ENOTTY; + + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (watchdogdev->watchdog_ops->set_heartbeat(watchdogdev, new_heartbeat)) + return -EFAULT; + + /* If the watchdog is active then we sent a keepalive to make sure + * that the watchdog keep's running (and if possible takes the new + * heartbeat) */ + if (watchdogdev->watchdog_ops->keepalive && + (watchdogdev->watchdog_state == WATCHDOG_STARTED)) + watchdogdev->watchdog_ops->keepalive(watchdogdev); + /* Fall */ + } + + case WDIOC_GETTIMEOUT: + return put_user(watchdogdev->heartbeat, p); + + case WDIOC_GETTIMELEFT: + { + if (!watchdogdev->watchdog_ops->get_timeleft) + return -ENOTTY; + + if (watchdogdev->watchdog_ops->get_timeleft(watchdogdev, &time_left)) + return -EFAULT; + + return put_user(time_left, p); + } + + default: + return -ENOTTY; + } +} + +/* + * watchdog_open: open the /dev/watchdog device. + * @inode: inode of device + * @file: file handle to device + * + * When the /dev/watchdog device get's opened, we start the watchdog + * and feed it with his first keepalive signal. Watch out: the + * /dev/watchdog device is single open, so make sure it can only be + * opened once. + */ + +static int watchdog_open(struct inode *inode, struct file *file) +{ + trace("%p, %p", inode, file); + + /* only open if we have a valid watchdog device */ + if (!watchdogdev || + !watchdogdev->watchdog_ops || + !watchdogdev->watchdog_ops->start || + !watchdogdev->watchdog_ops->stop || + !watchdogdev->watchdog_ops->keepalive) + return -EBUSY; + + /* the watchdog is single open! */ + if (test_and_set_bit(0, &watchdog_dev_open)) + return -EBUSY; + + /* if the watchdog is not allready running, try to start it */ + if (watchdogdev->watchdog_state != WATCHDOG_STARTED) { + if (watchdogdev->watchdog_ops->start(watchdogdev) == 0) + watchdogdev->watchdog_state = WATCHDOG_STARTED; + } + + /* if the watchdog started, then feed the watchdog it's first keepalive signal */ + if (watchdogdev->watchdog_state == WATCHDOG_STARTED) + watchdogdev->watchdog_ops->keepalive(watchdogdev); + + return nonseekable_open(inode, file); +} + +/* + * watchdog_release: release the /dev/watchdog device. + * @inode: inode of device + * @file: file handle to device + * + * This is the code for when /dev/watchdog get's closed. We will only + * stop the watchdog when we have received the magic char, else the + * watchdog will keep running. + */ + +static int watchdog_release(struct inode *inode, struct file *file) +{ + int err; + + trace("%p, %p", inode, file); + dbg("received_magic_char=%d", received_magic_char); + + if (watchdogdev && (watchdogdev->watchdog_state == WATCHDOG_STARTED)) { + /* Only stop a watchdog if it actually started */ + if (received_magic_char == 42) { + /* we received the magic char -> we can stop the watchdog */ + if (watchdogdev->watchdog_ops && watchdogdev->watchdog_ops->stop) { + err = watchdogdev->watchdog_ops->stop(watchdogdev); + if (err == 0) { + watchdogdev->watchdog_state = WATCHDOG_STOPPED; + } else { + printk(KERN_CRIT PFX "Watchdog didn't stop successfull! (err=%d)", + err); + } + } else { + printk(KERN_CRIT PFX "Unable to stop watchdog!"); + } + } else { + /* If we didn't receive the magic char, then we will close + * /dev/watchdog but the watchdog keeps running... */ + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!"); + if (watchdogdev->watchdog_ops && watchdogdev->watchdog_ops->keepalive) { + watchdogdev->watchdog_ops->keepalive(watchdogdev); + } + } + } + + received_magic_char = 0; + + /* make sure that /dev/watchdog can be re-opened */ + clear_bit(0, &watchdog_dev_open); + + return 0; +} + +/* + * /dev/watchdog kernel interfaces + */ + +static struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = watchdog_write, + .ioctl = watchdog_ioctl, + .open = watchdog_open, + .release = watchdog_release, +}; + +static struct miscdevice watchdog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &watchdog_fops, +}; + +/* + * /dev/watchdog register and unregister functions + */ + +/* + * watchdog_dev_register: + * + * Register a watchdog device as /dev/watchdog. /dev/watchdog + * is actually a miscdevice and thus we set it up like that. + */ + +int watchdog_dev_register(struct watchdog_device *wdd, struct device *parent) +{ + int err = -EBUSY; + + trace("%p %p", wdd, parent); + + mutex_lock(&watchdog_register_mtx); + + if (watchdogdev) { + printk(KERN_ERR PFX "another watchdog device is allready registered as /dev/watchdog\n"); + goto out; + } + + watchdog_miscdev.parent = parent; + + dbg("Register a new /dev/watchdog device\n"); + err = misc_register(&watchdog_miscdev); + if (err != 0) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + watchdog_miscdev.minor, err); + goto out; + } + + watchdogdev = wdd; + +out: + mutex_unlock(&watchdog_register_mtx); + return err; +} +EXPORT_SYMBOL(watchdog_dev_register); + +/* + * watchdog_dev_unregister: + * + * Deregister the /dev/watchdog device. + */ + +int watchdog_dev_unregister(struct watchdog_device *wdd) +{ + trace("%p", wdd); + + mutex_lock(&watchdog_register_mtx); + + if (!watchdogdev) { + printk(KERN_ERR PFX "there is no watchdog registered\n"); + mutex_unlock(&watchdog_register_mtx); + return -1; + } + + if (!wdd) { + printk(KERN_ERR PFX "cannot unregister non-existing watchdog-driver\n"); + mutex_unlock(&watchdog_register_mtx); + return -2; + } + + if (watchdogdev != wdd) { + printk(KERN_ERR PFX "another watchdog device is running\n"); + mutex_unlock(&watchdog_register_mtx); + return -3; + } + + dbg("Unregister /dev/watchdog device\n"); + misc_deregister(&watchdog_miscdev); + watchdogdev = NULL; + mutex_unlock(&watchdog_register_mtx); + return 0; +} +EXPORT_SYMBOL(watchdog_dev_unregister); + +MODULE_AUTHOR("Wim Van Sebroeck "); +MODULE_DESCRIPTION("Generic /dev/watchdog Code"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); + diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index a0e6809..ceac0b2 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -1,5 +1,5 @@ /* - * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets) + * intel TCO Watchdog Driver (Used in i82801 and i63xxESB chipsets) * * (c) Copyright 2006-2007 Wim Van Sebroeck . * @@ -55,8 +55,8 @@ /* Module and version information */ #define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.02" -#define DRV_RELDATE "26-Jul-2007" +#define DRV_VERSION "1.10" +#define DRV_RELDATE "17-Aug-2007" #define PFX DRV_NAME ": " /* Includes */ @@ -137,10 +137,10 @@ static struct { {"ICH9R", 2}, {"ICH9DH", 2}, {"631xESB/632xESB", 2}, - {NULL,0} + {NULL, 0} }; -#define ITCO_PCI_DEVICE(dev, data) \ +#define ITCO_PCI_DEVICE(dev, data) \ .vendor = PCI_VENDOR_ID_INTEL, \ .device = dev, \ .subvendor = PCI_ANY_ID, \ @@ -207,18 +207,16 @@ MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl); #define TCO_RLD TCOBASE + 0x00 /* TCO Timer Reload and Current Value */ #define TCOv1_TMR TCOBASE + 0x01 /* TCOv1 Timer Initial Value */ -#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ -#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ -#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ -#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ +#define TCO_DAT_IN TCOBASE + 0x02 /* TCO Data In Register */ +#define TCO_DAT_OUT TCOBASE + 0x03 /* TCO Data Out Register */ +#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ +#define TCO2_STS TCOBASE + 0x06 /* TCO2 Status Register */ #define TCO1_CNT TCOBASE + 0x08 /* TCO1 Control Register */ #define TCO2_CNT TCOBASE + 0x0a /* TCO2 Control Register */ #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 */ +static struct iTCO_wdt_data { /* this is private data for the iTCO_wdt device */ unsigned int iTCO_version; /* TCO version/generation */ unsigned long ACPIBASE; /* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ unsigned long __iomem *gcs; /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2) */ @@ -226,6 +224,7 @@ static struct { /* this is private data for the iTCO_wdt device */ struct pci_dev *pdev; /* the PCI-device */ } iTCO_wdt_private; +static struct watchdog_device *iTCO_wdt_dev; /* the watchdog device */ static struct platform_device *iTCO_wdt_platform_device; /* the watchdog platform device */ /* module parameters */ @@ -248,7 +247,7 @@ extern int iTCO_vendor_check_noreboot_on(void); #else #define iTCO_vendor_pre_start(acpibase, heartbeat) {} #define iTCO_vendor_pre_stop(acpibase) {} -#define iTCO_vendor_pre_keepalive(acpibase,heartbeat) {} +#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {} #define iTCO_vendor_pre_set_heartbeat(heartbeat) {} #define iTCO_vendor_check_noreboot_on() 1 /* 1=check noreboot; 0=don't check */ #endif @@ -307,10 +306,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; + if (!wd_dev) + return -ENODEV; + spin_lock(&iTCO_wdt_private.io_lock); iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat); @@ -334,10 +336,13 @@ 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; + if (!wd_dev) + return -ENODEV; + spin_lock(&iTCO_wdt_private.io_lock); iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE); @@ -358,8 +363,11 @@ static int iTCO_wdt_stop(void) return 0; } -static int iTCO_wdt_keepalive(void) +static int iTCO_wdt_keepalive(struct watchdog_device *wd_dev) { + if (!wd_dev) + return -ENODEV; + spin_lock(&iTCO_wdt_private.io_lock); iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat); @@ -375,12 +383,15 @@ static int iTCO_wdt_keepalive(void) return 0; } -static int iTCO_wdt_set_heartbeat(int t) +static int iTCO_wdt_set_heartbeat(struct watchdog_device *wd_dev, int t) { unsigned int val16; unsigned char val8; unsigned int tmrval; + if (!wd_dev) + return -ENODEV; + tmrval = seconds_to_ticks(t); /* from the specs: */ /* "Values of 0h-3h are ignored and should not be attempted" */ @@ -417,15 +428,18 @@ static int iTCO_wdt_set_heartbeat(int t) return -EINVAL; } - heartbeat = t; + wd_dev->heartbeat = t; return 0; } -static int iTCO_wdt_get_timeleft (int *time_left) +static int iTCO_wdt_get_timeleft (struct watchdog_device *wd_dev, int *time_left) { unsigned int val16; unsigned char val8; + if (!wd_dev) + return -ENODEV; + /* read the TCO Timer */ if (iTCO_wdt_private.iTCO_version == 2) { spin_lock(&iTCO_wdt_private.io_lock); @@ -447,161 +461,15 @@ static int iTCO_wdt_get_timeleft (int *time_left) } /* - * /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_keepalive(); - 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 { - printk(KERN_CRIT PFX "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 int iTCO_wdt_ioctl (struct inode *inode, 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 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_KEEPALIVE: - iTCO_wdt_keepalive(); - return 0; - - 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_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; - } -} - -/* * Kernel Interfaces */ -static const struct file_operations iTCO_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = iTCO_wdt_write, - .ioctl = iTCO_wdt_ioctl, - .open = iTCO_wdt_open, - .release = iTCO_wdt_release, -}; - -static struct miscdevice iTCO_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &iTCO_wdt_fops, +static struct watchdog_ops iTCO_wdt_ops = { + .start = iTCO_wdt_start, + .stop = iTCO_wdt_stop, + .keepalive = iTCO_wdt_keepalive, + .set_heartbeat = iTCO_wdt_set_heartbeat, + .get_timeleft = iTCO_wdt_get_timeleft, }; /* @@ -615,6 +483,21 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device unsigned long RCBA; unsigned long val32; + iTCO_wdt_dev = alloc_watchdogdev(); + if (!iTCO_wdt_dev) { + printk(KERN_ERR PFX "Unable to allocate a watchdogdevice\n"); + pci_dev_put(pdev); + return -ENODEV; + } + + strcpy(iTCO_wdt_dev->name, DRV_NAME); + iTCO_wdt_dev->options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING; + iTCO_wdt_dev->firmware = iTCO_chipset_info[ent->driver_data].iTCO_version; + iTCO_wdt_dev->nowayout = nowayout; + iTCO_wdt_dev->watchdog_ops = &iTCO_wdt_ops; + + iTCO_wdt_dev->private = &iTCO_wdt_private; + /* * Find the ACPI/PM base I/O address which is the base * for the TCO registers (TCOBASE=ACPIBASE + 0x60) @@ -638,7 +521,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device if (iTCO_wdt_private.iTCO_version == 2) { pci_read_config_dword(pdev, 0xf0, &base_address); RCBA = base_address & 0xffffc000; - iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410),4); + iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4); } /* Check chipset's NO_REBOOT bit */ @@ -681,19 +564,19 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device outb(3, TCO2_STS); /* Make sure the watchdog is not running */ - iTCO_wdt_stop(); + iTCO_wdt_stop(iTCO_wdt_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); + if (iTCO_wdt_set_heartbeat(iTCO_wdt_dev, heartbeat)) { + iTCO_wdt_set_heartbeat(iTCO_wdt_dev, WATCHDOG_HEARTBEAT); printk(KERN_INFO PFX "heartbeat value must be 2dev); if (ret != 0) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); + printk(KERN_ERR PFX "cannot register new watchdog device (err=%d)\n", + ret); goto unreg_region; } @@ -707,6 +590,7 @@ unreg_region: out: if (iTCO_wdt_private.iTCO_version == 2) iounmap(iTCO_wdt_private.gcs); + free_watchdogdev(iTCO_wdt_dev); pci_dev_put(iTCO_wdt_private.pdev); iTCO_wdt_private.ACPIBASE = 0; return ret; @@ -716,13 +600,14 @@ static void __devexit iTCO_wdt_cleanup(void) { /* Stop the timer before we leave */ if (!nowayout) - iTCO_wdt_stop(); + iTCO_wdt_stop(iTCO_wdt_dev); /* Deregister */ - misc_deregister(&iTCO_wdt_miscdev); + unregister_watchdogdevice(iTCO_wdt_dev); release_region(TCOBASE, 0x20); if (iTCO_wdt_private.iTCO_version == 2) iounmap(iTCO_wdt_private.gcs); + free_watchdogdev(iTCO_wdt_dev); pci_dev_put(iTCO_wdt_private.pdev); iTCO_wdt_private.ACPIBASE = 0; } @@ -763,7 +648,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(iTCO_wdt_dev); } #define iTCO_wdt_suspend NULL diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c index c622a0e..6ea125e 100644 --- a/drivers/watchdog/w83697hf_wdt.c +++ b/drivers/watchdog/w83697hf_wdt.c @@ -382,8 +382,10 @@ wdt_init(void) /* we will autodetect the W83697HF/HG watchdog */ for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) { wdt_io = w83697hf_ioports[i]; - if (!w83697hf_check_wdt()) + if (!w83697hf_check_wdt()) { found++; + break; + } } } else { if (!w83697hf_check_wdt()) diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 011bcfe..4f57bf4 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -53,12 +53,61 @@ struct watchdog_info { #ifdef __KERNEL__ +#include + #ifdef CONFIG_WATCHDOG_NOWAYOUT #define WATCHDOG_NOWAYOUT 1 #else #define WATCHDOG_NOWAYOUT 0 #endif +struct watchdog_ops; +struct watchdog_device; + +struct watchdog_ops { + /* mandatory routines */ + /* operation = start watchdog */ + int (*start)(struct watchdog_device *); + /* operation = stop watchdog */ + int (*stop)(struct watchdog_device *); + /* operation = send keepalive ping */ + int (*keepalive)(struct watchdog_device *); + /* optional routines */ + /* operation = set watchdog's heartbeat */ + int (*set_heartbeat)(struct watchdog_device *, int); + /* operation = get the watchdog's status */ + int (*get_status)(struct watchdog_device *, int *); + /* operation = get the time left before reboot */ + int (*get_timeleft)(struct watchdog_device *, int *); +}; + +struct watchdog_device { + unsigned char name[32]; /* The watchdog's 'identity' */ + unsigned long options; /* The supported capabilities/options */ + unsigned long firmware; /* The Watchdog's Firmware version */ + int nowayout; /* The nowayout setting for this watchdog */ + int heartbeat; /* The watchdog's heartbeat */ + int bootstatus; /* The watchdog's bootstatus */ + struct watchdog_ops *watchdog_ops; /* link to watchdog_ops */ + + /* watchdog status (register/unregister) state machine */ + enum { WATCHDOG_UNINITIALIZED = 0, + WATCHDOG_REGISTERED, /* completed register_watchdogdevice */ + WATCHDOG_STARTED, /* watchdog device started */ + WATCHDOG_STOPPED, /* watchdog device stopped */ + WATCHDOG_UNREGISTERED, /* completed unregister_watchdogdevice */ + } watchdog_state; + + /* From here on everything is device dependent */ + void *private; +}; + +/* drivers/watchdog/watchdog_core.c */ +extern struct watchdog_device *alloc_watchdogdev(void); +extern int register_watchdogdevice(struct watchdog_device *, struct device *); +extern int unregister_watchdogdevice(struct watchdog_device *); +extern int free_watchdogdev(struct watchdog_device *); + #endif /* __KERNEL__ */ #endif /* ifndef _LINUX_WATCHDOG_H */