Commit 6d03a68e authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog

* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog: (33 commits)
  [WATCHDOG] remove experimental on iTCO_wdt.c
  [WATCHDOG] Atmel AT91RM9200 rename.
  [WATCHDOG] includes for sample watchdog program.
  [WATCHDOG] watchdog/iTCO_wdt: fix bug related to gcc uninit warning
  [WATCHDOG] add ich8 support to iTCO_wdt.c (patch 2)
  [WATCHDOG] add ich8 support to iTCO_wdt.c
  [WATCHDOG] ioremap balanced with iounmap for drivers/char/watchdog/s3c2410_wdt.c
  [WATCHDOG] w83697hf/hg WDT driver - Kconfig patch
  [WATCHDOG] w83697hf/hg WDT driver - autodetect patch
  [WATCHDOG] w83697hf/hg WDT driver - patch 16
  [WATCHDOG] w83697hf/hg WDT driver - patch 15
  [WATCHDOG] w83697hf/hg WDT driver - patch 14
  [WATCHDOG] w83697hf/hg WDT driver - patch 13
  [WATCHDOG] w83697hf/hg WDT driver - patch 12
  [WATCHDOG] w83697hf/hg WDT driver - patch 11
  [WATCHDOG] w83697hf/hg WDT driver - patch 10
  [WATCHDOG] w83697hf/hg WDT driver - patch 9
  [WATCHDOG] w83697hf/hg WDT driver - patch 8
  [WATCHDOG] w83697hf/hg WDT driver - patch 7
  [WATCHDOG] w83697hf/hg WDT driver - patch 6
  ...
parents 0c0e4668 cbf40d3f
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
......
...@@ -577,7 +577,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y ...@@ -577,7 +577,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
# Watchdog Device Drivers # Watchdog Device Drivers
# #
# CONFIG_SOFT_WATCHDOG is not set # CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT91_WATCHDOG=y CONFIG_AT91RM9200_WATCHDOG=y
# #
# USB-based Watchdog Cards # USB-based Watchdog Cards
......
...@@ -558,7 +558,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y ...@@ -558,7 +558,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
# Watchdog Device Drivers # Watchdog Device Drivers
# #
# CONFIG_SOFT_WATCHDOG is not set # CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT91_WATCHDOG=y CONFIG_AT91RM9200_WATCHDOG=y
# #
# USB-based Watchdog Cards # USB-based Watchdog Cards
......
...@@ -615,7 +615,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y ...@@ -615,7 +615,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
# Watchdog Device Drivers # Watchdog Device Drivers
# #
# CONFIG_SOFT_WATCHDOG is not set # CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT91_WATCHDOG=y CONFIG_AT91RM9200_WATCHDOG=y
# #
# USB-based Watchdog Cards # USB-based Watchdog Cards
......
...@@ -615,7 +615,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y ...@@ -615,7 +615,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
# Watchdog Device Drivers # Watchdog Device Drivers
# #
# CONFIG_SOFT_WATCHDOG is not set # CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT91_WATCHDOG=y CONFIG_AT91RM9200_WATCHDOG=y
# #
# USB-based Watchdog Cards # USB-based Watchdog Cards
......
...@@ -560,7 +560,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y ...@@ -560,7 +560,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
# Watchdog Device Drivers # Watchdog Device Drivers
# #
# CONFIG_SOFT_WATCHDOG is not set # CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT91_WATCHDOG=y CONFIG_AT91RM9200_WATCHDOG=y
# CONFIG_NVRAM is not set # CONFIG_NVRAM is not set
# CONFIG_DTLK is not set # CONFIG_DTLK is not set
# CONFIG_R3964 is not set # CONFIG_R3964 is not set
......
...@@ -607,7 +607,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y ...@@ -607,7 +607,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y
# Watchdog Device Drivers # Watchdog Device Drivers
# #
# CONFIG_SOFT_WATCHDOG is not set # CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT91_WATCHDOG=y CONFIG_AT91RM9200_WATCHDOG=y
# #
# USB-based Watchdog Cards # USB-based Watchdog Cards
......
...@@ -13,7 +13,7 @@ config WATCHDOG ...@@ -13,7 +13,7 @@ config WATCHDOG
subsequently opening the file and then failing to write to it for subsequently opening the file and then failing to write to it for
longer than 1 minute will result in rebooting the machine. This longer than 1 minute will result in rebooting the machine. This
could be useful for a networked machine that needs to come back could be useful for a networked machine that needs to come back
online as fast as possible after a lock-up. There's both a watchdog on-line as fast as possible after a lock-up. There's both a watchdog
implementation entirely in software (which can sometimes fail to implementation entirely in software (which can sometimes fail to
reboot the machine) and a driver for hardware watchdog boards, which reboot the machine) and a driver for hardware watchdog boards, which
are more robust and can also keep track of the temperature inside are more robust and can also keep track of the temperature inside
...@@ -60,7 +60,7 @@ config SOFT_WATCHDOG ...@@ -60,7 +60,7 @@ config SOFT_WATCHDOG
# ARM Architecture # ARM Architecture
config AT91_WATCHDOG config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog" tristate "AT91RM9200 watchdog"
depends on WATCHDOG && ARCH_AT91RM9200 depends on WATCHDOG && ARCH_AT91RM9200
help help
...@@ -71,7 +71,7 @@ config 21285_WATCHDOG ...@@ -71,7 +71,7 @@ config 21285_WATCHDOG
tristate "DC21285 watchdog" tristate "DC21285 watchdog"
depends on WATCHDOG && FOOTBRIDGE depends on WATCHDOG && FOOTBRIDGE
help help
The Intel Footbridge chip contains a builtin watchdog circuit. Say Y The Intel Footbridge chip contains a built-in watchdog circuit. Say Y
here if you wish to use this. Alternatively say M to compile the here if you wish to use this. Alternatively say M to compile the
driver as a module, which will be called wdt285. driver as a module, which will be called wdt285.
...@@ -269,11 +269,11 @@ config IB700_WDT ...@@ -269,11 +269,11 @@ config IB700_WDT
Most people will say N. Most people will say N.
config IBMASR config IBMASR
tristate "IBM Automatic Server Restart" tristate "IBM Automatic Server Restart"
depends on WATCHDOG && X86 depends on WATCHDOG && X86
help help
This is the driver for the IBM Automatic Server Restart watchdog This is the driver for the IBM Automatic Server Restart watchdog
timer builtin into some eServer xSeries machines. timer built-in into some eServer xSeries machines.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called ibmasr. module will be called ibmasr.
...@@ -316,13 +316,16 @@ config I8XX_TCO ...@@ -316,13 +316,16 @@ config I8XX_TCO
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called i8xx_tco. module will be called i8xx_tco.
Note: This driver will be removed in the near future. Please
use the Intel TCO Timer/Watchdog driver.
config ITCO_WDT config ITCO_WDT
tristate "Intel TCO Timer/Watchdog (EXPERIMENTAL)" tristate "Intel TCO Timer/Watchdog"
depends on WATCHDOG && (X86 || IA64) && PCI && EXPERIMENTAL depends on WATCHDOG && (X86 || IA64) && PCI
---help--- ---help---
Hardware driver for the intel TCO timer based watchdog devices. Hardware driver for the intel TCO timer based watchdog devices.
These drivers are included in the Intel 82801 I/O Controller These drivers are included in the Intel 82801 I/O Controller
Hub family 'from ICH0 up to ICH7) and in the Intel 6300ESB Hub family (from ICH0 up to ICH8) and in the Intel 6300ESB
controller hub. controller hub.
The TCO (Total Cost of Ownership) timer is a watchdog timer The TCO (Total Cost of Ownership) timer is a watchdog timer
...@@ -395,6 +398,26 @@ config CPU5_WDT ...@@ -395,6 +398,26 @@ config CPU5_WDT
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cpu5wdt. module will be called cpu5wdt.
config SMSC37B787_WDT
tristate "Winbond SMsC37B787 Watchdog Timer"
depends on WATCHDOG && X86
---help---
This is the driver for the hardware watchdog component on the
Winbond SMsC37B787 chipset as used on the NetRunner Mainboard
from Vision Systems and maybe others.
This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain
amount of time.
Usually a userspace daemon will notify the kernel WDT driver that
userspace is still alive, at regular intervals.
To compile this driver as a module, choose M here: the
module will be called smsc37b787_wdt.
Most people will say N.
config W83627HF_WDT config W83627HF_WDT
tristate "W83627HF Watchdog Timer" tristate "W83627HF Watchdog Timer"
depends on WATCHDOG && X86 depends on WATCHDOG && X86
...@@ -410,6 +433,21 @@ config W83627HF_WDT ...@@ -410,6 +433,21 @@ config W83627HF_WDT
Most people will say N. Most people will say N.
config W83697HF_WDT
tristate "W83697HF/W83697HG Watchdog Timer"
depends on WATCHDOG && X86
---help---
This is the driver for the hardware watchdog on the W83697HF/HG
chipset as used in Dedibox/VIA motherboards (and likely others).
This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain
amount of time.
To compile this driver as a module, choose M here: the
module will be called w83697hf_wdt.
Most people will say N.
config W83877F_WDT config W83877F_WDT
tristate "W83877F (EMACS) Watchdog Timer" tristate "W83877F (EMACS) Watchdog Timer"
depends on WATCHDOG && X86 depends on WATCHDOG && X86
...@@ -443,7 +481,7 @@ config MACHZ_WDT ...@@ -443,7 +481,7 @@ config MACHZ_WDT
depends on WATCHDOG && X86 depends on WATCHDOG && X86
---help--- ---help---
If you are using a ZF Micro MachZ processor, say Y here, otherwise If you are using a ZF Micro MachZ processor, say Y here, otherwise
N. This is the driver for the watchdog timer builtin on that N. This is the driver for the watchdog timer built-in on that
processor using ZF-Logic interface. This watchdog simply watches processor using ZF-Logic interface. This watchdog simply watches
your kernel to make sure it doesn't freeze, and if it does, it your kernel to make sure it doesn't freeze, and if it does, it
reboots your computer after a certain amount of time. reboots your computer after a certain amount of time.
...@@ -472,7 +510,6 @@ config SBC_EPX_C3_WATCHDOG ...@@ -472,7 +510,6 @@ config SBC_EPX_C3_WATCHDOG
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called sbc_epx_c3. module will be called sbc_epx_c3.
# PowerPC Architecture # PowerPC Architecture
config 8xx_WDT config 8xx_WDT
...@@ -502,7 +539,7 @@ config WATCHDOG_RTAS ...@@ -502,7 +539,7 @@ config WATCHDOG_RTAS
help help
This driver adds watchdog support for the RTAS watchdog. This driver adds watchdog support for the RTAS watchdog.
To compile this driver as a module, choose M here. The module To compile this driver as a module, choose M here. The module
will be called wdrtas. will be called wdrtas.
# MIPS Architecture # MIPS Architecture
...@@ -556,7 +593,7 @@ config SH_WDT_MMAP ...@@ -556,7 +593,7 @@ config SH_WDT_MMAP
help help
If you say Y here, user applications will be able to mmap the If you say Y here, user applications will be able to mmap the
WDT/CPG registers. WDT/CPG registers.
#
# SPARC64 Architecture # SPARC64 Architecture
config WATCHDOG_CP1XXX config WATCHDOG_CP1XXX
......
...@@ -23,7 +23,7 @@ obj-$(CONFIG_WDTPCI) += wdt_pci.o ...@@ -23,7 +23,7 @@ obj-$(CONFIG_WDTPCI) += wdt_pci.o
obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture # ARM Architecture
obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o
...@@ -53,7 +53,9 @@ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o ...@@ -53,7 +53,9 @@ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o
......
...@@ -35,6 +35,10 @@ ...@@ -35,6 +35,10 @@
* 82801GDH (ICH7DH) : document number 307013-002, 307014-009, * 82801GDH (ICH7DH) : document number 307013-002, 307014-009,
* 82801GBM (ICH7-M) : document number 307013-002, 307014-009, * 82801GBM (ICH7-M) : document number 307013-002, 307014-009,
* 82801GHM (ICH7-M DH) : document number 307013-002, 307014-009, * 82801GHM (ICH7-M DH) : document number 307013-002, 307014-009,
* 82801HB (ICH8) : document number 313056-002, 313057-004,
* 82801HR (ICH8R) : document number 313056-002, 313057-004,
* 82801HH (ICH8DH) : document number 313056-002, 313057-004,
* 82801HO (ICH8DO) : document number 313056-002, 313057-004,
* 6300ESB (6300ESB) : document number 300641-003 * 6300ESB (6300ESB) : document number 300641-003
*/ */
...@@ -45,7 +49,7 @@ ...@@ -45,7 +49,7 @@
/* Module and version information */ /* Module and version information */
#define DRV_NAME "iTCO_wdt" #define DRV_NAME "iTCO_wdt"
#define DRV_VERSION "1.00" #define DRV_VERSION "1.00"
#define DRV_RELDATE "30-Jul-2006" #define DRV_RELDATE "08-Oct-2006"
#define PFX DRV_NAME ": " #define PFX DRV_NAME ": "
/* Includes */ /* Includes */
...@@ -85,6 +89,9 @@ enum iTCO_chipsets { ...@@ -85,6 +89,9 @@ enum iTCO_chipsets {
TCO_ICH7, /* ICH7 & ICH7R */ TCO_ICH7, /* ICH7 & ICH7R */
TCO_ICH7M, /* ICH7-M */ TCO_ICH7M, /* ICH7-M */
TCO_ICH7MDH, /* ICH7-M DH */ TCO_ICH7MDH, /* ICH7-M DH */
TCO_ICH8, /* ICH8 & ICH8R */
TCO_ICH8DH, /* ICH8DH */
TCO_ICH8DO, /* ICH8DO */
}; };
static struct { static struct {
...@@ -108,6 +115,9 @@ static struct { ...@@ -108,6 +115,9 @@ static struct {
{"ICH7 or ICH7R", 2}, {"ICH7 or ICH7R", 2},
{"ICH7-M", 2}, {"ICH7-M", 2},
{"ICH7-M DH", 2}, {"ICH7-M DH", 2},
{"ICH8 or ICH8R", 2},
{"ICH8DH", 2},
{"ICH8DO", 2},
{NULL,0} {NULL,0}
}; };
...@@ -135,6 +145,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = { ...@@ -135,6 +145,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7M }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7M },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7MDH }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH7MDH },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH8 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH8DH },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH8DO },
{ 0, }, /* End of list */ { 0, }, /* End of list */
}; };
MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl); MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl);
...@@ -355,7 +368,8 @@ static int iTCO_wdt_get_timeleft (int *time_left) ...@@ -355,7 +368,8 @@ static int iTCO_wdt_get_timeleft (int *time_left)
spin_unlock(&iTCO_wdt_private.io_lock); spin_unlock(&iTCO_wdt_private.io_lock);
*time_left = (val8 * 6) / 10; *time_left = (val8 * 6) / 10;
} } else
return -EINVAL;
return 0; return 0;
} }
...@@ -426,7 +440,6 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file, ...@@ -426,7 +440,6 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file,
{ {
int new_options, retval = -EINVAL; int new_options, retval = -EINVAL;
int new_heartbeat; int new_heartbeat;
int time_left;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
int __user *p = argp; int __user *p = argp;
static struct watchdog_info ident = { static struct watchdog_info ident = {
...@@ -486,6 +499,8 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file, ...@@ -486,6 +499,8 @@ static int iTCO_wdt_ioctl (struct inode *inode, struct file *file,
case WDIOC_GETTIMELEFT: case WDIOC_GETTIMELEFT:
{ {
int time_left;
if (iTCO_wdt_get_timeleft(&time_left)) if (iTCO_wdt_get_timeleft(&time_left))
return -EINVAL; return -EINVAL;
......
...@@ -380,18 +380,21 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -380,18 +380,21 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) { if (res == NULL) {
printk(KERN_INFO PFX "failed to get irq resource\n"); printk(KERN_INFO PFX "failed to get irq resource\n");
iounmap(wdt_base);
return -ENOENT; return -ENOENT;
} }
ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev); ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, pdev);
if (ret != 0) { if (ret != 0) {
printk(KERN_INFO PFX "failed to install irq (%d)\n", ret); printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
iounmap(wdt_base);
return ret; return ret;
} }
wdt_clock = clk_get(&pdev->dev, "watchdog"); wdt_clock = clk_get(&pdev->dev, "watchdog");
if (wdt_clock == NULL) { if (wdt_clock == NULL) {
printk(KERN_INFO PFX "failed to find watchdog clock source\n"); printk(KERN_INFO PFX "failed to find watchdog clock source\n");
iounmap(wdt_base);
return -ENOENT; return -ENOENT;
} }
...@@ -415,6 +418,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -415,6 +418,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (ret) { if (ret) {
printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n", printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret); WATCHDOG_MINOR, ret);
iounmap(wdt_base);
return ret; return ret;
} }
...@@ -451,6 +455,7 @@ static int s3c2410wdt_remove(struct platform_device *dev) ...@@ -451,6 +455,7 @@ static int s3c2410wdt_remove(struct platform_device *dev)
wdt_clock = NULL; wdt_clock = NULL;
} }
iounmap(wdt_base);
misc_deregister(&s3c2410wdt_miscdev); misc_deregister(&s3c2410wdt_miscdev);
return 0; return 0;
} }
......
/*
* SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
*
* Based on acquirewdt.c by Alan Cox <alan@redhat.com>
* and some other existing drivers
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* The authors do NOT admit liability nor provide warranty for
* any of this software. This material is provided "AS-IS" in
* the hope that it may be useful for others.
*
* (C) Copyright 2003-2006 Sven Anders <anders@anduras.de>
*
* History:
* 2003 - Created version 1.0 for Linux 2.4.x.
* 2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
* features. Released version 1.1
*
* Theory of operation:
*
* A Watchdog Timer (WDT) is a hardware circuit that can
* reset the computer system in case of a software fault.
* You probably knew that already.
*
* Usually a userspace daemon will notify the kernel WDT driver
* via the /dev/watchdog special device file that userspace is
* still alive, at regular intervals. When such a notification
* occurs, the driver will usually tell the hardware watchdog
* that everything is in order, and that the watchdog should wait
* for yet another little while to reset the system.
* If userspace fails (RAM error, kernel bug, whatever), the
* notifications cease to occur, and the hardware watchdog will
* reset the system (causing a reboot) after the timeout occurs.
*
* Create device with:
* mknod /dev/watchdog c 10 130
*
* For an example userspace keep-alive daemon, see:
* Documentation/watchdog/watchdog.txt
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
/* enable support for minutes as units? */
/* (does not always work correctly, so disabled by default!) */
#define SMSC_SUPPORT_MINUTES
#undef SMSC_SUPPORT_MINUTES
#define MAX_TIMEOUT 255
#define UNIT_SECOND 0
#define UNIT_MINUTE 1
#define MODNAME "smsc37b787_wdt: "
#define VERSION "1.1"
#define IOPORT 0x3F0
#define IOPORT_SIZE 2
#define IODEV_NO 8
static int unit = UNIT_SECOND; /* timer's unit */
static int timeout = 60; /* timeout value: default is 60 "units" */
static unsigned long timer_enabled = 0; /* is the timer enabled? */
static char expect_close; /* is the close expected? */
static spinlock_t io_lock; /* to guard the watchdog from io races */
static int nowayout = WATCHDOG_NOWAYOUT;
/* -- Low level function ----------------------------------------*/
/* unlock the IO chip */
static inline void open_io_config(void)
{
outb(0x55, IOPORT);
mdelay(1);
outb(0x55, IOPORT);
}
/* lock the IO chip */
static inline void close_io_config(void)
{
outb(0xAA, IOPORT);
}
/* select the IO device */
static inline void select_io_device(unsigned char devno)
{
outb(0x07, IOPORT);
outb(devno, IOPORT+1);
}
/* write to the control register */
static inline void write_io_cr(unsigned char reg, unsigned char data)
{
outb(reg, IOPORT);
outb(data, IOPORT+1);
}
/* read from the control register */
static inline char read_io_cr(unsigned char reg)
{
outb(reg, IOPORT);
return inb(IOPORT+1);
}
/* -- Medium level functions ------------------------------------*/
static inline void gpio_bit12(unsigned char reg)
{
// -- General Purpose I/O Bit 1.2 --
// Bit 0, In/Out: 0 = Output, 1 = Input
// Bit 1, Polarity: 0 = No Invert, 1 = Invert
// Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable
// Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
// 11 = Either Edge Triggered Intr. 2
// Bit 5/6 (Reserved)
// Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
write_io_cr(0xE2, reg);
}
static inline void gpio_bit13(unsigned char reg)
{
// -- General Purpose I/O Bit 1.3 --
// Bit 0, In/Out: 0 = Output, 1 = Input
// Bit 1, Polarity: 0 = No Invert, 1 = Invert
// Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable
// Bit 3, Function select: 0 = GPI/O, 1 = LED
// Bit 4-6 (Reserved)
// Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain
write_io_cr(0xE3, reg);
}
static inline void wdt_timer_units(unsigned char new_units)
{
// -- Watchdog timer units --
// Bit 0-6 (Reserved)
// Bit 7, WDT Time-out Value Units Select
// (0 = Minutes, 1 = Seconds)
write_io_cr(0xF1, new_units);
}
static inline void wdt_timeout_value(unsigned char new_timeout)
{
// -- Watchdog Timer Time-out Value --
// Bit 0-7 Binary coded units (0=Disabled, 1..255)
write_io_cr(0xF2, new_timeout);
}
static inline void wdt_timer_conf(unsigned char conf)
{
// -- Watchdog timer configuration --
// Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon Gameport I/O
// Bit 1 Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
// Bit 2 Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr.
// Bit 3 Reset the timer
// (Wrong in SMsC documentation? Given as: PowerLED Timout Enabled)
// Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
// 0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
write_io_cr(0xF3, conf);
}
static inline void wdt_timer_ctrl(unsigned char reg)
{
// -- Watchdog timer control --
// Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occured
// Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
// Bit 2 Force Timeout: 1 = Forces WD timeout event (self-cleaning)
// Bit 3 P20 Force Timeout enabled:
// 0 = P20 activity does not generate the WD timeout event
// 1 = P20 Allows rising edge of P20, from the keyboard
// controller, to force the WD timeout event.
// Bit 4 (Reserved)
// -- Soft power management --
// Bit 5 Stop Counter: 1 = Stop software power down counter
// set via register 0xB8, (self-cleaning)
// (Upon read: 0 = Counter running, 1 = Counter stopped)
// Bit 6 Restart Counter: 1 = Restart software power down counter
// set via register 0xB8, (self-cleaning)
// Bit 7 SPOFF: 1 = Force software power down (self-cleaning)
write_io_cr(0xF4, reg);
}
/* -- Higher level functions ------------------------------------*/
/* initialize watchdog */
static void wb_smsc_wdt_initialize(void)
{
unsigned char old;
spin_lock(&io_lock);
open_io_config();
select_io_device(IODEV_NO);
// enable the watchdog
gpio_bit13(0x08); // Select pin 80 = LED not GPIO
gpio_bit12(0x0A); // Set pin 79 = WDT not GPIO/Output/Polarity=Invert
// disable the timeout
wdt_timeout_value(0);
// reset control register
wdt_timer_ctrl(0x00);
// reset configuration register
wdt_timer_conf(0x00);
// read old (timer units) register
old = read_io_cr(0xF1) & 0x7F;
if (unit == UNIT_SECOND) old |= 0x80; // set to seconds
// set the watchdog timer units
wdt_timer_units(old);
close_io_config();
spin_unlock(&io_lock);
}
/* shutdown the watchdog */
static void wb_smsc_wdt_shutdown(void)
{
spin_lock(&io_lock);
open_io_config();
select_io_device(IODEV_NO);
// disable the watchdog
gpio_bit13(0x09);
gpio_bit12(0x09);
// reset watchdog config register
wdt_timer_conf(0x00);
// reset watchdog control register
wdt_timer_ctrl(0x00);
// disable timeout
wdt_timeout_value(0x00);
close_io_config();
spin_unlock(&io_lock);
}
/* set timeout => enable watchdog */
static void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
{
spin_lock(&io_lock);
open_io_config();
select_io_device(IODEV_NO);
// set Power LED to blink, if we enable the timeout
wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
// set timeout value
wdt_timeout_value(new_timeout);
close_io_config();
spin_unlock(&io_lock);
}
/* get timeout */
static unsigned char wb_smsc_wdt_get_timeout(void)
{
unsigned char set_timeout;
spin_lock(&io_lock);
open_io_config();
select_io_device(IODEV_NO);
set_timeout = read_io_cr(0xF2);
close_io_config();
spin_unlock(&io_lock);
return set_timeout;
}
/* disable watchdog */
static void wb_smsc_wdt_disable(void)
{
// set the timeout to 0 to disable the watchdog
wb_smsc_wdt_set_timeout(0);
}
/* enable watchdog by setting the current timeout */
static void wb_smsc_wdt_enable(void)
{
// set the current timeout...
wb_smsc_wdt_set_timeout(timeout);
}
/* reset the timer */
static void wb_smsc_wdt_reset_timer(void)
{
spin_lock(&io_lock);
open_io_config();
select_io_device(IODEV_NO);
// reset the timer
wdt_timeout_value(timeout);
wdt_timer_conf(0x08);
close_io_config();
spin_unlock(&io_lock);
}
/* return, if the watchdog is enabled (timeout is set...) */
static int wb_smsc_wdt_status(void)
{
return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
}
/* -- File operations -------------------------------------------*/
/* open => enable watchdog and set initial timeout */
static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
{
/* /dev/watchdog can only be opened once */
if (test_and_set_bit(0, &timer_enabled))
return -EBUSY;
if (nowayout)
__module_get(THIS_MODULE);
/* Reload and activate timer */
wb_smsc_wdt_enable();
printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
return nonseekable_open(inode, file);
}
/* close => shut off the timer */
static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
{
/* Shut off the timer. */
if (expect_close == 42) {
wb_smsc_wdt_disable();
printk(KERN_INFO MODNAME "Watchdog disabled, sleeping again...\n");
} else {
printk(KERN_CRIT MODNAME "Unexpected close, not stopping watchdog!\n");
wb_smsc_wdt_reset_timer();
}
clear_bit(0, &timer_enabled);
expect_close = 0;
return 0;
}
/* write => update the timer to keep the machine alive */
static ssize_t wb_smsc_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;
/* reset expect flag */
expect_close = 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_close = 42;
}
}
/* someone wrote to us, we should reload the timer */
wb_smsc_wdt_reset_timer();
}
return len;
}
/* ioctl => control interface */
static int wb_smsc_wdt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int new_timeout;
union {
struct watchdog_info __user *ident;
int __user *i;
} uarg;
static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "SMsC 37B787 Watchdog"
};
uarg.i = (int __user *)arg;
switch (cmd) {
default:
return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(uarg.ident, &ident,
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
return put_user(wb_smsc_wdt_status(), uarg.i);
case WDIOC_GETBOOTSTATUS:
return put_user(0, uarg.i);
case WDIOC_KEEPALIVE:
wb_smsc_wdt_reset_timer();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, uarg.i))
return -EFAULT;
// the API states this is given in secs
if (unit == UNIT_MINUTE)
new_timeout /= 60;
if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
return -EINVAL;
timeout = new_timeout;
wb_smsc_wdt_set_timeout(timeout);
// fall through and return the new timeout...
case WDIOC_GETTIMEOUT:
new_timeout = timeout;
if (unit == UNIT_MINUTE)
new_timeout *= 60;
return put_user(new_timeout, uarg.i);
case WDIOC_SETOPTIONS:
{
int options, retval = -EINVAL;
if (get_user(options, uarg.i))
return -EFAULT;
if (options & WDIOS_DISABLECARD) {
wb_smsc_wdt_disable();
retval = 0;
}
if (options & WDIOS_ENABLECARD) {
wb_smsc_wdt_enable();
retval = 0;
}
return retval;
}
}
}
/* -- Notifier funtions -----------------------------------------*/
static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
{
// set timeout to 0, to avoid possible race-condition
timeout = 0;
wb_smsc_wdt_disable();
}
return NOTIFY_DONE;
}
/* -- Module's structures ---------------------------------------*/
static struct file_operations wb_smsc_wdt_fops =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wb_smsc_wdt_write,
.ioctl = wb_smsc_wdt_ioctl,
.open = wb_smsc_wdt_open,
.release = wb_smsc_wdt_release,
};
static struct notifier_block wb_smsc_wdt_notifier =
{
.notifier_call = wb_smsc_wdt_notify_sys,
};
static struct miscdevice wb_smsc_wdt_miscdev =
{
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &wb_smsc_wdt_fops,
};
/* -- Module init functions -------------------------------------*/
/* module's "constructor" */
static int __init wb_smsc_wdt_init(void)
{
int ret;
spin_lock_init(&io_lock);
printk("SMsC 37B787 watchdog component driver " VERSION " initialising...\n");
if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
printk(KERN_ERR MODNAME "Unable to register IO port %#x\n", IOPORT);
ret = -EBUSY;
goto out_pnp;
}
// set new maximum, if it's too big
if (timeout > MAX_TIMEOUT)
timeout = MAX_TIMEOUT;
// init the watchdog timer
wb_smsc_wdt_initialize();
ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
if (ret) {
printk(KERN_ERR MODNAME "Unable to register reboot notifier err = %d\n", ret);
goto out_io;
}
ret = misc_register(&wb_smsc_wdt_miscdev);
if (ret) {
printk(KERN_ERR MODNAME "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
goto out_rbt;
}
// output info
printk(KERN_INFO MODNAME "Timeout set to %d %s.\n", timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
printk(KERN_INFO MODNAME "Watchdog initialized and sleeping (nowayout=%d)...\n", nowayout);
// ret = 0
out_clean:
return ret;
out_rbt:
unregister_reboot_notifier(&wb_smsc_wdt_notifier);
out_io:
release_region(IOPORT, IOPORT_SIZE);
out_pnp:
goto out_clean;
}
/* module's "destructor" */
static void __exit wb_smsc_wdt_exit(void)
{
/* Stop the timer before we leave */
if (!nowayout)
{
wb_smsc_wdt_shutdown();
printk(KERN_INFO MODNAME "Watchdog disabled.\n");
}
misc_deregister(&wb_smsc_wdt_miscdev);
unregister_reboot_notifier(&wb_smsc_wdt_notifier);
release_region(IOPORT, IOPORT_SIZE);
printk("SMsC 37B787 watchdog component driver removed.\n");
}
module_init(wb_smsc_wdt_init);
module_exit(wb_smsc_wdt_exit);
MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version " VERSION ")");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
#ifdef SMSC_SUPPORT_MINUTES
module_param(unit, int, 0);
MODULE_PARM_DESC(unit, "set unit to use, 0=seconds or 1=minutes, default is 0");
#endif
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
static unsigned long wdt_is_open; static unsigned long wdt_is_open;
static char expect_close; static char expect_close;
static spinlock_t io_lock;
/* You must set this - there is no sane way to probe for this board. */ /* You must set this - there is no sane way to probe for this board. */
static int wdt_io = 0x2E; static int wdt_io = 0x2E;
...@@ -110,12 +112,16 @@ w83627hf_init(void) ...@@ -110,12 +112,16 @@ w83627hf_init(void)
static void static void
wdt_ctrl(int timeout) wdt_ctrl(int timeout)
{ {
spin_lock(&io_lock);
w83627hf_select_wd_register(); w83627hf_select_wd_register();
outb_p(0xF6, WDT_EFER); /* Select CRF6 */ outb_p(0xF6, WDT_EFER); /* Select CRF6 */
outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */
w83627hf_unselect_wd_register(); w83627hf_unselect_wd_register();
spin_unlock(&io_lock);
} }
static int static int
...@@ -303,6 +309,8 @@ wdt_init(void) ...@@ -303,6 +309,8 @@ wdt_init(void)
{ {
int ret; int ret;
spin_lock_init(&io_lock);
printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF Super I/O chip initialising.\n"); printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF Super I/O chip initialising.\n");
if (wdt_set_heartbeat(timeout)) { if (wdt_set_heartbeat(timeout)) {
......
/*
* w83697hf/hg WDT driver
*
* (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
* (c) Copyright 2006 Marcus Junker <junker@anduras.de>
*
* Based on w83627hf_wdt.c which is based on advantechwdt.c
* which is based on wdt.c.
* Original copyright messages:
*
* (c) Copyright 2003 Pdraig Brady <P@draigBrady.com>
*
* (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
*
* (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Neither Marcus Junker nor ANDURAS AG admit liability nor provide
* warranty for any of this software. This material is provided
* "AS-IS" and at no charge.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#define WATCHDOG_NAME "w83697hf/hg WDT"
#define PFX WATCHDOG_NAME ": "
#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
static unsigned long wdt_is_open;
static char expect_close;
static spinlock_t io_lock;
/* You must set this - there is no sane way to probe for this board. */
static int wdt_io = 0x2e;
module_param(wdt_io, int, 0);
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) ".");
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
/*
* Kernel methods.
*/
#define W83697HF_EFER (wdt_io+0) /* Extended Function Enable Register */
#define W83697HF_EFIR (wdt_io+0) /* Extended Function Index Register (same as EFER) */
#define W83697HF_EFDR (wdt_io+1) /* Extended Function Data Register */
static inline void
w83697hf_unlock(void)
{
outb_p(0x87, W83697HF_EFER); /* Enter extended function mode */
outb_p(0x87, W83697HF_EFER); /* Again according to manual */
}
static inline void
w83697hf_lock(void)
{
outb_p(0xAA, W83697HF_EFER); /* Leave extended function mode */
}
/*
* The three functions w83697hf_get_reg(), w83697hf_set_reg() and
* w83697hf_write_timeout() must be called with the device unlocked.
*/
static unsigned char
w83697hf_get_reg(unsigned char reg)
{
outb_p(reg, W83697HF_EFIR);
return inb_p(W83697HF_EFDR);
}
static void
w83697hf_set_reg(unsigned char reg, unsigned char data)
{
outb_p(reg, W83697HF_EFIR);
outb_p(data, W83697HF_EFDR);
}
static void
w83697hf_write_timeout(int timeout)
{
w83697hf_set_reg(0xF4, timeout); /* Write Timeout counter to CRF4 */
}
static void
w83697hf_select_wdt(void)
{
w83697hf_unlock();
w83697hf_set_reg(0x07, 0x08); /* Switch to logic device 8 (GPIO2) */
}
static inline void
w83697hf_deselect_wdt(void)
{
w83697hf_lock();
}
static void
w83697hf_init(void)
{
unsigned char bbuf;
w83697hf_select_wdt();
bbuf = w83697hf_get_reg(0x29);
bbuf &= ~0x60;
bbuf |= 0x20;
w83697hf_set_reg(0x29, bbuf); /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
bbuf = w83697hf_get_reg(0xF3);
bbuf &= ~0x04;
w83697hf_set_reg(0xF3, bbuf); /* Count mode is seconds */
w83697hf_deselect_wdt();
}
static int
wdt_ping(void)
{
spin_lock(&io_lock);
w83697hf_select_wdt();
w83697hf_write_timeout(timeout);
w83697hf_deselect_wdt();
spin_unlock(&io_lock);
return 0;
}
static int
wdt_enable(void)
{
spin_lock(&io_lock);
w83697hf_select_wdt();
w83697hf_write_timeout(timeout);
w83697hf_set_reg(0x30, 1); /* Enable timer */
w83697hf_deselect_wdt();
spin_unlock(&io_lock);
return 0;
}
static int
wdt_disable(void)
{
spin_lock(&io_lock);
w83697hf_select_wdt();
w83697hf_set_reg(0x30, 0); /* Disable timer */
w83697hf_write_timeout(0);
w83697hf_deselect_wdt();
spin_unlock(&io_lock);
return 0;
}
static int
wdt_set_heartbeat(int t)
{
if ((t < 1) || (t > 255))
return -EINVAL;
timeout = t;
return 0;
}
static ssize_t
wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
if (count) {
if (!nowayout) {
size_t i;
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
expect_close = 42;
}
}
wdt_ping();
}
return count;
}
static int
wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_timeout;
static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "W83697HF WDT",
};
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &ident, sizeof(ident)))
return -EFAULT;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
wdt_ping();
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, p))
return -EFAULT;
if (wdt_set_heartbeat(new_timeout))
return -EINVAL;
wdt_ping();
/* Fall */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
case WDIOC_SETOPTIONS:
{
int options, retval = -EINVAL;
if (get_user(options, p))
return -EFAULT;
if (options & WDIOS_DISABLECARD) {
wdt_disable();
retval = 0;
}
if (options & WDIOS_ENABLECARD) {
wdt_enable();
retval = 0;
}
return retval;
}
default:
return -ENOTTY;
}
return 0;
}
static int
wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &wdt_is_open))
return -EBUSY;
/*
* Activate
*/
wdt_enable();
return nonseekable_open(inode, file);
}
static int
wdt_close(struct inode *inode, struct file *file)
{
if (expect_close == 42) {
wdt_disable();
} else {
printk (KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
wdt_ping();
}
expect_close = 0;
clear_bit(0, &wdt_is_open);
return 0;
}
/*
* Notifier for system down
*/
static int
wdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT) {
/* Turn the WDT off */
wdt_disable();
}
return NOTIFY_DONE;
}
/*
* Kernel Interfaces
*/
static struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wdt_write,
.ioctl = wdt_ioctl,
.open = wdt_open,
.release = wdt_close,
};
static struct miscdevice wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &wdt_fops,
};
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static struct notifier_block wdt_notifier = {
.notifier_call = wdt_notify_sys,
};
static int
w83697hf_check_wdt(void)
{
if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
printk (KERN_ERR PFX "I/O address 0x%x already in use\n", wdt_io);
return -EIO;
}
printk (KERN_DEBUG PFX "Looking for watchdog at address 0x%x\n", wdt_io);
w83697hf_unlock();
if (w83697hf_get_reg(0x20) == 0x60) {
printk (KERN_INFO PFX "watchdog found at address 0x%x\n", wdt_io);
w83697hf_lock();
return 0;
}
w83697hf_lock(); /* Reprotect in case it was a compatible device */
printk (KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io);
release_region(wdt_io, 2);
return -EIO;
}
static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
static int __init
wdt_init(void)
{
int ret, i, found = 0;
spin_lock_init(&io_lock);
printk (KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n");
if (wdt_io == 0) {
/* we will autodetect the W83697HF/HG watchdog */
for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
wdt_io = w83697hf_ioports[i];
if (!w83697hf_check_wdt())
found++;
}
} else {
if (!w83697hf_check_wdt())
found++;
}
if (!found) {
printk (KERN_ERR PFX "No W83697HF/HG could be found\n");
ret = -EIO;
goto out;
}
w83697hf_init();
wdt_disable(); /* Disable watchdog until first use */
if (wdt_set_heartbeat(timeout)) {
wdt_set_heartbeat(WATCHDOG_TIMEOUT);
printk (KERN_INFO PFX "timeout value must be 1<=timeout<=255, using %d\n",
WATCHDOG_TIMEOUT);
}
ret = register_reboot_notifier(&wdt_notifier);
if (ret != 0) {
printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
ret);
goto unreg_regions;
}
ret = misc_register(&wdt_miscdev);
if (ret != 0) {
printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
goto unreg_reboot;
}
printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
out:
return ret;
unreg_reboot:
unregister_reboot_notifier(&wdt_notifier);
unreg_regions:
release_region(wdt_io, 2);
goto out;
}
static void __exit
wdt_exit(void)
{
misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier);
release_region(wdt_io, 2);
}
module_init(wdt_init);
module_exit(wdt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, Samuel Tardieu <sam@rfc1149.net>");
MODULE_DESCRIPTION("w83697hf/hg WDT driver");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment