GIT 5febd3eadd26323227008635bf7f1305a849bc29 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog-mm.git commit Author: Andrew Morton Date: Thu Mar 6 15:52:00 2008 -0800 [WATCHDOG] prevent kconfig explosion drivers/watchdog/Kconfig:305: unknown option "CS5535" drivers/watchdog/Kconfig:306: unknown option "processors" drivers/watchdog/Kconfig:307: unknown option "it" drivers/watchdog/Kconfig:308: unknown option "a" drivers/watchdog/Kconfig:310: unknown option "You" drivers/watchdog/Kconfig:311: unknown option "it" That seems rather severe treatment for just an extra leading space? Signed-off-by: Wim Van Sebroeck Cc: Sam Ravnborg Cc: Roman Zippel Signed-off-by: Andrew Morton commit f06bb7eb87c92935c41cadf68dcce5027509d536 Author: Adrian Bunk Date: Fri Feb 22 21:58:02 2008 +0200 [WATCHDOG] make watchdog/hpwdt.c:asminline_call() static This patch makes the needlessly global asminline_call() static and removes the not required "asmlinkage". Signed-off-by: Adrian Bunk Acked-by: Thomas Mingarelli Signed-off-by: Wim Van Sebroeck Cc: Andrew Morton commit a0c52acc4eb5412cf3826bd957357ae5719fde2c Author: Jordan Crouse Date: Mon Jan 21 10:07:00 2008 -0700 [WATCHDOG] Add a watchdog driver based on the CS5535/CS5536 MFGPT timers Add a watchdog timer based on the MFGPT timers in the CS5535/CS5536 companion chips to the AMD Geode GX and LX processors. Only caveat is that the BIOS must provide at least a one free timer, and most do not. Signed-off-by: Jordan Crouse Signed-off-by: Wim Van Sebroeck commit b2de97cf901162e0a5936b2bbdae27ec1e55455d Author: Florian Fainelli Date: Mon Feb 25 12:59:26 2008 +0100 [WATCHDOG] Add support for the built-int RDC R-321x SoC watchdog This patch adds support for the built-in RDC R-321x SoC watchdog. Signed-off-by: Florian Fainelli Signed-off-by: Wim Van Sebroeck commit 560aa3cd546cba87dbb40f0499d77623e7b2a184 Author: Florian Fainelli Date: Mon Feb 25 13:11:31 2008 +0100 [WATCHDOG] Add support for the IDT RC32434 watchdog Add driver for the IDT RC32434 SoC built-in watchdog. Signed-off-by: Florian Fainelli Signed-off-by: Wim Van Sebroeck commit 098d0b6d72d94c034478f46b61328271cfe027a0 Author: Florian Fainelli Date: Mon Feb 25 13:39:57 2008 +0100 [WATCHDOG] Remove volatiles from watchdog device structures Remove the volatile since those are useless in such a structure. Signed-off-by: Florian Fainelli Signed-off-by: Wim Van Sebroeck commit fc1b13d3dd0af69139a9782fd2cca187b18e627d Author: Harvey Harrison Date: Wed Mar 5 18:24:58 2008 -0800 [WATCHDOG] replace remaining __FUNCTION__ occurrences __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit 449a2f7fc56b6a45f315ec74cb11f69792776f27 Author: Roland Dreier Date: Thu Feb 28 12:34:42 2008 -0800 [WATCHDOG] hpwdt: Use dmi_walk() instead of own copy We can simplify the code by deleting all of the duplicated DMI table walking code and using the kernel's existing dmi_walk() interface to find the DMI entry the driver is looking for. Signed-off-by: Roland Dreier Acked-by: Thomas Mingarelli Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit 4123c4e697a963ec566841f46759dc64dc1ae027 Author: Roland Dreier Date: Thu Feb 28 09:48:10 2008 -0800 [WATCHDOG] Fix return value warning in hpwdt The return value of smbios_scan_machine() is never used, and when it succeeds it doesn't return anything, so just make it void. This fixes: drivers/watchdog/hpwdt.c: In function 'smbios_scan_machine': drivers/watchdog/hpwdt.c:562: warning: control reaches end of non-void function Signed-off-by: Roland Dreier Acked-by: Thomas Mingarelli Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit 2cc6aa4a83b1a579e52d1157e3d642c7aacf0439 Author: Roland Dreier Date: Thu Feb 28 09:38:44 2008 -0800 [WATCHDOG] Fix declaration of struct smbios_entry_point in hpwdt On my HP DL380 G5 system running a 64-bit kernel, loading the hpwdt driver causes a crash because the driver attempts to ioremap an invalid physical address. This is because the driver has an incorrect definition of the SMBIOS table entry point structure: the table address is only a 32-bit quantity, and making it a u64 means that the high-order 32 bits end up containing garbage. Correcting the structure definition fixes the driver so that it loads without any problems on my system. Signed-off-by: Roland Dreier Acked-by: Thomas Mingarelli Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit 84fec934fd81a69fd534e510499e59c470df5d75 Author: Andrew Paprocki Date: Sun Feb 10 22:11:15 2008 -0500 [WATCHDOG] it8712f_wdt support for 16-bit timeout values, WDIOC_GETSTATUS This patch adds support for 16-bit watchdog timeout values which are available in chip revisions >= 0x08. Values <= 65535 are seconds precision, otherwise minutes precision is used up to a maximum value of 3932100. Added implementation for WDIOC_GETSTATUS which checks the WDT status bit in the WDT control register. Signed-off-by: Andrew Paprocki Signed-off-by: Wim Van Sebroeck commit 6294079f470183b98c787c9572db25b978546667 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 b3cd854e3466a3bd51947b7deaab4fdde70364ea 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 drivers/watchdog/Kconfig | 38 +++- drivers/watchdog/Makefile | 5 + drivers/watchdog/core/Kconfig | 33 +++ drivers/watchdog/core/Makefile | 7 + drivers/watchdog/core/watchdog_core.c | 200 ++++++++++++++ drivers/watchdog/core/watchdog_dev.c | 465 +++++++++++++++++++++++++++++++++ drivers/watchdog/geodewdt.c | 309 ++++++++++++++++++++++ drivers/watchdog/iTCO_wdt.c | 247 +++++------------- drivers/watchdog/rc32434_wdt.c | 333 +++++++++++++++++++++++ drivers/watchdog/rdc321x_wdt.c | 271 +++++++++++++++++++ include/linux/watchdog.h | 49 ++++ 11 files changed, 1775 insertions(+), 182 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 254d115..f6e7296 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 # @@ -295,6 +297,19 @@ config ALIM7101_WDT Most people will say N. +config GEODE_WDT + tristate "AMD Geode CS5535/CS5536 Watchdog" + depends on MGEODE_LX + help + This driver enables a watchdog capability built into the + CS5535/CS5536 companion chips for the AMD Geode GX and LX + processors. This watchdog watches your kernel to make sure + it doesn't freeze, and if it does, it reboots your computer after + a certain amount of time. + + You can compile this driver directly into the kernel, or use + it as a module. The module will be called geodewdt. + config SC520_WDT tristate "AMD Elan SC520 processor Watchdog" depends on X86 @@ -366,10 +381,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 @@ -451,6 +467,16 @@ config PC87413_WDT Most people will say N. +config RDC321X_WDT + tristate "RDC R-321x SoC watchdog" + depends on X86_RDC321X + help + This is the driver for the built in hardware watchdog + in the RDC R-321x SoC. + + To compile this driver as a module, choose M here: the + module will be called rdc321x_wdt. + config 60XX_WDT tristate "SBC-60XX Watchdog Timer" depends on X86 @@ -619,6 +645,16 @@ config SBC_EPX_C3_WATCHDOG # MIPS Architecture +config RC32434_WDT + tristate "IDT RC32434 SoC Watchdog Timer" + depends on MIKROTIK_RB500 + help + Hardware driver for the IDT RC32434 SoC built-in + watchdog timer. + + To compile this driver as a module, choose M here: the + module will be called rc32434_wdt. + config INDYDOG tristate "Indy/I2 Hardware Watchdog" depends on SGI_HAS_INDYDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f3fb170..8e38569 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. @@ -59,6 +61,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o +obj-$(CONFIG_GEODE_WDT) += geodewdt.o obj-$(CONFIG_SC520_WDT) += sc520_wdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_IB700_WDT) += ib700wdt.o @@ -71,6 +74,7 @@ obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o +obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o @@ -90,6 +94,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o # M68KNOMMU Architecture # MIPS Architecture +obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o 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..e82aabd --- /dev/null +++ b/drivers/watchdog/core/watchdog_core.c @@ -0,0 +1,200 @@ +/* + * 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 + * @sizeof_priv: size of the private data area + * + * Creates a new watchdog device structure, complete with a private + * data area of size @sizeof_priv. This private data area will be + * aligned to sizeof(long). + * + * Returns the new structure, or NULL if an error occured. + */ +struct watchdog_device *alloc_watchdogdev(size_t sizeof_priv) +{ + struct watchdog_device *dev; + int alloc_size = sizeof(struct watchdog_device); + + if (sizeof_priv) { + /* ensure alignment to sizeof(long) of the private area */ + alloc_size += ((BITS_PER_LONG/8) - + (sizeof(struct watchdog_device) % (BITS_PER_LONG/8))); + } + + /* allocate memory for our device and initialize it */ + dev = kzalloc(alloc_size + sizeof_priv, GFP_KERNEL); + if (!dev) { + printk(KERN_ERR PFX "Unable to allocate watchdog device\n"); + return NULL; + } + + if (sizeof_priv) + dev->private = dev + alloc_size; + + 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/geodewdt.c b/drivers/watchdog/geodewdt.c new file mode 100644 index 0000000..f85b196 --- /dev/null +++ b/drivers/watchdog/geodewdt.c @@ -0,0 +1,309 @@ +/* Watchdog timer for the Geode GX/LX with the CS5535/CS5536 companion chip + * + * Copyright (C) 2006-2007, Advanced Micro Devices, Inc. + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define GEODEWDT_HZ 500 +#define GEODEWDT_SCALE 6 +#define GEODEWDT_MAX_SECONDS 131 + +#define WDT_FLAGS_OPEN 1 +#define WDT_FLAGS_ORPHAN 2 + +#define DRV_NAME "geodewdt" +#define WATCHDOG_NAME "Geode GX/LX WDT" +#define WATCHDOG_TIMEOUT 60 + +static int timeout = WATCHDOG_TIMEOUT; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=131, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static struct platform_device *geodewdt_platform_device; +static unsigned long wdt_flags; +static int wdt_timer; +static int safe_close; + +static void geodewdt_ping(void) +{ + /* Stop the counter */ + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); + + /* Reset the counter */ + geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); + + /* Enable the counter */ + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); +} + +static void geodewdt_disable(void) +{ + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); +} + +static int geodewdt_set_heartbeat(int val) +{ + if (val < 1 || val > GEODEWDT_MAX_SECONDS) + return -EINVAL; + + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ); + geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); + + timeout = val; + return 0; +} + +static int +geodewdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags)) + return -EBUSY; + + if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags)) + __module_get(THIS_MODULE); + + geodewdt_ping(); + return nonseekable_open(inode, file); +} + +static int +geodewdt_release(struct inode *inode, struct file *file) +{ + if (safe_close) { + geodewdt_disable(); + module_put(THIS_MODULE); + } + else { + printk(KERN_CRIT "Unexpected close - watchdog is not stopping.\n"); + geodewdt_ping(); + + set_bit(WDT_FLAGS_ORPHAN, &wdt_flags); + } + + clear_bit(WDT_FLAGS_OPEN, &wdt_flags); + safe_close = 0; + return 0; +} + +static ssize_t +geodewdt_write(struct file *file, const char __user *data, size_t len, + loff_t *ppos) +{ + if(len) { + if (!nowayout) { + size_t i; + safe_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + + if (c == 'V') + safe_close = 1; + } + } + + geodewdt_ping(); + } + return len; +} + +static int +geodewdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int interval; + + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = WATCHDOG_NAME, + }; + + switch(cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + geodewdt_ping(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(interval, p)) + return -EFAULT; + + if (geodewdt_set_heartbeat(interval)) + return -EINVAL; + +/* Fall through */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + + case WDIOC_SETOPTIONS: + { + int options, ret = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + geodewdt_disable(); + ret = 0; + } + + if (options & WDIOS_ENABLECARD) { + geodewdt_ping(); + ret = 0; + } + + return ret; + } + default: + return -ENOTTY; + } + + return 0; +} + +static const struct file_operations geodewdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = geodewdt_write, + .ioctl = geodewdt_ioctl, + .open = geodewdt_open, + .release = geodewdt_release, +}; + +static struct miscdevice geodewdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &geodewdt_fops +}; + +static int __devinit +geodewdt_probe(struct platform_device *dev) +{ + int ret, timer; + + timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, + MFGPT_DOMAIN_WORKING, THIS_MODULE); + + if (timer == -1) { + printk(KERN_ERR "geodewdt: No timers were available\n"); + return -ENODEV; + } + + wdt_timer = timer; + + /* Set up the timer */ + + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, + GEODEWDT_SCALE | (3 << 8)); + + /* Set up comparator 2 to reset when the event fires */ + geode_mfgpt_toggle_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET, 1); + + /* Set up the initial timeout */ + + geode_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, + timeout * GEODEWDT_HZ); + + ret = misc_register(&geodewdt_miscdev); + + return ret; +} + +static int __devexit +geodewdt_remove(struct platform_device *dev) +{ + misc_deregister(&geodewdt_miscdev); + return 0; +} + +static void +geodewdt_shutdown(struct platform_device *dev) +{ + geodewdt_disable(); +} + +static struct platform_driver geodewdt_driver = { + .probe = geodewdt_probe, + .remove = __devexit_p(geodewdt_remove), + .shutdown = geodewdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init +geodewdt_init(void) +{ + int ret; + + ret = platform_driver_register(&geodewdt_driver); + if (ret) + return ret; + + geodewdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); + if (IS_ERR(geodewdt_platform_device)) { + ret = PTR_ERR(geodewdt_platform_device); + goto err; + } + + return 0; +err: + platform_driver_unregister(&geodewdt_driver); + return ret; +} + +static void __exit +geodewdt_exit(void) +{ + platform_device_unregister(geodewdt_platform_device); + platform_driver_unregister(&geodewdt_driver); +} + +module_init(geodewdt_init); +module_exit(geodewdt_exit); + +MODULE_AUTHOR("Advanced Micro Devices, Inc"); +MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index a0e6809..b4febc7 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(0); + 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/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c new file mode 100644 index 0000000..dec1b59 --- /dev/null +++ b/drivers/watchdog/rc32434_wdt.c @@ -0,0 +1,333 @@ +/* + * IDT Interprise 79RC32434 watchdog driver + * + * Copyright (C) 2006, Ondrej Zajicek + * Copyright (C) 2008, Florian Fainelli + * + * based on + * SoftDog 0.05: A Software Watchdog Device + * + * (c) Copyright 1996 Alan Cox , 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_TIMEOUT 20 +#define RC32434_WDT_INTERVAL (15 * HZ) + +#define VERSION "0.2" + +static struct { + struct completion stop; + int running; + struct timer_list timer; + int queue; + int default_ticks; + unsigned long inuse; +} rc32434_wdt_device; + +static struct integ __iomem *wdt_reg; +static int ticks = 100 * HZ; + +static int expect_close; +static int timeout; + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + + +static void rc32434_wdt_start(void) +{ + u32 val; + + writel(0, (void *)(&wdt_reg->wtcount)); + + val = RC32434_ERR_WRE; + writel(readl(&wdt_reg->errcs) | val, (void *)&wdt_reg->errcs); + + val = RC32434_WTC_EN; + writel(readl(&wdt_reg->wtc) | val, (void *)&wdt_reg->wtc); +} + +static void rc32434_wdt_stop(void) +{ + u32 val; + + val = ~RC32434_WTC_EN; + writel(readl(&wdt_reg->wtc) & val, (void *)&wdt_reg->wtc); + + val = ~RC32434_ERR_WRE; + writel(readl(&wdt_reg->errcs) & val, (void *)&wdt_reg->errcs); +} + +static void rc32434_wdt_set(int new_timeout) +{ + u32 cmp = new_timeout * HZ; + u32 state, val; + + timeout = new_timeout; + /* + * store and disable WTC + */ + state = (u32)(readl(&wdt_reg->wtc) & RC32434_WTC_EN); + val = ~RC32434_WTC_EN; + writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc); + + writel(0, &wdt_reg->wtcount); + writel(cmp, &wdt_reg->wtcompare); + + /* + * restore WTC + */ + + writel(readl(&wdt_reg->wtc) | state, &wdt_reg); +} + +static void rc32434_wdt_reset(void) +{ + ticks = rc32434_wdt_device.default_ticks; +} + +static void rc32434_wdt_update(unsigned long unused) +{ + if (rc32434_wdt_device.running) + ticks--; + + writel(0, &wdt_reg->wtcount); + + if (rc32434_wdt_device.queue && ticks) + mod_timer(&rc32434_wdt_device.timer, + jiffies + RC32434_WDT_INTERVAL); + else + complete(&rc32434_wdt_device.stop); +} + +static int rc32434_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &rc32434_wdt_device.inuse)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + return nonseekable_open(inode, file); +} + +static int rc32434_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close && nowayout == 0) { + rc32434_wdt_stop(); + printk(KERN_INFO KBUILD_MODNAME ": disabling watchdog timer\n"); + module_put(THIS_MODULE); + } else + printk(KERN_CRIT KBUILD_MODNAME + ": device closed unexpectedly. WDT will not stop !\n"); + + clear_bit(0, &rc32434_wdt_device.inuse); + return 0; +} + +static ssize_t rc32434_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + 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; + } + } + rc32434_wdt_update(0); + return len; + } + return 0; +} + +static int rc32434_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int new_timeout; + unsigned int value; + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "RC32434_WDT Watchdog", + }; + switch (cmd) { + case WDIOC_KEEPALIVE: + rc32434_wdt_reset(); + break; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (copy_to_user(argp, &value, sizeof(int))) + return -EFAULT; + break; + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + break; + case WDIOC_SETOPTIONS: + if (copy_from_user(&value, argp, sizeof(int))) + return -EFAULT; + switch (value) { + case WDIOS_ENABLECARD: + rc32434_wdt_start(); + break; + case WDIOS_DISABLECARD: + rc32434_wdt_stop(); + default: + return -EINVAL; + } + break; + case WDIOC_SETTIMEOUT: + if (copy_from_user(&new_timeout, argp, sizeof(int))) + return -EFAULT; + if (new_timeout < 1) + return -EINVAL; + if (new_timeout > MAX_TIMEOUT) + return -EINVAL; + rc32434_wdt_set(new_timeout); + case WDIOC_GETTIMEOUT: + return copy_to_user(argp, &timeout, sizeof(int)); + default: + return -ENOTTY; + } + + return 0; +} + +static struct file_operations rc32434_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = rc32434_wdt_write, + .ioctl = rc32434_wdt_ioctl, + .open = rc32434_wdt_open, + .release = rc32434_wdt_release, +}; + +static struct miscdevice rc32434_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &rc32434_wdt_fops, +}; + +static char banner[] __initdata = KERN_INFO KBUILD_MODNAME + ": Watchdog Timer version " VERSION ", timer margin: %d sec\n"; + +static int rc32434_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb500_wdt_res"); + if (!r) { + printk(KERN_ERR KBUILD_MODNAME + "failed to retrieve resources\n"); + return -ENODEV; + } + + wdt_reg = ioremap_nocache(r->start, r->end - r->start); + if (!wdt_reg) { + printk(KERN_ERR KBUILD_MODNAME + "failed to remap I/O resources\n"); + return -ENXIO; + } + + ret = misc_register(&rc32434_wdt_miscdev); + + if (ret < 0) { + printk(KERN_ERR KBUILD_MODNAME + "failed to register watchdog device\n"); + goto unmap; + } + + init_completion(&rc32434_wdt_device.stop); + rc32434_wdt_device.queue = 0; + + clear_bit(0, &rc32434_wdt_device.inuse); + + setup_timer(&rc32434_wdt_device.timer, rc32434_wdt_update, 0L); + + rc32434_wdt_device.default_ticks = ticks; + rc32434_wdt_set(RC32434_WDT_INTERVAL); + rc32434_wdt_start(); + + printk(banner, timeout); +unmap: + iounmap((void *)wdt_reg); + + return 0; +} + +static int rc32434_wdt_remove(struct platform_device *pdev) +{ + if (rc32434_wdt_device.queue) { + rc32434_wdt_device.queue = 0; + wait_for_completion(&rc32434_wdt_device.stop); + } + misc_deregister(&rc32434_wdt_miscdev); + + iounmap((void *)wdt_reg); + + return 0; +} + +static struct platform_driver rc32434_wdt = { + .probe = rc32434_wdt_probe, + .remove = rc32434_wdt_remove, + .driver = { + .name = "rc32434_wdt", + } +}; + +static int __init rc32434_wdt_init(void) +{ + return platform_driver_register(&rc32434_wdt); +} + +static void __exit rc32434_wdt_exit(void) +{ + platform_driver_unregister(&rc32434_wdt); +} + +module_init(rc32434_wdt_init); +module_exit(rc32434_wdt_exit); + +MODULE_AUTHOR("Ondrej Zajicek ," + "Florian Fainelli "); +MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c new file mode 100644 index 0000000..72212b1 --- /dev/null +++ b/drivers/watchdog/rdc321x_wdt.c @@ -0,0 +1,271 @@ +/* + * RDC321x watchdog driver + * + * Copyright (C) 2007 Florian Fainelli + * + * This driver is highly inspired from the cpu5_wdt driver + * + * 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 distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RDC_WDT_MASK 0x80000000 /* Mask */ +#define RDC_WDT_EN 0x00800000 /* Enable bit */ +#define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */ +#define RDC_WDT_RST 0x00100000 /* Reset bit */ +#define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */ +#define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */ +#define RDC_WDT_CNT 0x00000001 /* WDT count */ + +#define RDC_CLS_TMR 0x80003844 /* Clear timer */ + +#define RDC_WDT_INTERVAL (HZ/10+1) + +static int ticks = 1000; + +/* some device data */ + +static struct { + struct completion stop; + int running; + struct timer_list timer; + int queue; + int default_ticks; + unsigned long inuse; +} rdc321x_wdt_device; + +/* generic helper functions */ + +static void rdc321x_wdt_trigger(unsigned long unused) +{ + if (rdc321x_wdt_device.running) + ticks--; + + /* keep watchdog alive */ + outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA), + RDC3210_CFGREG_DATA); + + /* requeue?? */ + if (rdc321x_wdt_device.queue && ticks) + mod_timer(&rdc321x_wdt_device.timer, + jiffies + RDC_WDT_INTERVAL); + else { + /* ticks doesn't matter anyway */ + complete(&rdc321x_wdt_device.stop); + } + +} + +static void rdc321x_wdt_reset(void) +{ + ticks = rdc321x_wdt_device.default_ticks; +} + +static void rdc321x_wdt_start(void) +{ + if (!rdc321x_wdt_device.queue) { + rdc321x_wdt_device.queue = 1; + + /* Clear the timer */ + outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR); + + /* Enable watchdog and set the timeout to 81.92 us */ + outl(RDC_WDT_EN | RDC_WDT_CNT, RDC3210_CFGREG_DATA); + + mod_timer(&rdc321x_wdt_device.timer, + jiffies + RDC_WDT_INTERVAL); + } + + /* if process dies, counter is not decremented */ + rdc321x_wdt_device.running++; +} + +static int rdc321x_wdt_stop(void) +{ + if (rdc321x_wdt_device.running) + rdc321x_wdt_device.running = 0; + + ticks = rdc321x_wdt_device.default_ticks; + + return -EIO; +} + +/* filesystem operations */ +static int rdc321x_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &rdc321x_wdt_device.inuse)) + return -EBUSY; + + return nonseekable_open(inode, file); +} + +static int rdc321x_wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &rdc321x_wdt_device.inuse); + return 0; +} + +static int rdc321x_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + unsigned int value; + static struct watchdog_info ident = { + .options = WDIOF_CARDRESET, + .identity = "RDC321x WDT", + }; + + switch (cmd) { + case WDIOC_KEEPALIVE: + rdc321x_wdt_reset(); + break; + case WDIOC_GETSTATUS: + /* Read the value from the DATA register */ + value = inl(RDC3210_CFGREG_DATA); + if (copy_to_user(argp, &value, sizeof(int))) + return -EFAULT; + break; + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + break; + case WDIOC_SETOPTIONS: + if (copy_from_user(&value, argp, sizeof(int))) + return -EFAULT; + switch (value) { + case WDIOS_ENABLECARD: + rdc321x_wdt_start(); + break; + case WDIOS_DISABLECARD: + return rdc321x_wdt_stop(); + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + +static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (!count) + return -EIO; + + rdc321x_wdt_reset(); + + return count; +} + +static const struct file_operations rdc321x_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = rdc321x_wdt_ioctl, + .open = rdc321x_wdt_open, + .write = rdc321x_wdt_write, + .release = rdc321x_wdt_release, +}; + +static struct miscdevice rdc321x_wdt_misc = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &rdc321x_wdt_fops, +}; + +static int __devinit rdc321x_wdt_probe(struct platform_device *pdev) +{ + int err; + + err = misc_register(&rdc321x_wdt_misc); + if (err < 0) { + printk(KERN_ERR PFX "watchdog misc_register failed\n"); + return err; + } + + /* Reset the watchdog */ + outl(RDC_WDT_RST, RDC3210_CFGREG_DATA); + + init_completion(&rdc321x_wdt_device.stop); + rdc321x_wdt_device.queue = 0; + + clear_bit(0, &rdc321x_wdt_device.inuse); + + setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0); + + rdc321x_wdt_device.default_ticks = ticks; + + printk(KERN_INFO PFX "watchdog init success\n"); + + return 0; +} + +static int rdc321x_wdt_remove(struct platform_device *pdev) +{ + if (rdc321x_wdt_device.queue) { + rdc321x_wdt_device.queue = 0; + wait_for_completion(&rdc321x_wdt_device.stop); + } + + misc_deregister(&rdc321x_wdt_misc); + + return 0; +} + +static struct platform_driver rdc321x_wdt_driver = { + .probe = rdc321x_wdt_probe, + .remove = rdc321x_wdt_remove, + .driver = { + .owner = THIS_MODULE, + .name = "rdc321x-wdt", + }, +}; + +static int __init rdc321x_wdt_init(void) +{ + return platform_driver_register(&rdc321x_wdt_driver); +} + +static void __exit rdc321x_wdt_exit(void) +{ + platform_driver_unregister(&rdc321x_wdt_driver); +} + +module_init(rdc321x_wdt_init); +module_exit(rdc321x_wdt_exit); + +MODULE_AUTHOR("Florian Fainelli "); +MODULE_DESCRIPTION("RDC321x watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 011bcfe..217fc0b 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(size_t sizeof_priv); +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 */