GIT 715d595ea488960b94045d99a32ae8e3ffa6167b git+ssh://master.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog-mm.git 715d595ea488960b94045d99a32ae8e3ffa6167b commit 715d595ea488960b94045d99a32ae8e3ffa6167b Author: Florian Fainelli Date: Thu Apr 3 20:25:06 2008 +0200 [WATCHDOG] rdc321x_wdt: protect reads and writes with spinlocks Since we are reading and writing values to the PCI configuration register on the R-321x SoC, it is not safe to do so without spinlock protection. Signed-off-by: Florian Fainelli Signed-off-by: Wim Van Sebroeck commit 9f0100ed84f22bbf0850ec09d483101d9d0cdf18 Author: Andrew Paprocki Date: Wed Apr 2 02:43:19 2008 -0400 [WATCHDOG] it8712f_wdt Zero MSB timeout byte when disabling watchdog I noticed this while testing the latest code. I'm not sure if it is required, but the normal (or LSB) timeout value is set to zero, so the MSB should be as well to stay consistent. If the chip revision is >= 8, set MSB of the 16-bit timeout value to zero when disabling the watchdog in it8712f_wdt_disable(). Signed-off-by: Andrew Paprocki Signed-off-by: Wim Van Sebroeck Signed-off-by: Andrew Morton commit 9edfa4cbe7317556486b46e7a4967814cb9ba406 Author: Andrew Morton Date: Thu Mar 27 14:16:30 2008 -0700 [WATCHDOG] hpwdt: git-watchdog: fix build drivers/watchdog/hpwdt.c: In function 'hpwdt_pretimeout': drivers/watchdog/hpwdt.c:424: error: 'cur_rom_addr' undeclared (first use in this function) drivers/watchdog/hpwdt.c:424: error: (Each undeclared identifier is reported only once drivers/watchdog/hpwdt.c:424: error: for each function it appears in.) Signed-off-by: Andrew Morton Signed-off-by: Wim Van Sebroeck commit bb74cbde8f424cb1b95692cb76c09b8cc2295ebb Author: Mike Frysinger Date: Thu Mar 27 11:53:32 2008 -0700 [WATCHDOG] Blackfin Watchdog Driver: split platform device/driver - split platform device/driver registering from actual watchdog device/driver registering so that we can cleanly load/unload - fixup __initdata with __initconst and __devinitdata with __devinitconst Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Wim Van Sebroeck commit 083d5c0f8314b851a08d2f28ded65c9f701b24a3 Author: Mingarelli, Thomas Date: Tue Mar 25 17:17:30 2008 +0000 [WATCHDOG] hpwdt: Fix NMI handling. I need to just return in case it's not my NMI so someone else can take a look at it (and reset die_nmi_called to 0 in case I actually do get one that's mine to handle). Signed-off-by: Thomas Mingarelli Signed-off-by: Wim Van Sebroeck commit 94190fc9337d1ad50c4d616754eea1b986fc97f1 Author: Samuel Tardieu Date: Wed Mar 12 14:28:03 2008 +0100 [WATCHDOG] Add w83697h_wdt early_disable option Pádraig Brady requested the possibility of not disabling the watchdog at module load time or kernel boot time if it had been previously enabled in the bios. It may help rebooting the machine if it freezes before the userland daemon kicks in. Signed-off-by: Samuel Tardieu Cc: Pádraig Brady Signed-off-by: Wim Van Sebroeck commit b1a6d2845d655c5cbdfe7c020a7abd8a2ed9a55b Author: Samuel Tardieu Date: Wed Mar 12 14:28:02 2008 +0100 [WATCHDOG] Make w83697h_wdt timeout option string similar to others Signed-off-by: Samuel Tardieu Signed-off-by: Wim Van Sebroeck commit 783c0ee61608886a62f3d7a805a15a23828ec6c4 Author: Samuel Tardieu Date: Wed Mar 12 14:28:01 2008 +0100 [WATCHDOG] Make w83697h_wdt void-like functions void Some non-exported functions always returned 0. Mark them void instead. Signed-off-by: Samuel Tardieu Signed-off-by: Wim Van Sebroeck commit fad7da46260b23ea4ef194a61e7edc2e8daf143d Author: Wim Van Sebroeck Date: Sun Mar 9 12:03:17 2008 +0000 [WATCHDOG] Fix AMD Geode CS5535/CS5536 Watchdog Kconfig spacing Signed-off-by: Wim Van Sebroeck commit 5febd3eadd26323227008635bf7f1305a849bc29 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/bfin_wdt.c | 111 +++++--- 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/hpwdt.c | 27 +- drivers/watchdog/iTCO_wdt.c | 247 +++++------------- drivers/watchdog/it8712f_wdt.c | 21 ++ drivers/watchdog/rc32434_wdt.c | 333 +++++++++++++++++++++++ drivers/watchdog/rdc321x_wdt.c | 285 ++++++++++++++++++++ drivers/watchdog/w83697hf_wdt.c | 38 +++- include/linux/watchdog.h | 49 ++++ 15 files changed, 1923 insertions(+), 245 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 254d115..379d2d9 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/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c index 1237113..03b3e3d 100644 --- a/drivers/watchdog/bfin_wdt.c +++ b/drivers/watchdog/bfin_wdt.c @@ -29,7 +29,8 @@ #define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) #define stampit() stamp("here i am") -#define pr_init(fmt, args...) ({ static const __initdata char __fmt[] = fmt; printk(__fmt, ## args); }) +#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) +#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) #define WATCHDOG_NAME "bfin-wdt" #define PFX WATCHDOG_NAME ": " @@ -377,20 +378,6 @@ static int bfin_wdt_resume(struct platform_device *pdev) # define bfin_wdt_resume NULL #endif -static struct platform_device bfin_wdt_device = { - .name = WATCHDOG_NAME, - .id = -1, -}; - -static struct platform_driver bfin_wdt_driver = { - .driver = { - .name = WATCHDOG_NAME, - .owner = THIS_MODULE, - }, - .suspend = bfin_wdt_suspend, - .resume = bfin_wdt_resume, -}; - static const struct file_operations bfin_wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -418,11 +405,67 @@ static struct notifier_block bfin_wdt_notifier = { }; /** - * bfin_wdt_init - Initialize module + * bfin_wdt_probe - Initialize module * - * Registers the device and notifier handler. Actual device + * Registers the misc device and notifier handler. Actual device * initialization is handled by bfin_wdt_open(). */ +static int __devinit bfin_wdt_probe(struct platform_device *pdev) +{ + int ret; + + ret = register_reboot_notifier(&bfin_wdt_notifier); + if (ret) { + pr_devinit(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); + return ret; + } + + ret = misc_register(&bfin_wdt_miscdev); + if (ret) { + pr_devinit(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&bfin_wdt_notifier); + return ret; + } + + pr_devinit(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + + return 0; +} + +/** + * bfin_wdt_remove - Initialize module + * + * Unregisters the misc device and notifier handler. Actual device + * deinitialization is handled by bfin_wdt_close(). + */ +static int __devexit bfin_wdt_remove(struct platform_device *pdev) +{ + misc_deregister(&bfin_wdt_miscdev); + unregister_reboot_notifier(&bfin_wdt_notifier); + return 0; +} + +static struct platform_device *bfin_wdt_device; + +static struct platform_driver bfin_wdt_driver = { + .probe = bfin_wdt_probe, + .remove = __devexit_p(bfin_wdt_remove), + .suspend = bfin_wdt_suspend, + .resume = bfin_wdt_resume, + .driver = { + .name = WATCHDOG_NAME, + .owner = THIS_MODULE, + }, +}; + +/** + * bfin_wdt_init - Initialize module + * + * Checks the module params and registers the platform device & driver. + * Real work is in the platform probe function. + */ static int __init bfin_wdt_init(void) { int ret; @@ -436,44 +479,32 @@ static int __init bfin_wdt_init(void) /* Since this is an on-chip device and needs no board-specific * resources, we'll handle all the platform device stuff here. */ - ret = platform_device_register(&bfin_wdt_device); - if (ret) - return ret; - - ret = platform_driver_probe(&bfin_wdt_driver, NULL); - if (ret) - return ret; - - ret = register_reboot_notifier(&bfin_wdt_notifier); + ret = platform_driver_register(&bfin_wdt_driver); if (ret) { - pr_init(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); + pr_init(KERN_ERR PFX "unable to register driver\n"); return ret; } - ret = misc_register(&bfin_wdt_miscdev); - if (ret) { - pr_init(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - unregister_reboot_notifier(&bfin_wdt_notifier); - return ret; + bfin_wdt_device = platform_device_register_simple(WATCHDOG_NAME, -1, NULL, 0); + if (IS_ERR(bfin_wdt_device)) { + pr_init(KERN_ERR PFX "unable to register device\n"); + platform_driver_unregister(&bfin_wdt_driver); + return PTR_ERR(bfin_wdt_device); } - pr_init(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - return 0; } /** * bfin_wdt_exit - Deinitialize module * - * Unregisters the device and notifier handler. Actual device - * deinitialization is handled by bfin_wdt_close(). + * Back out the platform device & driver steps. Real work is in the + * platform remove function. */ static void __exit bfin_wdt_exit(void) { - misc_deregister(&bfin_wdt_miscdev); - unregister_reboot_notifier(&bfin_wdt_notifier); + platform_device_unregister(bfin_wdt_device); + platform_driver_unregister(&bfin_wdt_driver); } module_init(bfin_wdt_init); 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/hpwdt.c b/drivers/watchdog/hpwdt.c index 6483d10..6a63535 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -418,23 +418,20 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, static unsigned long rom_pl; static int die_nmi_called; - if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) - return NOTIFY_OK; - - spin_lock_irqsave(&rom_lock, rom_pl); - if (!die_nmi_called) - asminline_call(&cmn_regs, cru_rom_addr); - die_nmi_called = 1; - spin_unlock_irqrestore(&rom_lock, rom_pl); - if (cmn_regs.u1.ral == 0) { - printk(KERN_WARNING "hpwdt: An NMI occurred, " - "but unable to determine source.\n"); - } else { - panic("An NMI occurred, please see the Integrated " - "Management Log for details.\n"); + if (ulReason == DIE_NMI || ulReason == DIE_NMI_IPI) { + spin_lock_irqsave(&rom_lock, rom_pl); + if (!die_nmi_called) + asminline_call(&cmn_regs, cru_rom_addr); + die_nmi_called = 1; + spin_unlock_irqrestore(&rom_lock, rom_pl); + if (cmn_regs.u1.ral != 0) { + panic("An NMI occurred, please see the Integrated " + "Management Log for details.\n"); + } } - return NOTIFY_STOP; + die_nmi_called = 0; + return NOTIFY_DONE; } /* 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/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 445b7e8..0ed17bb 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -111,6 +111,15 @@ superio_inw(int reg) return val; } +static void +superio_outw(int val, int reg) +{ + outb(reg++, REG); + outb((val >> 8) & 0xff, VAL); + outb(reg, REG); + outb(val & 0xff, VAL); +} + static inline void superio_select(int ldn) { @@ -161,8 +170,14 @@ it8712f_wdt_update_margin(void) superio_outb(config, WDT_CONFIG); if (revision >= 0x08) +<<<<<<< HEAD:drivers/watchdog/it8712f_wdt.c superio_outb(units >> 8, WDT_TIMEOUT + 1); superio_outb(units, WDT_TIMEOUT); +======= + superio_outw(units, WDT_TIMEOUT); + else + superio_outb(units, WDT_TIMEOUT); +>>>>>>> FETCH_HEAD:drivers/watchdog/it8712f_wdt.c } static int @@ -283,9 +298,15 @@ it8712f_wdt_ioctl(struct inode *inode, struct file *file, if (get_user(value, p)) return -EFAULT; if (value < 1) +<<<<<<< HEAD:drivers/watchdog/it8712f_wdt.c + return -EINVAL; + if (value > (max_units * 60)) + return -EINVAL; +======= return -EINVAL; if (value > (max_units * 60)) return -EINVAL; +>>>>>>> FETCH_HEAD:drivers/watchdog/it8712f_wdt.c margin = value; superio_enter(); superio_select(LDN_GPIO); 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..9108efa --- /dev/null +++ b/drivers/watchdog/rdc321x_wdt.c @@ -0,0 +1,285 @@ +/* + * 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; + spinlock_t lock; +} rdc321x_wdt_device; + +/* generic helper functions */ + +static void rdc321x_wdt_trigger(unsigned long unused) +{ + unsigned long flags; + + if (rdc321x_wdt_device.running) + ticks--; + + /* keep watchdog alive */ + spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); + outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA), + RDC3210_CFGREG_DATA); + spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + + /* 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) +{ + unsigned long flags; + + if (!rdc321x_wdt_device.queue) { + rdc321x_wdt_device.queue = 1; + + /* Clear the timer */ + spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); + 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); + spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + + 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", + }; + unsigned long flags; + + switch (cmd) { + case WDIOC_KEEPALIVE: + rdc321x_wdt_reset(); + break; + case WDIOC_GETSTATUS: + /* Read the value from the DATA register */ + spin_lock_irqsave(&rdc321x_wdt_device.lock, flags); + value = inl(RDC3210_CFGREG_DATA); + spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags); + 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; + } + + spin_lock_init(&rdc321x_wdt_device.lock); + + /* 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/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c index c622a0e..528b882 100644 --- a/drivers/watchdog/w83697hf_wdt.c +++ b/drivers/watchdog/w83697hf_wdt.c @@ -44,6 +44,7 @@ #define WATCHDOG_NAME "w83697hf/hg WDT" #define PFX WATCHDOG_NAME ": " #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ +#define WATCHDOG_EARLY_DISABLE 1 /* Disable until userland kicks in */ static unsigned long wdt_is_open; static char expect_close; @@ -56,12 +57,16 @@ MODULE_PARM_DESC(wdt_io, "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect) static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) "."); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255 (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 int early_disable = WATCHDOG_EARLY_DISABLE; +module_param(early_disable, int, 0); +MODULE_PARM_DESC(early_disable, "Watchdog gets disabled at boot time (default=" __MODULE_STRING(WATCHDOG_EARLY_DISABLE) ")"); + /* * Kernel methods. */ @@ -140,7 +145,7 @@ w83697hf_init(void) w83697hf_deselect_wdt(); } -static int +static void wdt_ping(void) { spin_lock(&io_lock); @@ -150,10 +155,9 @@ wdt_ping(void) w83697hf_deselect_wdt(); spin_unlock(&io_lock); - return 0; } -static int +static void wdt_enable(void) { spin_lock(&io_lock); @@ -164,10 +168,9 @@ wdt_enable(void) w83697hf_deselect_wdt(); spin_unlock(&io_lock); - return 0; } -static int +static void wdt_disable(void) { spin_lock(&io_lock); @@ -178,7 +181,22 @@ wdt_disable(void) w83697hf_deselect_wdt(); spin_unlock(&io_lock); - return 0; +} + +static unsigned char +wdt_running(void) +{ + unsigned char t; + + spin_lock(&io_lock); + w83697hf_select_wdt(); + + t = w83697hf_get_reg(0xF4); /* Read timer */ + + w83697hf_deselect_wdt(); + spin_unlock(&io_lock); + + return t; } static int @@ -397,7 +415,11 @@ wdt_init(void) } w83697hf_init(); - wdt_disable(); /* Disable watchdog until first use */ + if (early_disable) { + if (wdt_running()) + printk (KERN_WARNING PFX "Stopping previously enabled watchdog until userland kicks in\n"); + wdt_disable(); + } if (wdt_set_heartbeat(timeout)) { wdt_set_heartbeat(WATCHDOG_TIMEOUT); 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 */