From b7e04f8c61a46d742de23af5d7ca2b41b33e40ac Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 17 Aug 2007 08:38:02 +0000 Subject: mv watchdog tree under drivers move watchdog tree from drivers/char/watchdog to drivers/watchdog. Signed-off-by: Wim Van Sebroeck diff --git a/drivers/Kconfig b/drivers/Kconfig index 4fb134d..34f40ea 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -58,6 +58,8 @@ source "drivers/power/Kconfig" source "drivers/hwmon/Kconfig" +source "drivers/watchdog/Kconfig" + source "drivers/ssb/Kconfig" source "drivers/mfd/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 174c27e..d2dc01c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -66,7 +66,7 @@ obj-y += i2c/ obj-$(CONFIG_W1) += w1/ obj-$(CONFIG_POWER_SUPPLY) += power/ obj-$(CONFIG_HWMON) += hwmon/ -obj-$(CONFIG_WATCHDOG) += char/watchdog/ +obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 204d53e..69f8518 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -649,8 +649,6 @@ config HVCS source "drivers/char/ipmi/Kconfig" -source "drivers/char/watchdog/Kconfig" - config DS1620 tristate "NetWinder thermometer support" depends on ARCH_NETWINDER diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig deleted file mode 100644 index 37bddc1..0000000 --- a/drivers/char/watchdog/Kconfig +++ /dev/null @@ -1,853 +0,0 @@ -# -# Watchdog device configuration -# - -menuconfig WATCHDOG - bool "Watchdog Timer Support" - ---help--- - If you say Y here (and to one of the following options) and create a - character special file /dev/watchdog with major number 10 and minor - number 130 using mknod ("man mknod"), you will get a watchdog, i.e.: - subsequently opening the file and then failing to write to it for - longer than 1 minute will result in rebooting the machine. This - could be useful for a networked machine that needs to come back - on-line as fast as possible after a lock-up. There's both a watchdog - implementation entirely in software (which can sometimes fail to - reboot the machine) and a driver for hardware watchdog boards, which - are more robust and can also keep track of the temperature inside - your computer. For details, read - in the kernel source. - - The watchdog is usually used together with the watchdog daemon - which is available from - . This daemon can - also monitor NFS connections and can reboot the machine when the process - table is full. - - If unsure, say N. - -if WATCHDOG - -config WATCHDOG_NOWAYOUT - bool "Disable watchdog shutdown on close" - help - The default watchdog behaviour (which you get if you say N here) is - to stop the timer if the process managing it closes the file - /dev/watchdog. It's always remotely possible that this process might - get killed. If you say Y here, the watchdog cannot be stopped once - it has been started. - -# -# General Watchdog drivers -# - -comment "Watchdog Device Drivers" - -# Architecture Independent - -config SOFT_WATCHDOG - tristate "Software watchdog" - help - A software monitoring watchdog. This will fail to reboot your system - from some situations that the hardware watchdog will recover - from. Equally it's a lot cheaper to install. - - To compile this driver as a module, choose M here: the - module will be called softdog. - -# ALPHA Architecture - -# ARM Architecture - -config AT91RM9200_WATCHDOG - tristate "AT91RM9200 watchdog" - depends on ARCH_AT91RM9200 - help - Watchdog timer embedded into AT91RM9200 chips. This will reboot your - system when the timeout is reached. - -config 21285_WATCHDOG - tristate "DC21285 watchdog" - depends on FOOTBRIDGE - help - 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 - driver as a module, which will be called wdt285. - - This driver does not work on all machines. In particular, early CATS - boards have hardware problems that will cause the machine to simply - lock up if the watchdog fires. - - "If in doubt, leave it out" - say N. - -config 977_WATCHDOG - tristate "NetWinder WB83C977 watchdog" - depends on FOOTBRIDGE && ARCH_NETWINDER - help - Say Y here to include support for the WB977 watchdog included in - NetWinder machines. Alternatively say M to compile the driver as - a module, which will be called wdt977. - - Not sure? It's safe to say N. - -config IXP2000_WATCHDOG - tristate "IXP2000 Watchdog" - depends on ARCH_IXP2000 - help - Say Y here if to include support for the watchdog timer - in the Intel IXP2000(2400, 2800, 2850) network processors. - This driver can be built as a module by choosing M. The module - will be called ixp2000_wdt. - - Say N if you are unsure. - -config IXP4XX_WATCHDOG - tristate "IXP4xx Watchdog" - depends on ARCH_IXP4XX - help - Say Y here if to include support for the watchdog timer - in the Intel IXP4xx network processors. This driver can - be built as a module by choosing M. The module will - be called ixp4xx_wdt. - - Note: The internal IXP4xx watchdog does a soft CPU reset - which doesn't reset any peripherals. There are circumstances - where the watchdog will fail to reset the board correctly - (e.g., if the boot ROM is in an unreadable state). - - Say N if you are unsure. - -config KS8695_WATCHDOG - tristate "KS8695 watchdog" - depends on ARCH_KS8695 - help - Watchdog timer embedded into KS8695 processor. This will reboot your - system when the timeout is reached. - -config S3C2410_WATCHDOG - tristate "S3C2410 Watchdog" - depends on ARCH_S3C2410 - help - Watchdog timer block in the Samsung S3C2410 chips. This will - reboot the system when the timer expires with the watchdog - enabled. - - The driver is limited by the speed of the system's PCLK - signal, so with reasonably fast systems (PCLK around 50-66MHz) - then watchdog intervals of over approximately 20seconds are - unavailable. - - The driver can be built as a module by choosing M, and will - be called s3c2410_wdt - -config SA1100_WATCHDOG - tristate "SA1100/PXA2xx watchdog" - depends on ARCH_SA1100 || ARCH_PXA - help - Watchdog timer embedded into SA11x0 and PXA2xx chips. This will - reboot your system when timeout is reached. - - NOTE: once enabled, this timer cannot be disabled. - - To compile this driver as a module, choose M here: the - module will be called sa1100_wdt. - -config MPCORE_WATCHDOG - tristate "MPcore watchdog" - depends on ARM_MPCORE_PLATFORM && LOCAL_TIMERS - help - Watchdog timer embedded into the MPcore system. - - To compile this driver as a module, choose M here: the - module will be called mpcore_wdt. - -config EP93XX_WATCHDOG - tristate "EP93xx Watchdog" - depends on ARCH_EP93XX - help - Say Y here if to include support for the watchdog timer - embedded in the Cirrus Logic EP93xx family of devices. - - To compile this driver as a module, choose M here: the - module will be called ep93xx_wdt. - -config OMAP_WATCHDOG - tristate "OMAP Watchdog" - depends on ARCH_OMAP16XX || ARCH_OMAP24XX - help - Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to - enable the OMAP1610/OMAP1710 watchdog timer. - -config PNX4008_WATCHDOG - tristate "PNX4008 Watchdog" - depends on ARCH_PNX4008 - help - Say Y here if to include support for the watchdog timer - in the PNX4008 processor. - This driver can be built as a module by choosing M. The module - will be called pnx4008_wdt. - - Say N if you are unsure. - -config IOP_WATCHDOG - tristate "IOP Watchdog" - depends on PLAT_IOP - select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) - help - Say Y here if to include support for the watchdog timer - in the Intel IOP3XX & IOP13XX I/O Processors. This driver can - be built as a module by choosing M. The module will - be called iop_wdt. - - Note: The IOP13XX watchdog does an Internal Bus Reset which will - affect both cores and the peripherals of the IOP. The ATU-X - and/or ATUe configuration registers will remain intact, but if - operating as an Root Complex and/or Central Resource, the PCI-X - and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER. - -config DAVINCI_WATCHDOG - tristate "DaVinci watchdog" - depends on ARCH_DAVINCI - help - Say Y here if to include support for the watchdog timer - in the DaVinci DM644x/DM646x processors. - To compile this driver as a module, choose M here: the - module will be called davinci_wdt. - - NOTE: once enabled, this timer cannot be disabled. - Say N if you are unsure. - -# ARM26 Architecture - -# AVR32 Architecture - -config AT32AP700X_WDT - tristate "AT32AP700x watchdog" - depends on CPU_AT32AP7000 - help - Watchdog timer embedded into AT32AP700x devices. This will reboot - your system when the timeout is reached. - -# BLACKFIN Architecture - -config BFIN_WDT - tristate "Blackfin On-Chip Watchdog Timer" - depends on BLACKFIN - ---help--- - If you say yes here you will get support for the Blackfin On-Chip - Watchdog Timer. If you have one of these processors and wish to - have watchdog support enabled, say Y, otherwise say N. - - To compile this driver as a module, choose M here: the - module will be called bfin_wdt. - -# CRIS Architecture - -# FRV Architecture - -# H8300 Architecture - -# X86 (i386 + ia64 + x86_64) Architecture - -config ACQUIRE_WDT - tristate "Acquire SBC Watchdog Timer" - depends on X86 - ---help--- - This is the driver for the hardware watchdog on Single Board - Computers produced by Acquire Inc (and 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 acquirewdt. - - Most people will say N. - -config ADVANTECH_WDT - tristate "Advantech SBC Watchdog Timer" - depends on X86 - help - If you are configuring a Linux kernel for the Advantech single-board - computer, say `Y' here to support its built-in watchdog timer - feature. More information can be found at - - -config ALIM1535_WDT - tristate "ALi M1535 PMU Watchdog Timer" - depends on X86 && PCI - ---help--- - This is the driver for the hardware watchdog on the ALi M1535 PMU. - - To compile this driver as a module, choose M here: the - module will be called alim1535_wdt. - - Most people will say N. - -config ALIM7101_WDT - tristate "ALi M7101 PMU Computer Watchdog" - depends on X86 && PCI - help - This is the driver for the hardware watchdog on the ALi M7101 PMU - as used in the x86 Cobalt servers. - - To compile this driver as a module, choose M here: the - module will be called alim7101_wdt. - - Most people will say N. - -config SC520_WDT - tristate "AMD Elan SC520 processor Watchdog" - depends on X86 - help - This is the driver for the hardware watchdog built in to the - AMD "Elan" SC520 microcomputer commonly used in embedded systems. - 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. - - You can compile this driver directly into the kernel, or use - it as a module. The module will be called sc520_wdt. - -config EUROTECH_WDT - tristate "Eurotech CPU-1220/1410 Watchdog Timer" - depends on X86 - help - Enable support for the watchdog timer on the Eurotech CPU-1220 and - CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product - information are at . - -config IB700_WDT - tristate "IB700 SBC Watchdog Timer" - depends on X86 - ---help--- - This is the driver for the hardware watchdog on the IB700 Single - Board Computer produced by TMC Technology (www.tmc-uk.com). 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. - - This driver is like the WDT501 driver but for slightly different hardware. - - To compile this driver as a module, choose M here: the - module will be called ib700wdt. - - Most people will say N. - -config IBMASR - tristate "IBM Automatic Server Restart" - depends on X86 - help - This is the driver for the IBM Automatic Server Restart watchdog - timer built-in into some eServer xSeries machines. - - To compile this driver as a module, choose M here: the - module will be called ibmasr. - -config WAFER_WDT - tristate "ICP Wafer 5823 Single Board Computer Watchdog" - depends on X86 - help - This is a driver for the hardware watchdog on the ICP Wafer 5823 - Single Board Computer (and probably other similar models). - - To compile this driver as a module, choose M here: the - module will be called wafer5823wdt. - -config I6300ESB_WDT - tristate "Intel 6300ESB Timer/Watchdog" - depends on X86 && PCI - ---help--- - Hardware driver for the watchdog timer built into the Intel - 6300ESB controller hub. - - To compile this driver as a module, choose M here: the - module will be called i6300esb. - -config ITCO_WDT - tristate "Intel TCO Timer/Watchdog" - depends on (X86 || IA64) && PCI - ---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 - controller hub. - - The TCO (Total Cost of Ownership) timer is a watchdog timer - that will reboot the machine after its second expiration. The - expiration time can be configured with the "heartbeat" parameter. - - On some motherboards the driver may fail to reset the chipset's - NO_REBOOT flag which prevents the watchdog from rebooting the - machine. If this is the case you will get a kernel message like - "failed to reset NO_REBOOT flag, reboot disabled by hardware". - - To compile this driver as a module, choose M here: the - module will be called iTCO_wdt. - -config ITCO_VENDOR_SUPPORT - bool "Intel TCO Timer/Watchdog Specific Vendor Support" - depends on ITCO_WDT - ---help--- - Add vendor specific support to the intel TCO timer based watchdog - devices. At this moment we only have additional support for some - SuperMicro Inc. motherboards. - -config SC1200_WDT - tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" - depends on X86 - help - This is a driver for National Semiconductor PC87307/PC97307 hardware - watchdog cards as found on the SC1200. This watchdog is mainly used - for power management purposes and can be used to power down the device - during inactivity periods (includes interrupt activity monitoring). - - To compile this driver as a module, choose M here: the - module will be called sc1200wdt. - - Most people will say N. - -config SCx200_WDT - tristate "National Semiconductor SCx200 Watchdog" - depends on SCx200 && PCI - help - Enable the built-in watchdog timer support on the National - Semiconductor SCx200 processors. - - If compiled as a module, it will be called scx200_wdt. - -config PC87413_WDT - tristate "NS PC87413 watchdog" - depends on X86 - ---help--- - This is the driver for the hardware watchdog on the PC87413 chipset - 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 pc87413_wdt. - - Most people will say N. - -config 60XX_WDT - tristate "SBC-60XX Watchdog Timer" - depends on X86 - help - This driver can be used with the watchdog timer found on some - single board computers, namely the 6010 PII based computer. - It may well work with other cards. It reads port 0x443 to enable - and re-set the watchdog timer, and reads port 0x45 to disable - the watchdog. If you have a card that behave in similar ways, - you can probably make this driver work with your card as well. - - You can compile this driver directly into the kernel, or use - it as a module. The module will be called sbc60xxwdt. - -config SBC8360_WDT - tristate "SBC8360 Watchdog Timer" - depends on X86 - ---help--- - - This is the driver for the hardware watchdog on the SBC8360 Single - Board Computer produced by Axiomtek Co., Ltd. (www.axiomtek.com). - - To compile this driver as a module, choose M here: the - module will be called sbc8360.ko. - - Most people will say N. - -config CPU5_WDT - tristate "SMA CPU5 Watchdog" - depends on X86 - ---help--- - TBD. - To compile this driver as a module, choose M here: the - module will be called cpu5wdt. - -config SMSC37B787_WDT - tristate "Winbond SMsC37B787 Watchdog Timer" - depends on 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 - tristate "W83627HF Watchdog Timer" - depends on X86 - ---help--- - This is the driver for the hardware watchdog on the W83627HF chipset - as used in Advantech PC-9578 and Tyan S2721-533 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 w83627hf_wdt. - - Most people will say N. - -config W83697HF_WDT - tristate "W83697HF/W83697HG Watchdog Timer" - depends on 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 - tristate "W83877F (EMACS) Watchdog Timer" - depends on X86 - ---help--- - This is the driver for the hardware watchdog on the W83877F chipset - as used in EMACS PC-104 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 w83877f_wdt. - - Most people will say N. - -config W83977F_WDT - tristate "W83977F (PCM-5335) Watchdog Timer" - depends on X86 - ---help--- - This is the driver for the hardware watchdog on the W83977F I/O chip - as used in AAEON's PCM-5335 SBC (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 w83977f_wdt. - -config MACHZ_WDT - tristate "ZF MachZ Watchdog" - depends on X86 - ---help--- - If you are using a ZF Micro MachZ processor, say Y here, otherwise - N. This is the driver for the watchdog timer built-in on that - processor using ZF-Logic interface. 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 machzwd. - -config SBC_EPX_C3_WATCHDOG - tristate "Winsystems SBC EPX-C3 watchdog" - depends on X86 - ---help--- - This is the driver for the built-in watchdog timer on the EPX-C3 - Single-board computer made by Winsystems, Inc. - - *Note*: This hardware watchdog is not probeable and thus there - is no way to know if writing to its IO address will corrupt - your system or have any real effect. The only way to be sure - that this driver does what you want is to make sure you - are running it on an EPX-C3 from Winsystems with the watchdog - timer at IO address 0x1ee and 0x1ef. It will write to both those - IO ports. Basically, the assumption is made that if you compile - this driver into your kernel and/or load it as a module, that you - know what you are doing and that you are in fact running on an - EPX-C3 board! - - To compile this driver as a module, choose M here: the - module will be called sbc_epx_c3. - -# M32R Architecture - -# M68K Architecture - -# M68KNOMMU Architecture - -# MIPS Architecture - -config INDYDOG - tristate "Indy/I2 Hardware Watchdog" - depends on SGI_IP22 - help - Hardware driver for the Indy's/I2's watchdog. This is a - watchdog timer that will reboot the machine after a 60 second - timer expired and no process has written to /dev/watchdog during - that time. - -config WDT_MTX1 - tristate "MTX-1 Hardware Watchdog" - depends on MIPS_MTX1 - help - Hardware driver for the MTX-1 boards. This is a watchdog timer that - will reboot the machine after a 100 seconds timer expired. - -config WDT_RM9K_GPI - tristate "RM9000/GPI hardware watchdog" - depends on CPU_RM9000 - help - Watchdog implementation using the GPI hardware found on - PMC-Sierra RM9xxx CPUs. - - To compile this driver as a module, choose M here: the - module will be called rm9k_wdt. - -# PARISC Architecture - -# POWERPC Architecture - -config MPC5200_WDT - tristate "MPC5200 Watchdog Timer" - depends on PPC_MPC52xx - -config 8xx_WDT - tristate "MPC8xx Watchdog Timer" - depends on 8xx - -config 83xx_WDT - tristate "MPC83xx Watchdog Timer" - depends on PPC_83xx - -config MV64X60_WDT - tristate "MV64X60 (Marvell Discovery) Watchdog Timer" - depends on MV64X60 - -config BOOKE_WDT - bool "PowerPC Book-E Watchdog Timer" - depends on BOOKE || 4xx - ---help--- - Please see Documentation/watchdog/watchdog-api.txt for - more information. - -# PPC64 Architecture - -config WATCHDOG_RTAS - tristate "RTAS watchdog" - depends on PPC_RTAS - help - This driver adds watchdog support for the RTAS watchdog. - - To compile this driver as a module, choose M here. The module - will be called wdrtas. - -# S390 Architecture - -config ZVM_WATCHDOG - tristate "z/VM Watchdog Timer" - depends on S390 - help - IBM s/390 and zSeries machines running under z/VM 5.1 or later - provide a virtual watchdog timer to their guest that cause a - user define Control Program command to be executed after a - timeout. - - To compile this driver as a module, choose M here. The module - will be called vmwatchdog. - -# SUPERH (sh + sh64) Architecture - -config SH_WDT - tristate "SuperH Watchdog" - depends on SUPERH && (CPU_SH3 || CPU_SH4) - help - This driver adds watchdog support for the integrated watchdog in the - SuperH processors. If you have one of these processors and wish - to have watchdog support enabled, say Y, otherwise say N. - - As a side note, saying Y here will automatically boost HZ to 1000 - so that the timer has a chance to clear the overflow counter. On - slower systems (such as the SH-2 and SH-3) this will likely yield - some performance issues. As such, the WDT should be avoided here - unless it is absolutely necessary. - - To compile this driver as a module, choose M here: the - module will be called shwdt. - -config SH_WDT_MMAP - bool "Allow mmap of SH WDT" - default n - depends on SH_WDT - help - If you say Y here, user applications will be able to mmap the - WDT/CPG registers. - -# SPARC Architecture - -# SPARC64 Architecture - -config WATCHDOG_CP1XXX - tristate "CP1XXX Hardware Watchdog support" - depends on SPARC64 && PCI - ---help--- - This is the driver for the hardware watchdog timers present on - Sun Microsystems CompactPCI models CP1400 and CP1500. - - To compile this driver as a module, choose M here: the - module will be called cpwatchdog. - - If you do not have a CompactPCI model CP1400 or CP1500, or - another UltraSPARC-IIi-cEngine boardset with hardware watchdog, - you should say N to this option. - -config WATCHDOG_RIO - tristate "RIO Hardware Watchdog support" - depends on SPARC64 && PCI - help - Say Y here to support the hardware watchdog capability on Sun RIO - machines. The watchdog timeout period is normally one minute but - can be changed with a boot-time parameter. - -# V850 Architecture - -# XTENSA Architecture - -# -# ISA-based Watchdog Cards -# - -comment "ISA-based Watchdog Cards" - depends on ISA - -config PCWATCHDOG - tristate "Berkshire Products ISA-PC Watchdog" - depends on ISA - ---help--- - This is the driver for the Berkshire Products ISA-PC Watchdog card. - This card 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. This driver is like the WDT501 driver but for different - hardware. Please read . The PC - watchdog cards can be ordered from . - - To compile this driver as a module, choose M here: the - module will be called pcwd. - - Most people will say N. - -config MIXCOMWD - tristate "Mixcom Watchdog" - depends on ISA - ---help--- - This is a driver for the Mixcom hardware watchdog cards. 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 mixcomwd. - - Most people will say N. - -config WDT - tristate "WDT Watchdog timer" - depends on ISA - ---help--- - If you have a WDT500P or WDT501P watchdog board, say Y here, - otherwise N. It is not possible to probe for this board, which means - that you have to inform the kernel about the IO port and IRQ that - is needed (you can do this via the io and irq parameters) - - To compile this driver as a module, choose M here: the - module will be called wdt. - -config WDT_501 - bool "WDT501 features" - depends on WDT - help - Saying Y here and creating a character special file /dev/temperature - with major number 10 and minor number 131 ("man mknod") will give - you a thermometer inside your computer: reading from - /dev/temperature yields one byte, the temperature in degrees - Fahrenheit. This works only if you have a WDT501P watchdog board - installed. - - If you want to enable the Fan Tachometer on the WDT501P, then you - can do this via the tachometer parameter. Only do this if you have a - fan tachometer actually set up. - -# -# PCI-based Watchdog Cards -# - -comment "PCI-based Watchdog Cards" - depends on PCI - -config PCIPCWATCHDOG - tristate "Berkshire Products PCI-PC Watchdog" - depends on PCI - ---help--- - This is the driver for the Berkshire Products PCI-PC Watchdog card. - This card 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. The card can also monitor the internal temperature of the PC. - More info is available at . - - To compile this driver as a module, choose M here: the - module will be called pcwd_pci. - - Most people will say N. - -config WDTPCI - tristate "PCI-WDT500/501 Watchdog timer" - depends on PCI - ---help--- - If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. - - To compile this driver as a module, choose M here: the - module will be called wdt_pci. - -config WDT_501_PCI - bool "PCI-WDT501 features" - depends on WDTPCI - help - Saying Y here and creating a character special file /dev/temperature - with major number 10 and minor number 131 ("man mknod") will give - you a thermometer inside your computer: reading from - /dev/temperature yields one byte, the temperature in degrees - Fahrenheit. This works only if you have a PCI-WDT501 watchdog board - installed. - - If you want to enable the Fan Tachometer on the PCI-WDT501, then you - can do this via the tachometer parameter. Only do this if you have a - fan tachometer actually set up. - -# -# USB-based Watchdog Cards -# - -comment "USB-based Watchdog Cards" - depends on USB - -config USBPCWATCHDOG - tristate "Berkshire Products USB-PC Watchdog" - depends on USB - ---help--- - This is the driver for the Berkshire Products USB-PC Watchdog card. - This card 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. The card can also monitor the internal temperature of the PC. - More info is available at . - - To compile this driver as a module, choose M here: the - module will be called pcwd_usb. - - Most people will say N. - -endif # WATCHDOG diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile deleted file mode 100644 index 389f8b1..0000000 --- a/drivers/char/watchdog/Makefile +++ /dev/null @@ -1,120 +0,0 @@ -# -# Makefile for the WatchDog device drivers. -# - -# 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. -# This means that if your ISA/PCI/USB card isn't detected that -# you can fall back to an architecture specific driver and if -# that also fails then you can fall back to the software watchdog -# to give you some cover. - -# ISA-based Watchdog Cards -obj-$(CONFIG_PCWATCHDOG) += pcwd.o -obj-$(CONFIG_MIXCOMWD) += mixcomwd.o -obj-$(CONFIG_WDT) += wdt.o - -# PCI-based Watchdog Cards -obj-$(CONFIG_PCIPCWATCHDOG) += pcwd_pci.o -obj-$(CONFIG_WDTPCI) += wdt_pci.o - -# USB-based Watchdog Cards -obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o - -# ALPHA Architecture - -# ARM Architecture -obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o -obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o -obj-$(CONFIG_21285_WATCHDOG) += wdt285.o -obj-$(CONFIG_977_WATCHDOG) += wdt977.o -obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o -obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o -obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o -obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o -obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o -obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o -obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o -obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o -obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o -obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o - -# ARM26 Architecture - -# AVR32 Architecture -obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o - -# BLACKFIN Architecture -obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o - -# CRIS Architecture - -# FRV Architecture - -# H8300 Architecture - -# X86 (i386 + ia64 + x86_64) Architecture -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_SC520_WDT) += sc520_wdt.o -obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o -obj-$(CONFIG_IB700_WDT) += ib700wdt.o -obj-$(CONFIG_IBMASR) += ibmasr.o -obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o -obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o -obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o iTCO_vendor_support.o -obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o -obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o -obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o -obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o -obj-$(CONFIG_SBC8360_WDT) += sbc8360.o -obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o -obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_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_W83977F_WDT) += w83977f_wdt.o -obj-$(CONFIG_MACHZ_WDT) += machzwd.o -obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o - -# M32R Architecture - -# M68K Architecture - -# M68KNOMMU Architecture - -# MIPS Architecture -obj-$(CONFIG_INDYDOG) += indydog.o -obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o -obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o - -# PARISC Architecture - -# POWERPC Architecture -obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o -obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o -obj-$(CONFIG_83xx_WDT) += mpc83xx_wdt.o -obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o -obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o - -# PPC64 Architecture -obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o - -# S390 Architecture - -# SUPERH (sh + sh64) Architecture -obj-$(CONFIG_SH_WDT) += shwdt.o - -# SPARC Architecture - -# SPARC64 Architecture - -# V850 Architecture - -# XTENSA Architecture - -# Architecture Independant -obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o diff --git a/drivers/char/watchdog/acquirewdt.c b/drivers/char/watchdog/acquirewdt.c deleted file mode 100644 index 85269c3..0000000 --- a/drivers/char/watchdog/acquirewdt.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Acquire Single Board Computer Watchdog Timer driver - * - * Based on wdt.c. Original copyright messages: - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * Can't add timeout - driver doesn't allow changing value - */ - -/* - * Theory of Operation: - * The Watch-Dog Timer is provided to ensure that standalone - * Systems can always recover from catastrophic conditions that - * caused the CPU to crash. This condition may have occured by - * external EMI or a software bug. When the CPU stops working - * correctly, hardware on the board will either perform a hardware - * reset (cold boot) or a non-maskable interrupt (NMI) to bring the - * system back to a known state. - * - * The Watch-Dog Timer is controlled by two I/O Ports. - * 443 hex - Read - Enable or refresh the Watch-Dog Timer - * 043 hex - Read - Disable the Watch-Dog Timer - * - * To enable the Watch-Dog Timer, a read from I/O port 443h must - * be performed. This will enable and activate the countdown timer - * which will eventually time out and either reset the CPU or cause - * an NMI depending on the setting of a jumper. To ensure that this - * reset condition does not occur, the Watch-Dog Timer must be - * periodically refreshed by reading the same I/O port 443h. - * The Watch-Dog Timer is disabled by reading I/O port 043h. - * - * The Watch-Dog Timer Time-Out Period is set via jumpers. - * It can be 1, 2, 10, 20, 110 or 220 seconds. - */ - -/* - * Includes, defines, variables, module parameters, ... - */ - -/* Includes */ -#include /* For module specific items */ -#include /* For new moduleparam's */ -#include /* For standard types (like size_t) */ -#include /* For the -ENODEV/... values */ -#include /* For printk/panic/... */ -#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ -#include /* For the watchdog specific items */ -#include /* For file operations */ -#include /* For io-port access */ -#include /* For platform_driver framework */ -#include /* For __init/__exit/... */ - -#include /* For copy_to_user/put_user/... */ -#include /* For inb/outb/... */ - -/* Module information */ -#define DRV_NAME "acquirewdt" -#define PFX DRV_NAME ": " -#define WATCHDOG_NAME "Acquire WDT" -#define WATCHDOG_HEARTBEAT 0 /* There is no way to see what the correct time-out period is */ - -/* internal variables */ -static struct platform_device *acq_platform_device; /* the watchdog platform device */ -static unsigned long acq_is_open; -static char expect_close; - -/* module parameters */ -static int wdt_stop = 0x43; /* You must set this - there is no sane way to probe for this board. */ -module_param(wdt_stop, int, 0); -MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)"); - -static int wdt_start = 0x443; /* You must set this - there is no sane way to probe for this board. */ -module_param(wdt_start, int, 0); -MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)"); - -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) ")"); - -/* - * Watchdog Operations - */ - -static void acq_keepalive(void) -{ - /* Write a watchdog value */ - inb_p(wdt_start); -} - -static void acq_stop(void) -{ - /* Turn the card off */ - inb_p(wdt_stop); -} - -/* - * /dev/watchdog handling - */ - -static ssize_t acq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if(count) { - if (!nowayout) { - size_t i; - - /* note: just in case someone wrote the magic character - * five months ago... */ - expect_close = 0; - - /* scan to see whether or not we got the magic character */ - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - - /* Well, anyhow someone wrote to us, we should return that favour */ - acq_keepalive(); - } - return count; -} - -static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int options, retval = -EINVAL; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = - { - .options = 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; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_KEEPALIVE: - acq_keepalive(); - return 0; - - case WDIOC_GETTIMEOUT: - return put_user(WATCHDOG_HEARTBEAT, p); - - case WDIOC_SETOPTIONS: - { - if (get_user(options, p)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) - { - acq_stop(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) - { - acq_keepalive(); - retval = 0; - } - - return retval; - } - - default: - return -ENOTTY; - } -} - -static int acq_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &acq_is_open)) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - /* Activate */ - acq_keepalive(); - return nonseekable_open(inode, file); -} - -static int acq_close(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - acq_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - acq_keepalive(); - } - clear_bit(0, &acq_is_open); - expect_close = 0; - return 0; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations acq_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = acq_write, - .ioctl = acq_ioctl, - .open = acq_open, - .release = acq_close, -}; - -static struct miscdevice acq_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &acq_fops, -}; - -/* - * Init & exit routines - */ - -static int __devinit acq_probe(struct platform_device *dev) -{ - int ret; - - if (wdt_stop != wdt_start) { - if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_stop); - ret = -EIO; - goto out; - } - } - - if (!request_region(wdt_start, 1, WATCHDOG_NAME)) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_start); - ret = -EIO; - goto unreg_stop; - } - - ret = misc_register(&acq_miscdev); - if (ret != 0) { - printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto unreg_regions; - } - - printk (KERN_INFO PFX "initialized. (nowayout=%d)\n", - nowayout); - - return 0; - -unreg_regions: - release_region(wdt_start, 1); -unreg_stop: - if (wdt_stop != wdt_start) - release_region(wdt_stop, 1); -out: - return ret; -} - -static int __devexit acq_remove(struct platform_device *dev) -{ - misc_deregister(&acq_miscdev); - release_region(wdt_start,1); - if(wdt_stop != wdt_start) - release_region(wdt_stop,1); - - return 0; -} - -static void acq_shutdown(struct platform_device *dev) -{ - /* Turn the WDT off if we have a soft shutdown */ - acq_stop(); -} - -static struct platform_driver acquirewdt_driver = { - .probe = acq_probe, - .remove = __devexit_p(acq_remove), - .shutdown = acq_shutdown, - .driver = { - .owner = THIS_MODULE, - .name = DRV_NAME, - }, -}; - -static int __init acq_init(void) -{ - int err; - - printk(KERN_INFO "WDT driver for Acquire single board computer initialising.\n"); - - err = platform_driver_register(&acquirewdt_driver); - if (err) - return err; - - acq_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(acq_platform_device)) { - err = PTR_ERR(acq_platform_device); - goto unreg_platform_driver; - } - - return 0; - -unreg_platform_driver: - platform_driver_unregister(&acquirewdt_driver); - return err; -} - -static void __exit acq_exit(void) -{ - platform_device_unregister(acq_platform_device); - platform_driver_unregister(&acquirewdt_driver); - printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); -} - -module_init(acq_init); -module_exit(acq_exit); - -MODULE_AUTHOR("David Woodhouse"); -MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/advantechwdt.c b/drivers/char/watchdog/advantechwdt.c deleted file mode 100644 index 8121cc2..0000000 --- a/drivers/char/watchdog/advantechwdt.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Advantech Single Board Computer WDT driver - * - * (c) Copyright 2000-2001 Marek Michalkiewicz - * - * Based on acquirewdt.c which is based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * - * 16-Oct-2002 Rob Radez - * Clean up ioctls, clean up init + exit, add expect close support, - * add wdt_start and wdt_stop as parameters. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define DRV_NAME "advantechwdt" -#define PFX DRV_NAME ": " -#define WATCHDOG_NAME "Advantech WDT" -#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ - -static struct platform_device *advwdt_platform_device; /* the watchdog platform device */ -static unsigned long advwdt_is_open; -static char adv_expect_close; - -/* - * You must set these - there is no sane way to probe for this board. - * - * To enable or restart, write the timeout value in seconds (1 to 63) - * to I/O port wdt_start. To disable, read I/O port wdt_stop. - * Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but - * check your manual (at least the PCA-6159 seems to be different - - * the manual says wdt_stop is 0x43, not 0x443). - * (0x43 is also a write-only control register for the 8254 timer!) - */ - -static int wdt_stop = 0x443; -module_param(wdt_stop, int, 0); -MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)"); - -static int wdt_start = 0x443; -module_param(wdt_start, int, 0); -MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)"); - -static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=63, 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) ")"); - -/* - * Watchdog Operations - */ - -static void -advwdt_ping(void) -{ - /* Write a watchdog value */ - outb_p(timeout, wdt_start); -} - -static void -advwdt_disable(void) -{ - inb_p(wdt_stop); -} - -static int -advwdt_set_heartbeat(int t) -{ - if ((t < 1) || (t > 63)) - return -EINVAL; - - timeout = t; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t -advwdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - if (count) { - if (!nowayout) { - size_t i; - - adv_expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf+i)) - return -EFAULT; - if (c == 'V') - adv_expect_close = 42; - } - } - advwdt_ping(); - } - return count; -} - -static int -advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int new_timeout; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = WATCHDOG_NAME, - }; - - 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: - advwdt_ping(); - break; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, p)) - return -EFAULT; - if (advwdt_set_heartbeat(new_timeout)) - return -EINVAL; - advwdt_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) { - advwdt_disable(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - advwdt_ping(); - retval = 0; - } - - return retval; - } - - default: - return -ENOTTY; - } - return 0; -} - -static int -advwdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &advwdt_is_open)) - return -EBUSY; - /* - * Activate - */ - - advwdt_ping(); - return nonseekable_open(inode, file); -} - -static int -advwdt_close(struct inode *inode, struct file *file) -{ - if (adv_expect_close == 42) { - advwdt_disable(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - advwdt_ping(); - } - clear_bit(0, &advwdt_is_open); - adv_expect_close = 0; - return 0; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations advwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = advwdt_write, - .ioctl = advwdt_ioctl, - .open = advwdt_open, - .release = advwdt_close, -}; - -static struct miscdevice advwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &advwdt_fops, -}; - -/* - * Init & exit routines - */ - -static int __devinit -advwdt_probe(struct platform_device *dev) -{ - int ret; - - if (wdt_stop != wdt_start) { - if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_stop); - ret = -EIO; - goto out; - } - } - - if (!request_region(wdt_start, 1, WATCHDOG_NAME)) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_start); - ret = -EIO; - goto unreg_stop; - } - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (advwdt_set_heartbeat(timeout)) { - advwdt_set_heartbeat(WATCHDOG_TIMEOUT); - printk (KERN_INFO PFX "timeout value must be 1<=x<=63, using %d\n", - timeout); - } - - ret = misc_register(&advwdt_miscdev); - if (ret != 0) { - printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto unreg_regions; - } - - printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - -out: - return ret; -unreg_regions: - release_region(wdt_start, 1); -unreg_stop: - if (wdt_stop != wdt_start) - release_region(wdt_stop, 1); - goto out; -} - -static int __devexit -advwdt_remove(struct platform_device *dev) -{ - misc_deregister(&advwdt_miscdev); - release_region(wdt_start,1); - if(wdt_stop != wdt_start) - release_region(wdt_stop,1); - - return 0; -} - -static void -advwdt_shutdown(struct platform_device *dev) -{ - /* Turn the WDT off if we have a soft shutdown */ - advwdt_disable(); -} - -static struct platform_driver advwdt_driver = { - .probe = advwdt_probe, - .remove = __devexit_p(advwdt_remove), - .shutdown = advwdt_shutdown, - .driver = { - .owner = THIS_MODULE, - .name = DRV_NAME, - }, -}; - -static int __init -advwdt_init(void) -{ - int err; - - printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n"); - - err = platform_driver_register(&advwdt_driver); - if (err) - return err; - - advwdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(advwdt_platform_device)) { - err = PTR_ERR(advwdt_platform_device); - goto unreg_platform_driver; - } - - return 0; - -unreg_platform_driver: - platform_driver_unregister(&advwdt_driver); - return err; -} - -static void __exit -advwdt_exit(void) -{ - platform_device_unregister(advwdt_platform_device); - platform_driver_unregister(&advwdt_driver); - printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); -} - -module_init(advwdt_init); -module_exit(advwdt_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marek Michalkiewicz "); -MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/alim1535_wdt.c b/drivers/char/watchdog/alim1535_wdt.c deleted file mode 100644 index c404fc6..0000000 --- a/drivers/char/watchdog/alim1535_wdt.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Watchdog for the 7101 PMU version found in the ALi M1535 chipsets - * - * 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 - -#define WATCHDOG_NAME "ALi_M1535" -#define PFX WATCHDOG_NAME ": " -#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ - -/* internal variables */ -static unsigned long ali_is_open; -static char ali_expect_release; -static struct pci_dev *ali_pci; -static u32 ali_timeout_bits; /* stores the computed timeout */ -static spinlock_t ali_lock; /* Guards the hardware */ - -/* module parameters */ -static int timeout = WATCHDOG_TIMEOUT; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (0= 18000) { - timeout = WATCHDOG_TIMEOUT; - printk(KERN_INFO PFX "timeout value must be 0 - * and the Cobalt kernel WDT timer driver by Tim Hockin - * - * - * (c)2002 Steve Hill - * - * This WDT driver is different from most other Linux WDT - * drivers in that the driver will ping the watchdog by itself, - * because this particular WDT has a very short timeout (1.6 - * seconds) and it would be insane to count on any userspace - * daemon always getting scheduled within that time frame. - * - * Additions: - * Aug 23, 2004 - Added use_gpio module parameter for use on revision a1d PMUs - * found on very old cobalt hardware. - * -- Mike Waychison - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define OUR_NAME "alim7101_wdt" -#define PFX OUR_NAME ": " - -#define WDT_ENABLE 0x9C -#define WDT_DISABLE 0x8C - -#define ALI_7101_WDT 0x92 -#define ALI_7101_GPIO 0x7D -#define ALI_7101_GPIO_O 0x7E -#define ALI_WDT_ARM 0x01 - -/* - * We're going to use a 1 second timeout. - * If we reset the watchdog every ~250ms we should be safe. */ - -#define WDT_INTERVAL (HZ/4+1) - -/* - * We must not require too good response from the userspace daemon. - * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 30 seconds. - */ - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); - -static int use_gpio = 0; /* Use the pic (for a1d revision alim7101) */ -module_param(use_gpio, int, 0); -MODULE_PARM_DESC(use_gpio, "Use the gpio watchdog. (required by old cobalt boards)"); - -static void wdt_timer_ping(unsigned long); -static DEFINE_TIMER(timer, wdt_timer_ping, 0, 1); -static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static char wdt_expect_close; -static struct pci_dev *alim7101_pmu; - -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) ")"); - -/* - * Whack the dog - */ - -static void wdt_timer_ping(unsigned long data) -{ - /* If we got a heartbeat pulse within the WDT_US_INTERVAL - * we agree to ping the WDT - */ - char tmp; - - if(time_before(jiffies, next_heartbeat)) - { - /* Ping the WDT (this is actually a disarm/arm sequence) */ - pci_read_config_byte(alim7101_pmu, 0x92, &tmp); - pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); - pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); - if (use_gpio) { - pci_read_config_byte(alim7101_pmu, ALI_7101_GPIO_O, &tmp); - pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp - | 0x20); - pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp - & ~0x20); - } - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); -} - -/* - * Utility routines - */ - -static void wdt_change(int writeval) -{ - char tmp; - - pci_read_config_byte(alim7101_pmu, ALI_7101_WDT, &tmp); - if (writeval == WDT_ENABLE) { - pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); - if (use_gpio) { - pci_read_config_byte(alim7101_pmu, ALI_7101_GPIO_O, &tmp); - pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp & ~0x20); - } - - } else { - pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); - if (use_gpio) { - pci_read_config_byte(alim7101_pmu, ALI_7101_GPIO_O, &tmp); - pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp | 0x20); - } - } -} - -static void wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - /* We must enable before we kick off the timer in case the timer - occurs as we ping it */ - - wdt_change(WDT_ENABLE); - - /* Start the timer */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); -} - -static void wdt_turnoff(void) -{ - /* Stop the timer */ - del_timer_sync(&timer); - wdt_change(WDT_DISABLE); - printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); -} - -static void wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); -} - -/* - * /dev/watchdog handling - */ - -static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if(count) { - if (!nowayout) { - size_t ofs; - - /* note: just in case someone wrote the magic character - * five months ago... */ - wdt_expect_close = 0; - - /* now scan */ - for (ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf+ofs)) - return -EFAULT; - if (c == 'V') - wdt_expect_close = 42; - } - } - /* someone wrote to us, we should restart timer */ - wdt_keepalive(); - } - return count; -} - -static int fop_open(struct inode * inode, struct file * file) -{ - /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - /* Good, fire up the show */ - wdt_startup(); - return nonseekable_open(inode, file); -} - -static int fop_close(struct inode * inode, struct file * file) -{ - if(wdt_expect_close == 42) - wdt_turnoff(); - else { - /* wim: shouldn't there be a: del_timer(&timer); */ - printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n"); - } - clear_bit(0, &wdt_is_open); - wdt_expect_close = 0; - return 0; -} - -static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = - { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "ALiM7101", - }; - - 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: - wdt_keepalive(); - return 0; - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if(get_user(new_options, p)) - return -EFAULT; - - if(new_options & WDIOS_DISABLECARD) { - wdt_turnoff(); - retval = 0; - } - - if(new_options & WDIOS_ENABLECARD) { - wdt_startup(); - retval = 0; - } - - return retval; - } - case WDIOC_SETTIMEOUT: - { - int new_timeout; - - if(get_user(new_timeout, p)) - return -EFAULT; - - if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ - return -EINVAL; - - timeout = new_timeout; - wdt_keepalive(); - /* Fall through */ - } - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - default: - return -ENOTTY; - } -} - -static const struct file_operations wdt_fops = { - .owner= THIS_MODULE, - .llseek= no_llseek, - .write= fop_write, - .open= fop_open, - .release= fop_close, - .ioctl= fop_ioctl, -}; - -static struct miscdevice wdt_miscdev = { - .minor=WATCHDOG_MINOR, - .name="watchdog", - .fops=&wdt_fops, -}; - -/* - * Notifier for system down - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) - wdt_turnoff(); - - if (code==SYS_RESTART) { - /* - * Cobalt devices have no way of rebooting themselves other than - * getting the watchdog to pull reset, so we restart the watchdog on - * reboot with no heartbeat - */ - wdt_change(WDT_ENABLE); - printk(KERN_INFO PFX "Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n"); - } - return NOTIFY_DONE; -} - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdt_notifier= -{ - .notifier_call = wdt_notify_sys, -}; - -static void __exit alim7101_wdt_unload(void) -{ - wdt_turnoff(); - /* Deregister */ - misc_deregister(&wdt_miscdev); - unregister_reboot_notifier(&wdt_notifier); - pci_dev_put(alim7101_pmu); -} - -static int __init alim7101_wdt_init(void) -{ - int rc = -EBUSY; - struct pci_dev *ali1543_south; - char tmp; - - printk(KERN_INFO PFX "Steve Hill .\n"); - alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, - NULL); - if (!alim7101_pmu) { - printk(KERN_INFO PFX "ALi M7101 PMU not present - WDT not set\n"); - return -EBUSY; - } - - /* Set the WDT in the PMU to 1 second */ - pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02); - - ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, - NULL); - if (!ali1543_south) { - printk(KERN_INFO PFX "ALi 1543 South-Bridge not present - WDT not set\n"); - goto err_out; - } - pci_read_config_byte(ali1543_south, 0x5e, &tmp); - pci_dev_put(ali1543_south); - if ((tmp & 0x1e) == 0x00) { - if (!use_gpio) { - printk(KERN_INFO PFX "Detected old alim7101 revision 'a1d'. If this is a cobalt board, set the 'use_gpio' module parameter.\n"); - goto err_out; - } - nowayout = 1; - } else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) { - printk(KERN_INFO PFX "ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n"); - goto err_out; - } - - if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ - { - timeout = WATCHDOG_TIMEOUT; - printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", - timeout); - } - - rc = misc_register(&wdt_miscdev); - if (rc) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - wdt_miscdev.minor, rc); - goto err_out; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - rc); - goto err_out_miscdev; - } - - if (nowayout) { - __module_get(THIS_MODULE); - } - - printk(KERN_INFO PFX "WDT driver for ALi M7101 initialised. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - return 0; - -err_out_miscdev: - misc_deregister(&wdt_miscdev); -err_out: - pci_dev_put(alim7101_pmu); - return rc; -} - -module_init(alim7101_wdt_init); -module_exit(alim7101_wdt_unload); - -static struct pci_device_id alim7101_pci_tbl[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) }, - { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, - { } -}; - -MODULE_DEVICE_TABLE(pci, alim7101_pci_tbl); - -MODULE_AUTHOR("Steve Hill"); -MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/at32ap700x_wdt.c b/drivers/char/watchdog/at32ap700x_wdt.c deleted file mode 100644 index 54a5161..0000000 --- a/drivers/char/watchdog/at32ap700x_wdt.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Watchdog driver for Atmel AT32AP700X devices - * - * Copyright (C) 2005-2006 Atmel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TIMEOUT_MIN 1 -#define TIMEOUT_MAX 2 -#define TIMEOUT_DEFAULT TIMEOUT_MAX - -/* module parameters */ -static int timeout = TIMEOUT_DEFAULT; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, - "Timeout value. Limited to be 1 or 2 seconds. (default=" - __MODULE_STRING(TIMEOUT_DEFAULT) ")"); - -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) ")"); - -/* Watchdog registers and write/read macro */ -#define WDT_CTRL 0x00 -#define WDT_CTRL_EN 0 -#define WDT_CTRL_PSEL 8 -#define WDT_CTRL_KEY 24 - -#define WDT_CLR 0x04 - -#define WDT_BIT(name) (1 << WDT_##name) -#define WDT_BF(name, value) ((value) << WDT_##name) - -#define wdt_readl(dev, reg) \ - __raw_readl((dev)->regs + WDT_##reg) -#define wdt_writel(dev, reg, value) \ - __raw_writel((value), (dev)->regs + WDT_##reg) - -struct wdt_at32ap700x { - void __iomem *regs; - spinlock_t io_lock; - int timeout; - unsigned long users; - struct miscdevice miscdev; -}; - -static struct wdt_at32ap700x *wdt; -static char expect_release; - -/* - * Disable the watchdog. - */ -static inline void at32_wdt_stop(void) -{ - unsigned long psel; - - spin_lock(&wdt->io_lock); - psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f); - wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55)); - wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa)); - spin_unlock(&wdt->io_lock); -} - -/* - * Enable and reset the watchdog. - */ -static inline void at32_wdt_start(void) -{ - /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */ - unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe; - - spin_lock(&wdt->io_lock); - wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) - | WDT_BF(CTRL_PSEL, psel) - | WDT_BF(CTRL_KEY, 0x55)); - wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) - | WDT_BF(CTRL_PSEL, psel) - | WDT_BF(CTRL_KEY, 0xaa)); - spin_unlock(&wdt->io_lock); -} - -/* - * Pat the watchdog timer. - */ -static inline void at32_wdt_pat(void) -{ - spin_lock(&wdt->io_lock); - wdt_writel(wdt, CLR, 0x42); - spin_unlock(&wdt->io_lock); -} - -/* - * Watchdog device is opened, and watchdog starts running. - */ -static int at32_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(1, &wdt->users)) - return -EBUSY; - - at32_wdt_start(); - return nonseekable_open(inode, file); -} - -/* - * Close the watchdog device. - */ -static int at32_wdt_close(struct inode *inode, struct file *file) -{ - if (expect_release == 42) { - at32_wdt_stop(); - } else { - dev_dbg(wdt->miscdev.parent, - "Unexpected close, not stopping watchdog!\n"); - at32_wdt_pat(); - } - clear_bit(1, &wdt->users); - expect_release = 0; - return 0; -} - -/* - * Change the watchdog time interval. - */ -static int at32_wdt_settimeout(int time) -{ - /* - * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is - * 2 ^ 16 allowing up to 2 seconds timeout. - */ - if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX)) - return -EINVAL; - - /* - * Set new watchdog time. It will be used when at32_wdt_start() is - * called. - */ - wdt->timeout = time; - return 0; -} - -static struct watchdog_info at32_wdt_info = { - .identity = "at32ap700x watchdog", - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, -}; - -/* - * Handle commands from user-space. - */ -static int at32_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret = -ENOTTY; - int time; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - switch (cmd) { - case WDIOC_KEEPALIVE: - at32_wdt_pat(); - ret = 0; - break; - case WDIOC_GETSUPPORT: - ret = copy_to_user(argp, &at32_wdt_info, - sizeof(at32_wdt_info)) ? -EFAULT : 0; - break; - case WDIOC_SETTIMEOUT: - ret = get_user(time, p); - if (ret) - break; - ret = at32_wdt_settimeout(time); - if (ret) - break; - /* Enable new time value */ - at32_wdt_start(); - /* fall through */ - case WDIOC_GETTIMEOUT: - ret = put_user(wdt->timeout, p); - break; - case WDIOC_GETSTATUS: /* fall through */ - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, p); - break; - case WDIOC_SETOPTIONS: - ret = get_user(time, p); - if (ret) - break; - if (time & WDIOS_DISABLECARD) - at32_wdt_stop(); - if (time & WDIOS_ENABLECARD) - at32_wdt_start(); - ret = 0; - break; - } - - return ret; -} - -static ssize_t at32_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 pat the watchdog */ - at32_wdt_pat(); - } - return len; -} - -static const struct file_operations at32_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = at32_wdt_ioctl, - .open = at32_wdt_open, - .release = at32_wdt_close, - .write = at32_wdt_write, -}; - -static int __init at32_wdt_probe(struct platform_device *pdev) -{ - struct resource *regs; - int ret; - - if (wdt) { - dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n"); - return -EBUSY; - } - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "missing mmio resource\n"); - return -ENXIO; - } - - wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL); - if (!wdt) { - dev_dbg(&pdev->dev, "no memory for wdt structure\n"); - return -ENOMEM; - } - - wdt->regs = ioremap(regs->start, regs->end - regs->start + 1); - if (!wdt->regs) { - ret = -ENOMEM; - dev_dbg(&pdev->dev, "could not map I/O memory\n"); - goto err_free; - } - spin_lock_init(&wdt->io_lock); - wdt->users = 0; - wdt->miscdev.minor = WATCHDOG_MINOR; - wdt->miscdev.name = "watchdog"; - wdt->miscdev.fops = &at32_wdt_fops; - - if (at32_wdt_settimeout(timeout)) { - at32_wdt_settimeout(TIMEOUT_DEFAULT); - dev_dbg(&pdev->dev, - "default timeout invalid, set to %d sec.\n", - TIMEOUT_DEFAULT); - } - - ret = misc_register(&wdt->miscdev); - if (ret) { - dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); - goto err_iounmap; - } - - platform_set_drvdata(pdev, wdt); - wdt->miscdev.parent = &pdev->dev; - dev_info(&pdev->dev, - "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", - wdt->regs, wdt->timeout, nowayout); - - return 0; - -err_iounmap: - iounmap(wdt->regs); -err_free: - kfree(wdt); - wdt = NULL; - return ret; -} - -static int __exit at32_wdt_remove(struct platform_device *pdev) -{ - if (wdt && platform_get_drvdata(pdev) == wdt) { - /* Stop the timer before we leave */ - if (!nowayout) - at32_wdt_stop(); - - misc_deregister(&wdt->miscdev); - iounmap(wdt->regs); - kfree(wdt); - wdt = NULL; - platform_set_drvdata(pdev, NULL); - } - - return 0; -} - -static void at32_wdt_shutdown(struct platform_device *pdev) -{ - at32_wdt_stop(); -} - -#ifdef CONFIG_PM -static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ - at32_wdt_stop(); - return 0; -} - -static int at32_wdt_resume(struct platform_device *pdev) -{ - if (wdt->users) - at32_wdt_start(); - return 0; -} -#else -#define at32_wdt_suspend NULL -#define at32_wdt_resume NULL -#endif - -static struct platform_driver at32_wdt_driver = { - .remove = __exit_p(at32_wdt_remove), - .suspend = at32_wdt_suspend, - .resume = at32_wdt_resume, - .driver = { - .name = "at32_wdt", - .owner = THIS_MODULE, - }, - .shutdown = at32_wdt_shutdown, -}; - -static int __init at32_wdt_init(void) -{ - return platform_driver_probe(&at32_wdt_driver, at32_wdt_probe); -} -module_init(at32_wdt_init); - -static void __exit at32_wdt_exit(void) -{ - platform_driver_unregister(&at32_wdt_driver); -} -module_exit(at32_wdt_exit); - -MODULE_AUTHOR("Hans-Christian Egtvedt "); -MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/at91rm9200_wdt.c b/drivers/char/watchdog/at91rm9200_wdt.c deleted file mode 100644 index 38bd373..0000000 --- a/drivers/char/watchdog/at91rm9200_wdt.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Watchdog driver for Atmel AT91RM9200 (Thunder) - * - * Copyright (C) 2003 SAN People (Pty) Ltd - * - * 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 - - -#define WDT_DEFAULT_TIME 5 /* seconds */ -#define WDT_MAX_TIME 256 /* seconds */ - -static int wdt_time = WDT_DEFAULT_TIME; -static int nowayout = WATCHDOG_NOWAYOUT; - -module_param(wdt_time, int, 0); -MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); - -#ifdef CONFIG_WATCHDOG_NOWAYOUT -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#endif - - -static unsigned long at91wdt_busy; - -/* ......................................................................... */ - -/* - * Disable the watchdog. - */ -static void inline at91_wdt_stop(void) -{ - at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); -} - -/* - * Enable and reset the watchdog. - */ -static void inline at91_wdt_start(void) -{ - at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); - at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); -} - -/* - * Reload the watchdog timer. (ie, pat the watchdog) - */ -static void inline at91_wdt_reload(void) -{ - at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); -} - -/* ......................................................................... */ - -/* - * Watchdog device is opened, and watchdog starts running. - */ -static int at91_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &at91wdt_busy)) - return -EBUSY; - - at91_wdt_start(); - return nonseekable_open(inode, file); -} - -/* - * Close the watchdog device. - * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also - * disabled. - */ -static int at91_wdt_close(struct inode *inode, struct file *file) -{ - if (!nowayout) - at91_wdt_stop(); /* Disable the watchdog when file is closed */ - - clear_bit(0, &at91wdt_busy); - return 0; -} - -/* - * Change the watchdog time interval. - */ -static int at91_wdt_settimeout(int new_time) -{ - /* - * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz - * - * Since WDV is a 16-bit counter, the maximum period is - * 65536 / 0.256 = 256 seconds. - */ - if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) - return -EINVAL; - - /* Set new watchdog time. It will be used when at91_wdt_start() is called. */ - wdt_time = new_time; - return 0; -} - -static struct watchdog_info at91_wdt_info = { - .identity = "at91 watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, -}; - -/* - * Handle commands from user-space. - */ -static int at91_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_value; - - switch(cmd) { - case WDIOC_KEEPALIVE: - at91_wdt_reload(); /* pat the watchdog */ - return 0; - - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_value, p)) - return -EFAULT; - - if (at91_wdt_settimeout(new_value)) - return -EINVAL; - - /* Enable new time value */ - at91_wdt_start(); - - /* Return current value */ - return put_user(wdt_time, p); - - case WDIOC_GETTIMEOUT: - return put_user(wdt_time, p); - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_SETOPTIONS: - if (get_user(new_value, p)) - return -EFAULT; - - if (new_value & WDIOS_DISABLECARD) - at91_wdt_stop(); - if (new_value & WDIOS_ENABLECARD) - at91_wdt_start(); - return 0; - - default: - return -ENOTTY; - } -} - -/* - * Pat the watchdog whenever device is written to. - */ -static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - at91_wdt_reload(); /* pat the watchdog */ - return len; -} - -/* ......................................................................... */ - -static const struct file_operations at91wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = at91_wdt_ioctl, - .open = at91_wdt_open, - .release = at91_wdt_close, - .write = at91_wdt_write, -}; - -static struct miscdevice at91wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &at91wdt_fops, -}; - -static int __init at91wdt_probe(struct platform_device *pdev) -{ - int res; - - if (at91wdt_miscdev.parent) - return -EBUSY; - at91wdt_miscdev.parent = &pdev->dev; - - res = misc_register(&at91wdt_miscdev); - if (res) - return res; - - printk("AT91 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : ""); - return 0; -} - -static int __exit at91wdt_remove(struct platform_device *pdev) -{ - int res; - - res = misc_deregister(&at91wdt_miscdev); - if (!res) - at91wdt_miscdev.parent = NULL; - - return res; -} - -static void at91wdt_shutdown(struct platform_device *pdev) -{ - at91_wdt_stop(); -} - -#ifdef CONFIG_PM - -static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ - at91_wdt_stop(); - return 0; -} - -static int at91wdt_resume(struct platform_device *pdev) -{ - if (at91wdt_busy) - at91_wdt_start(); - return 0; -} - -#else -#define at91wdt_suspend NULL -#define at91wdt_resume NULL -#endif - -static struct platform_driver at91wdt_driver = { - .probe = at91wdt_probe, - .remove = __exit_p(at91wdt_remove), - .shutdown = at91wdt_shutdown, - .suspend = at91wdt_suspend, - .resume = at91wdt_resume, - .driver = { - .name = "at91_wdt", - .owner = THIS_MODULE, - }, -}; - -static int __init at91_wdt_init(void) -{ - /* Check that the heartbeat value is within range; if not reset to the default */ - if (at91_wdt_settimeout(wdt_time)) { - at91_wdt_settimeout(WDT_DEFAULT_TIME); - pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); - } - - return platform_driver_register(&at91wdt_driver); -} - -static void __exit at91_wdt_exit(void) -{ - platform_driver_unregister(&at91wdt_driver); -} - -module_init(at91_wdt_init); -module_exit(at91_wdt_exit); - -MODULE_AUTHOR("Andrew Victor"); -MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/bfin_wdt.c b/drivers/char/watchdog/bfin_wdt.c deleted file mode 100644 index 309d279..0000000 --- a/drivers/char/watchdog/bfin_wdt.c +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Blackfin On-Chip Watchdog Driver - * Supports BF53[123]/BF53[467]/BF54[2489]/BF561 - * - * Originally based on softdog.c - * Copyright 2006-2007 Analog Devices Inc. - * Copyright 2006-2007 Michele d'Amico - * Copyright 1996 Alan Cox - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) -#define stampit() stamp("here i am") - -#define WATCHDOG_NAME "bfin-wdt" -#define PFX WATCHDOG_NAME ": " - -/* The BF561 has two watchdogs (one per core), but since Linux - * only runs on core A, we'll just work with that one. - */ -#ifdef BF561_FAMILY -# define bfin_read_WDOG_CTL() bfin_read_WDOGA_CTL() -# define bfin_read_WDOG_CNT() bfin_read_WDOGA_CNT() -# define bfin_read_WDOG_STAT() bfin_read_WDOGA_STAT() -# define bfin_write_WDOG_CTL(x) bfin_write_WDOGA_CTL(x) -# define bfin_write_WDOG_CNT(x) bfin_write_WDOGA_CNT(x) -# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x) -#endif - -/* Bit in SWRST that indicates boot caused by watchdog */ -#define SWRST_RESET_WDOG 0x4000 - -/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */ -#define WDOG_EXPIRED 0x8000 - -/* Masks for WDEV field in WDOG_CTL register */ -#define ICTL_RESET 0x0 -#define ICTL_NMI 0x2 -#define ICTL_GPI 0x4 -#define ICTL_NONE 0x6 -#define ICTL_MASK 0x6 - -/* Masks for WDEN field in WDOG_CTL register */ -#define WDEN_MASK 0x0FF0 -#define WDEN_ENABLE 0x0000 -#define WDEN_DISABLE 0x0AD0 - -/* some defaults */ -#define WATCHDOG_TIMEOUT 20 - -static unsigned int timeout = WATCHDOG_TIMEOUT; -static int nowayout = WATCHDOG_NOWAYOUT; -static struct watchdog_info bfin_wdt_info; -static unsigned long open_check; -static char expect_close; -static spinlock_t bfin_wdt_spinlock = SPIN_LOCK_UNLOCKED; - -/** - * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive - * - * The Userspace watchdog got a KeepAlive: schedule the next timeout. - */ -static int bfin_wdt_keepalive(void) -{ - stampit(); - bfin_write_WDOG_STAT(0); - return 0; -} - -/** - * bfin_wdt_stop - Stop the Watchdog - * - * Stops the on-chip watchdog. - */ -static int bfin_wdt_stop(void) -{ - stampit(); - bfin_write_WDOG_CTL(WDEN_DISABLE); - return 0; -} - -/** - * bfin_wdt_start - Start the Watchdog - * - * Starts the on-chip watchdog. Automatically loads WDOG_CNT - * into WDOG_STAT for us. - */ -static int bfin_wdt_start(void) -{ - stampit(); - bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET); - return 0; -} - -/** - * bfin_wdt_running - Check Watchdog status - * - * See if the watchdog is running. - */ -static int bfin_wdt_running(void) -{ - stampit(); - return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE); -} - -/** - * bfin_wdt_set_timeout - Set the Userspace Watchdog timeout - * @t: new timeout value (in seconds) - * - * Translate the specified timeout in seconds into System Clock - * terms which is what the on-chip Watchdog requires. - */ -static int bfin_wdt_set_timeout(unsigned long t) -{ - u32 cnt; - unsigned long flags; - - stampit(); - - cnt = t * get_sclk(); - if (cnt < get_sclk()) { - printk(KERN_WARNING PFX "timeout value is too large\n"); - return -EINVAL; - } - - spin_lock_irqsave(&bfin_wdt_spinlock, flags); - { - int run = bfin_wdt_running(); - bfin_wdt_stop(); - bfin_write_WDOG_CNT(cnt); - if (run) bfin_wdt_start(); - } - spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); - - timeout = t; - - return 0; -} - -/** - * bfin_wdt_open - Open the Device - * @inode: inode of device - * @file: file handle of device - * - * Watchdog device is opened and started. - */ -static int bfin_wdt_open(struct inode *inode, struct file *file) -{ - stampit(); - - if (test_and_set_bit(0, &open_check)) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - bfin_wdt_keepalive(); - bfin_wdt_start(); - - return nonseekable_open(inode, file); -} - -/** - * bfin_wdt_close - Close the Device - * @inode: inode of device - * @file: file handle of device - * - * Watchdog device is closed and stopped. - */ -static int bfin_wdt_release(struct inode *inode, struct file *file) -{ - stampit(); - - if (expect_close == 42) { - bfin_wdt_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - bfin_wdt_keepalive(); - } - - expect_close = 0; - clear_bit(0, &open_check); - - return 0; -} - -/** - * bfin_wdt_write - Write to Device - * @file: file handle of device - * @buf: buffer to write - * @count: length of buffer - * @ppos: offset - * - * Pings the watchdog on write. - */ -static ssize_t bfin_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - stampit(); - - 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 = 42; - } - } - bfin_wdt_keepalive(); - } - - return len; -} - -/** - * bfin_wdt_ioctl - Query Device - * @inode: inode of device - * @file: file handle of device - * @cmd: watchdog command - * @arg: argument - * - * Query basic information from the device or ping it, as outlined by the - * watchdog API. - */ -static int bfin_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - - stampit(); - - switch (cmd) { - default: - return -ENOTTY; - - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info))) - return -EFAULT; - else - return 0; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p); - - case WDIOC_KEEPALIVE: - bfin_wdt_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: { - int new_timeout; - - if (get_user(new_timeout, p)) - return -EFAULT; - - if (bfin_wdt_set_timeout(new_timeout)) - return -EINVAL; - } - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - - case WDIOC_SETOPTIONS: { - unsigned long flags; - int options, ret = -EINVAL; - - if (get_user(options, p)) - return -EFAULT; - - spin_lock_irqsave(&bfin_wdt_spinlock, flags); - - if (options & WDIOS_DISABLECARD) { - bfin_wdt_stop(); - ret = 0; - } - - if (options & WDIOS_ENABLECARD) { - bfin_wdt_start(); - ret = 0; - } - - spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); - - return ret; - } - } -} - -/** - * bfin_wdt_notify_sys - Notifier Handler - * @this: notifier block - * @code: notifier event - * @unused: unused - * - * Handles specific events, such as turning off the watchdog during a - * shutdown event. - */ -static int bfin_wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - stampit(); - - if (code == SYS_DOWN || code == SYS_HALT) - bfin_wdt_stop(); - - return NOTIFY_DONE; -} - -#ifdef CONFIG_PM -static int state_before_suspend; - -/** - * bfin_wdt_suspend - suspend the watchdog - * @pdev: device being suspended - * @state: requested suspend state - * - * Remember if the watchdog was running and stop it. - * TODO: is this even right? Doesn't seem to be any - * standard in the watchdog world ... - */ -static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state) -{ - stampit(); - - state_before_suspend = bfin_wdt_running(); - bfin_wdt_stop(); - - return 0; -} - -/** - * bfin_wdt_resume - resume the watchdog - * @pdev: device being resumed - * - * If the watchdog was running, turn it back on. - */ -static int bfin_wdt_resume(struct platform_device *pdev) -{ - stampit(); - - if (state_before_suspend) { - bfin_wdt_set_timeout(timeout); - bfin_wdt_start(); - } - - return 0; -} -#else -# define bfin_wdt_suspend NULL -# 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 struct file_operations bfin_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = bfin_wdt_write, - .ioctl = bfin_wdt_ioctl, - .open = bfin_wdt_open, - .release = bfin_wdt_release, -}; - -static struct miscdevice bfin_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &bfin_wdt_fops, -}; - -static struct watchdog_info bfin_wdt_info = { - .identity = "Blackfin Watchdog", - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, -}; - -static struct notifier_block bfin_wdt_notifier = { - .notifier_call = bfin_wdt_notify_sys, -}; - -/** - * bfin_wdt_init - Initialize module - * - * Registers the device and notifier handler. Actual device - * initialization is handled by bfin_wdt_open(). - */ -static int __init bfin_wdt_init(void) -{ - int ret; - - stampit(); - - /* Check that the timeout value is within range */ - if (bfin_wdt_set_timeout(timeout)) - return -EINVAL; - - /* 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); - if (ret) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); - return ret; - } - - ret = misc_register(&bfin_wdt_miscdev); - if (ret) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - unregister_reboot_notifier(&bfin_wdt_notifier); - return ret; - } - - printk(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(). - */ -static void __exit bfin_wdt_exit(void) -{ - misc_deregister(&bfin_wdt_miscdev); - unregister_reboot_notifier(&bfin_wdt_notifier); -} - -module_init(bfin_wdt_init); -module_exit(bfin_wdt_exit); - -MODULE_AUTHOR("Michele d'Amico, Mike Frysinger "); -MODULE_DESCRIPTION("Blackfin Watchdog Device Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -module_param(timeout, uint, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/char/watchdog/booke_wdt.c b/drivers/char/watchdog/booke_wdt.c deleted file mode 100644 index d362f5b..0000000 --- a/drivers/char/watchdog/booke_wdt.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * drivers/char/watchdog/booke_wdt.c - * - * Watchdog timer for PowerPC Book-E systems - * - * Author: Matthew McClintock - * Maintainer: Kumar Gala - * - * Copyright 2005 Freescale Semiconductor 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 - -/* If the kernel parameter wdt=1, the watchdog will be enabled at boot. - * Also, the wdt_period sets the watchdog timer period timeout. - * For E500 cpus the wdt_period sets which bit changing from 0->1 will - * trigger a watchog timeout. This watchdog timeout will occur 3 times, the - * first time nothing will happen, the second time a watchdog exception will - * occur, and the final time the board will reset. - */ - -#ifdef CONFIG_FSL_BOOKE -#define WDT_PERIOD_DEFAULT 63 /* Ex. wdt_period=28 bus=333Mhz , reset=~40sec */ -#else -#define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */ -#endif /* for timing information */ - -u32 booke_wdt_enabled = 0; -u32 booke_wdt_period = WDT_PERIOD_DEFAULT; - -#ifdef CONFIG_FSL_BOOKE -#define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15)) -#else -#define WDTP(x) (TCR_WP(x)) -#endif - -/* - * booke_wdt_ping: - */ -static __inline__ void booke_wdt_ping(void) -{ - mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); -} - -/* - * booke_wdt_enable: - */ -static __inline__ void booke_wdt_enable(void) -{ - u32 val; - - /* clear status before enabling watchdog */ - booke_wdt_ping(); - val = mfspr(SPRN_TCR); - val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period)); - - mtspr(SPRN_TCR, val); -} - -/* - * booke_wdt_write: - */ -static ssize_t booke_wdt_write (struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - booke_wdt_ping(); - return count; -} - -static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .firmware_version = 0, - .identity = "PowerPC Book-E Watchdog", -}; - -/* - * booke_wdt_ioctl: - */ -static int booke_wdt_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - u32 tmp = 0; - u32 __user *p = (u32 __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user ((struct watchdog_info __user *) arg, &ident, - sizeof(struct watchdog_info))) - return -EFAULT; - case WDIOC_GETSTATUS: - return put_user(ident.options, p); - case WDIOC_GETBOOTSTATUS: - /* XXX: something is clearing TSR */ - tmp = mfspr(SPRN_TSR) & TSR_WRS(3); - /* returns 1 if last reset was caused by the WDT */ - return (tmp ? 1 : 0); - case WDIOC_KEEPALIVE: - booke_wdt_ping(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(booke_wdt_period, p)) - return -EFAULT; - mtspr(SPRN_TCR, (mfspr(SPRN_TCR)&~WDTP(0))|WDTP(booke_wdt_period)); - return 0; - case WDIOC_GETTIMEOUT: - return put_user(booke_wdt_period, p); - case WDIOC_SETOPTIONS: - if (get_user(tmp, p)) - return -EINVAL; - if (tmp == WDIOS_ENABLECARD) { - booke_wdt_ping(); - break; - } else - return -EINVAL; - return 0; - default: - return -ENOTTY; - } - - return 0; -} -/* - * booke_wdt_open: - */ -static int booke_wdt_open (struct inode *inode, struct file *file) -{ - if (booke_wdt_enabled == 0) { - booke_wdt_enabled = 1; - booke_wdt_enable(); - printk (KERN_INFO "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", - booke_wdt_period); - } - - return nonseekable_open(inode, file); -} - -static const struct file_operations booke_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = booke_wdt_write, - .ioctl = booke_wdt_ioctl, - .open = booke_wdt_open, -}; - -static struct miscdevice booke_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &booke_wdt_fops, -}; - -static void __exit booke_wdt_exit(void) -{ - misc_deregister(&booke_wdt_miscdev); -} - -/* - * booke_wdt_init: - */ -static int __init booke_wdt_init(void) -{ - int ret = 0; - - printk (KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n"); - ident.firmware_version = cur_cpu_spec->pvr_value; - - ret = misc_register(&booke_wdt_miscdev); - if (ret) { - printk (KERN_CRIT "Cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - return ret; - } - - if (booke_wdt_enabled == 1) { - printk (KERN_INFO "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", - booke_wdt_period); - booke_wdt_enable(); - } - - return ret; -} -device_initcall(booke_wdt_init); diff --git a/drivers/char/watchdog/cpu5wdt.c b/drivers/char/watchdog/cpu5wdt.c deleted file mode 100644 index 5941ca6..0000000 --- a/drivers/char/watchdog/cpu5wdt.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * sma cpu5 watchdog driver - * - * Copyright (C) 2003 Heiko Ronsdorf - * - * 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 - -/* adjustable parameters */ - -static int verbose = 0; -static int port = 0x91; -static int ticks = 10000; - -#define PFX "cpu5wdt: " - -#define CPU5WDT_EXTENT 0x0A - -#define CPU5WDT_STATUS_REG 0x00 -#define CPU5WDT_TIME_A_REG 0x02 -#define CPU5WDT_TIME_B_REG 0x03 -#define CPU5WDT_MODE_REG 0x04 -#define CPU5WDT_TRIGGER_REG 0x07 -#define CPU5WDT_ENABLE_REG 0x08 -#define CPU5WDT_RESET_REG 0x09 - -#define CPU5WDT_INTERVAL (HZ/10+1) - -/* some device data */ - -static struct { - struct completion stop; - volatile int running; - struct timer_list timer; - volatile int queue; - int default_ticks; - unsigned long inuse; -} cpu5wdt_device; - -/* generic helper functions */ - -static void cpu5wdt_trigger(unsigned long unused) -{ - if ( verbose > 2 ) - printk(KERN_DEBUG PFX "trigger at %i ticks\n", ticks); - - if( cpu5wdt_device.running ) - ticks--; - - /* keep watchdog alive */ - outb(1, port + CPU5WDT_TRIGGER_REG); - - /* requeue?? */ - if (cpu5wdt_device.queue && ticks) - mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL); - else { - /* ticks doesn't matter anyway */ - complete(&cpu5wdt_device.stop); - } - -} - -static void cpu5wdt_reset(void) -{ - ticks = cpu5wdt_device.default_ticks; - - if ( verbose ) - printk(KERN_DEBUG PFX "reset (%i ticks)\n", (int) ticks); - -} - -static void cpu5wdt_start(void) -{ - if ( !cpu5wdt_device.queue ) { - cpu5wdt_device.queue = 1; - outb(0, port + CPU5WDT_TIME_A_REG); - outb(0, port + CPU5WDT_TIME_B_REG); - outb(1, port + CPU5WDT_MODE_REG); - outb(0, port + CPU5WDT_RESET_REG); - outb(0, port + CPU5WDT_ENABLE_REG); - mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL); - } - /* if process dies, counter is not decremented */ - cpu5wdt_device.running++; -} - -static int cpu5wdt_stop(void) -{ - if ( cpu5wdt_device.running ) - cpu5wdt_device.running = 0; - - ticks = cpu5wdt_device.default_ticks; - - if ( verbose ) - printk(KERN_CRIT PFX "stop not possible\n"); - - return -EIO; -} - -/* filesystem operations */ - -static int cpu5wdt_open(struct inode *inode, struct file *file) -{ - if ( test_and_set_bit(0, &cpu5wdt_device.inuse) ) - return -EBUSY; - - return nonseekable_open(inode, file); -} - -static int cpu5wdt_release(struct inode *inode, struct file *file) -{ - clear_bit(0, &cpu5wdt_device.inuse); - return 0; -} - -static int cpu5wdt_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 = "CPU5 WDT", - }; - - switch(cmd) { - case WDIOC_KEEPALIVE: - cpu5wdt_reset(); - break; - case WDIOC_GETSTATUS: - value = inb(port + CPU5WDT_STATUS_REG); - value = (value >> 2) & 1; - if ( copy_to_user(argp, &value, sizeof(int)) ) - return -EFAULT; - break; - 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: - cpu5wdt_start(); - break; - case WDIOS_DISABLECARD: - return cpu5wdt_stop(); - default: - return -EINVAL; - } - break; - default: - return -ENOTTY; - } - return 0; -} - -static ssize_t cpu5wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - if ( !count ) - return -EIO; - - cpu5wdt_reset(); - - return count; -} - -static const struct file_operations cpu5wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = cpu5wdt_ioctl, - .open = cpu5wdt_open, - .write = cpu5wdt_write, - .release = cpu5wdt_release, -}; - -static struct miscdevice cpu5wdt_misc = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &cpu5wdt_fops, -}; - -/* init/exit function */ - -static int __devinit cpu5wdt_init(void) -{ - unsigned int val; - int err; - - if ( verbose ) - printk(KERN_DEBUG PFX "port=0x%x, verbose=%i\n", port, verbose); - - if ( !request_region(port, CPU5WDT_EXTENT, PFX) ) { - printk(KERN_ERR PFX "request_region failed\n"); - err = -EBUSY; - goto no_port; - } - - if ( (err = misc_register(&cpu5wdt_misc)) < 0 ) { - printk(KERN_ERR PFX "misc_register failed\n"); - goto no_misc; - } - - /* watchdog reboot? */ - val = inb(port + CPU5WDT_STATUS_REG); - val = (val >> 2) & 1; - if ( !val ) - printk(KERN_INFO PFX "sorry, was my fault\n"); - - init_completion(&cpu5wdt_device.stop); - cpu5wdt_device.queue = 0; - - clear_bit(0, &cpu5wdt_device.inuse); - - setup_timer(&cpu5wdt_device.timer, cpu5wdt_trigger, 0); - - cpu5wdt_device.default_ticks = ticks; - - printk(KERN_INFO PFX "init success\n"); - - return 0; - -no_misc: - release_region(port, CPU5WDT_EXTENT); -no_port: - return err; -} - -static int __devinit cpu5wdt_init_module(void) -{ - return cpu5wdt_init(); -} - -static void __devexit cpu5wdt_exit(void) -{ - if ( cpu5wdt_device.queue ) { - cpu5wdt_device.queue = 0; - wait_for_completion(&cpu5wdt_device.stop); - } - - misc_deregister(&cpu5wdt_misc); - - release_region(port, CPU5WDT_EXTENT); - -} - -static void __devexit cpu5wdt_exit_module(void) -{ - cpu5wdt_exit(); -} - -/* module entry points */ - -module_init(cpu5wdt_init_module); -module_exit(cpu5wdt_exit_module); - -MODULE_AUTHOR("Heiko Ronsdorf "); -MODULE_DESCRIPTION("sma cpu5 watchdog driver"); -MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -module_param(port, int, 0); -MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91"); - -module_param(verbose, int, 0); -MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)"); - -module_param(ticks, int, 0); -MODULE_PARM_DESC(ticks, "count down ticks, default is 10000"); diff --git a/drivers/char/watchdog/davinci_wdt.c b/drivers/char/watchdog/davinci_wdt.c deleted file mode 100644 index 19db530..0000000 --- a/drivers/char/watchdog/davinci_wdt.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * drivers/char/watchdog/davinci_wdt.c - * - * Watchdog driver for DaVinci DM644x/DM646x processors - * - * Copyright (C) 2006 Texas Instruments. - * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MODULE_NAME "DAVINCI-WDT: " - -#define DEFAULT_HEARTBEAT 60 -#define MAX_HEARTBEAT 600 /* really the max margin is 264/27MHz*/ - -/* Timer register set definition */ -#define PID12 (0x0) -#define EMUMGT (0x4) -#define TIM12 (0x10) -#define TIM34 (0x14) -#define PRD12 (0x18) -#define PRD34 (0x1C) -#define TCR (0x20) -#define TGCR (0x24) -#define WDTCR (0x28) - -/* TCR bit definitions */ -#define ENAMODE12_DISABLED (0 << 6) -#define ENAMODE12_ONESHOT (1 << 6) -#define ENAMODE12_PERIODIC (2 << 6) - -/* TGCR bit definitions */ -#define TIM12RS_UNRESET (1 << 0) -#define TIM34RS_UNRESET (1 << 1) -#define TIMMODE_64BIT_WDOG (2 << 2) - -/* WDTCR bit definitions */ -#define WDEN (1 << 14) -#define WDFLAG (1 << 15) -#define WDKEY_SEQ0 (0xa5c6 << 16) -#define WDKEY_SEQ1 (0xda7e << 16) - -static int heartbeat = DEFAULT_HEARTBEAT; - -static spinlock_t io_lock; -static unsigned long wdt_status; -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 -#define WDT_REGION_INITED 2 -#define WDT_DEVICE_INITED 3 - -static struct resource *wdt_mem; -static void __iomem *wdt_base; - -static void wdt_service(void) -{ - spin_lock(&io_lock); - - /* put watchdog in service state */ - davinci_writel(WDKEY_SEQ0, wdt_base + WDTCR); - /* put watchdog in active state */ - davinci_writel(WDKEY_SEQ1, wdt_base + WDTCR); - - spin_unlock(&io_lock); -} - -static void wdt_enable(void) -{ - u32 tgcr; - u32 timer_margin; - - spin_lock(&io_lock); - - /* disable, internal clock source */ - davinci_writel(0, wdt_base + TCR); - /* reset timer, set mode to 64-bit watchdog, and unreset */ - davinci_writel(0, wdt_base + TGCR); - tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET; - davinci_writel(tgcr, wdt_base + TGCR); - /* clear counter regs */ - davinci_writel(0, wdt_base + TIM12); - davinci_writel(0, wdt_base + TIM34); - /* set timeout period */ - timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff); - davinci_writel(timer_margin, wdt_base + PRD12); - timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32); - davinci_writel(timer_margin, wdt_base + PRD34); - /* enable run continuously */ - davinci_writel(ENAMODE12_PERIODIC, wdt_base + TCR); - /* Once the WDT is in pre-active state write to - * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are - * write protected (except for the WDKEY field) - */ - /* put watchdog in pre-active state */ - davinci_writel(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR); - /* put watchdog in active state */ - davinci_writel(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR); - - spin_unlock(&io_lock); -} - -static int davinci_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - wdt_enable(); - - return nonseekable_open(inode, file); -} - -static ssize_t -davinci_wdt_write(struct file *file, const char *data, size_t len, - loff_t *ppos) -{ - if (len) - wdt_service(); - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING, - .identity = "DaVinci Watchdog", -}; - -static int -davinci_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_service(); - ret = 0; - break; - } - return ret; -} - -static int davinci_wdt_release(struct inode *inode, struct file *file) -{ - wdt_service(); - clear_bit(WDT_IN_USE, &wdt_status); - - return 0; -} - -static const struct file_operations davinci_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = davinci_wdt_write, - .ioctl = davinci_wdt_ioctl, - .open = davinci_wdt_open, - .release = davinci_wdt_release, -}; - -static struct miscdevice davinci_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &davinci_wdt_fops, -}; - -static int davinci_wdt_probe(struct platform_device *pdev) -{ - int ret = 0, size; - struct resource *res; - - spin_lock_init(&io_lock); - - if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) - heartbeat = DEFAULT_HEARTBEAT; - - printk(KERN_INFO MODULE_NAME - "DaVinci Watchdog Timer: heartbeat %d sec\n", heartbeat); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - printk(KERN_INFO MODULE_NAME - "failed to get memory region resource\n"); - return -ENOENT; - } - - size = res->end - res->start + 1; - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { - printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); - return -ENOENT; - } - wdt_base = (void __iomem *)(res->start); - - ret = misc_register(&davinci_wdt_miscdev); - if (ret < 0) { - printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); - } else { - set_bit(WDT_DEVICE_INITED, &wdt_status); - } - - return ret; -} - -static int davinci_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&davinci_wdt_miscdev); - if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); - wdt_mem = NULL; - } - return 0; -} - -static struct platform_driver platform_wdt_driver = { - .driver = { - .name = "watchdog", - }, - .probe = davinci_wdt_probe, - .remove = davinci_wdt_remove, -}; - -static int __init davinci_wdt_init(void) -{ - return platform_driver_register(&platform_wdt_driver); -} - -static void __exit davinci_wdt_exit(void) -{ - return platform_driver_unregister(&platform_wdt_driver); -} - -module_init(davinci_wdt_init); -module_exit(davinci_wdt_exit); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("DaVinci Watchdog Driver"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, - "Watchdog heartbeat period in seconds from 1 to " - __MODULE_STRING(MAX_HEARTBEAT) ", default " - __MODULE_STRING(DEFAULT_HEARTBEAT)); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/ep93xx_wdt.c b/drivers/char/watchdog/ep93xx_wdt.c deleted file mode 100644 index 0e4787a..0000000 --- a/drivers/char/watchdog/ep93xx_wdt.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Watchdog driver for Cirrus Logic EP93xx family of devices. - * - * Copyright (c) 2004 Ray Lehtiniemi - * Copyright (c) 2006 Tower Technologies - * Based on ep93xx driver, bits from alim7101_wdt.c - * - * Authors: Ray Lehtiniemi , - * Alessandro Zummo - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * - * This watchdog fires after 250msec, which is a too short interval - * for us to rely on the user space daemon alone. So we ping the - * wdt each ~200msec and eventually stop doing it if the user space - * daemon dies. - * - * TODO: - * - * - Test last reset from watchdog status - * - Add a few missing ioctls - */ - -#include -#include -#include -#include -#include - -#include -#include - -#define WDT_VERSION "0.3" -#define PFX "ep93xx_wdt: " - -/* default timeout (secs) */ -#define WDT_TIMEOUT 30 - -static int nowayout = WATCHDOG_NOWAYOUT; -static int timeout = WDT_TIMEOUT; - -static struct timer_list timer; -static unsigned long next_heartbeat; -static unsigned long wdt_status; -static unsigned long boot_status; - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 - -#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x)) -#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00) -#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04) - -/* reset the wdt every ~200ms */ -#define WDT_INTERVAL (HZ/5) - -static void wdt_enable(void) -{ - __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); -} - -static void wdt_disable(void) -{ - __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); -} - -static inline void wdt_ping(void) -{ - __raw_writew(0x5555, EP93XX_WDT_WATCHDOG); -} - -static void wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - wdt_enable(); - mod_timer(&timer, jiffies + WDT_INTERVAL); -} - -static void wdt_shutdown(void) -{ - del_timer_sync(&timer); - wdt_disable(); -} - -static void wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); -} - -static int ep93xx_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - wdt_startup(); - - return nonseekable_open(inode, file); -} - -static ssize_t -ep93xx_wdt_write(struct file *file, const char __user *data, size_t len, - loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - else - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_keepalive(); - } - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, - .identity = "EP93xx Watchdog", -}; - -static int -ep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info __user *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int __user *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int __user *)arg); - break; - - case WDIOC_GETTIMEOUT: - /* actually, it is 0.250 seconds.... */ - ret = put_user(1, (int __user *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_keepalive(); - ret = 0; - break; - } - return ret; -} - -static int ep93xx_wdt_release(struct inode *inode, struct file *file) -{ - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - wdt_shutdown(); - else - printk(KERN_CRIT PFX "Device closed unexpectedly - " - "timer will not stop\n"); - - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - -static const struct file_operations ep93xx_wdt_fops = { - .owner = THIS_MODULE, - .write = ep93xx_wdt_write, - .ioctl = ep93xx_wdt_ioctl, - .open = ep93xx_wdt_open, - .release = ep93xx_wdt_release, -}; - -static struct miscdevice ep93xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ep93xx_wdt_fops, -}; - -static void ep93xx_timer_ping(unsigned long data) -{ - if (time_before(jiffies, next_heartbeat)) - wdt_ping(); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); -} - -static int __init ep93xx_wdt_init(void) -{ - int err; - - err = misc_register(&ep93xx_wdt_miscdev); - - boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; - - printk(KERN_INFO PFX "EP93XX watchdog, driver version " - WDT_VERSION "%s\n", - (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) - ? " (nCS1 disable detected)" : ""); - - if (timeout < 1 || timeout > 3600) { - timeout = WDT_TIMEOUT; - printk(KERN_INFO PFX - "timeout value must be 1<=x<=3600, using %d\n", - timeout); - } - - setup_timer(&timer, ep93xx_timer_ping, 1); - return err; -} - -static void __exit ep93xx_wdt_exit(void) -{ - wdt_shutdown(); - misc_deregister(&ep93xx_wdt_miscdev); -} - -module_init(ep93xx_wdt_init); -module_exit(ep93xx_wdt_exit); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); - -MODULE_AUTHOR("Ray Lehtiniemi ," - "Alessandro Zummo "); -MODULE_DESCRIPTION("EP93xx Watchdog"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(WDT_VERSION); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c deleted file mode 100644 index b14e9d1..0000000 --- a/drivers/char/watchdog/eurotechwdt.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Eurotech CPU-1220/1410/1420 on board WDT driver - * - * (c) Copyright 2001 Ascensit - * (c) Copyright 2001 Rodolfo Giometti - * (c) Copyright 2002 Rob Radez - * - * Based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox * - */ - -/* Changelog: - * - * 2001 - Rodolfo Giometti - * Initial release - * - * 2002/04/25 - Rob Radez - * clean up #includes - * clean up locking - * make __setup param unique - * proper options in watchdog_info - * add WDIOC_GETSTATUS and WDIOC_SETOPTIONS ioctls - * add expect_close support - * - * 2002.05.30 - Joel Becker - * Added Matt Domsch's nowayout module option. - */ - -/* - * The eurotech CPU-1220/1410/1420's watchdog is a part - * of the on-board SUPER I/O device SMSC FDC 37B782. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static unsigned long eurwdt_is_open; -static int eurwdt_timeout; -static char eur_expect_close; - -/* - * You must set these - there is no sane way to probe for this board. - * You can use eurwdt=x,y to set these now. - */ - -static int io = 0x3f0; -static int irq = 10; -static char *ev = "int"; - -#define WDT_TIMEOUT 60 /* 1 minute */ - -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) ")"); - -/* - * Some symbolic names - */ - -#define WDT_CTRL_REG 0x30 -#define WDT_OUTPIN_CFG 0xe2 -#define WDT_EVENT_INT 0x00 -#define WDT_EVENT_REBOOT 0x08 -#define WDT_UNIT_SEL 0xf1 -#define WDT_UNIT_SECS 0x80 -#define WDT_TIMEOUT_VAL 0xf2 -#define WDT_TIMER_CFG 0xf3 - - -module_param(io, int, 0); -MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)"); -module_param(irq, int, 0); -MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)"); -module_param(ev, charp, 0); -MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')"); - - -/* - * Programming support - */ - -static inline void eurwdt_write_reg(u8 index, u8 data) -{ - outb(index, io); - outb(data, io+1); -} - -static inline void eurwdt_lock_chip(void) -{ - outb(0xaa, io); -} - -static inline void eurwdt_unlock_chip(void) -{ - outb(0x55, io); - eurwdt_write_reg(0x07, 0x08); /* set the logical device */ -} - -static inline void eurwdt_set_timeout(int timeout) -{ - eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout); -} - -static inline void eurwdt_disable_timer(void) -{ - eurwdt_set_timeout(0); -} - -static void eurwdt_activate_timer(void) -{ - eurwdt_disable_timer(); - eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */ - eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT); - - /* Setting interrupt line */ - if (irq == 2 || irq > 15 || irq < 0) { - printk(KERN_ERR ": invalid irq number\n"); - irq = 0; /* if invalid we disable interrupt */ - } - if (irq == 0) - printk(KERN_INFO ": interrupt disabled\n"); - - eurwdt_write_reg(WDT_TIMER_CFG, irq<<4); - - eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */ - eurwdt_set_timeout(0); /* the default timeout */ -} - - -/* - * Kernel methods. - */ - -static irqreturn_t eurwdt_interrupt(int irq, void *dev_id) -{ - printk(KERN_CRIT "timeout WDT timeout\n"); - -#ifdef ONLY_TESTING - printk(KERN_CRIT "Would Reboot.\n"); -#else - printk(KERN_CRIT "Initiating system reboot.\n"); - emergency_restart(); -#endif - return IRQ_HANDLED; -} - - -/** - * eurwdt_ping: - * - * Reload counter one with the watchdog timeout. - */ - -static void eurwdt_ping(void) -{ - /* Write the watchdog default value */ - eurwdt_set_timeout(eurwdt_timeout); -} - -/** - * eurwdt_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t eurwdt_write(struct file *file, const char __user *buf, -size_t count, loff_t *ppos) -{ - if (count) { - if (!nowayout) { - size_t i; - - eur_expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if(get_user(c, buf+i)) - return -EFAULT; - if (c == 'V') - eur_expect_close = 42; - } - } - eurwdt_ping(); /* the default timeout */ - } - - return count; -} - -/** - * eurwdt_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - */ - -static int eurwdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "WDT Eurotech CPU-1220/1410", - }; - - int time; - int options, retval = -EINVAL; - - switch(cmd) { - default: - return -ENOTTY; - - 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: - eurwdt_ping(); - return 0; - - case WDIOC_SETTIMEOUT: - if (copy_from_user(&time, p, sizeof(int))) - return -EFAULT; - - /* Sanity check */ - if (time < 0 || time > 255) - return -EINVAL; - - eurwdt_timeout = time; - eurwdt_set_timeout(time); - /* Fall */ - - case WDIOC_GETTIMEOUT: - return put_user(eurwdt_timeout, p); - - case WDIOC_SETOPTIONS: - if (get_user(options, p)) - return -EFAULT; - if (options & WDIOS_DISABLECARD) { - eurwdt_disable_timer(); - retval = 0; - } - if (options & WDIOS_ENABLECARD) { - eurwdt_activate_timer(); - eurwdt_ping(); - retval = 0; - } - return retval; - } -} - -/** - * eurwdt_open: - * @inode: inode of device - * @file: file handle to device - * - * The misc device has been opened. The watchdog device is single - * open and on opening we load the counter. - */ - -static int eurwdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &eurwdt_is_open)) - return -EBUSY; - eurwdt_timeout = WDT_TIMEOUT; /* initial timeout */ - /* Activate the WDT */ - eurwdt_activate_timer(); - return nonseekable_open(inode, file); -} - -/** - * eurwdt_release: - * @inode: inode to board - * @file: file handle to board - * - * The watchdog has a configurable API. There is a religious dispute - * between people who want their watchdog to be able to shut down and - * those who want to be sure if the watchdog manager dies the machine - * reboots. In the former case we disable the counters, in the latter - * case you have to open it again very soon. - */ - -static int eurwdt_release(struct inode *inode, struct file *file) -{ - if (eur_expect_close == 42) { - eurwdt_disable_timer(); - } else { - printk(KERN_CRIT "eurwdt: Unexpected close, not stopping watchdog!\n"); - eurwdt_ping(); - } - clear_bit(0, &eurwdt_is_open); - eur_expect_close = 0; - return 0; -} - -/** - * eurwdt_notify_sys: - * @this: our notifier block - * @code: the event being reported - * @unused: unused - * - * Our notifier is called on system shutdowns. We want to turn the card - * off at reboot otherwise the machine will reboot again during memory - * test or worse yet during the following fsck. This would suck, in fact - * trust me - if it happens it does suck. - */ - -static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) { - /* Turn the card off */ - eurwdt_disable_timer(); - } - - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - - -static const struct file_operations eurwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = eurwdt_write, - .ioctl = eurwdt_ioctl, - .open = eurwdt_open, - .release = eurwdt_release, -}; - -static struct miscdevice eurwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &eurwdt_fops, -}; - -/* - * The WDT card needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block eurwdt_notifier = { - .notifier_call = eurwdt_notify_sys, -}; - -/** - * cleanup_module: - * - * Unload the watchdog. You cannot do this with any file handles open. - * If your watchdog is set to continue ticking on close and you unload - * it, well it keeps ticking. We won't get the interrupt but the board - * will not touch PC memory so all is fine. You just have to load a new - * module in 60 seconds or reboot. - */ - -static void __exit eurwdt_exit(void) -{ - eurwdt_lock_chip(); - - misc_deregister(&eurwdt_miscdev); - - unregister_reboot_notifier(&eurwdt_notifier); - release_region(io, 2); - free_irq(irq, NULL); -} - -/** - * eurwdt_init: - * - * Set up the WDT watchdog board. After grabbing the resources - * we require we need also to unlock the device. - * The open() function will actually kick the board off. - */ - -static int __init eurwdt_init(void) -{ - int ret; - - ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL); - if(ret) { - printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); - goto out; - } - - if (!request_region(io, 2, "eurwdt")) { - printk(KERN_ERR "eurwdt: IO %X is not free.\n", io); - ret = -EBUSY; - goto outirq; - } - - ret = register_reboot_notifier(&eurwdt_notifier); - if (ret) { - printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret); - goto outreg; - } - - ret = misc_register(&eurwdt_miscdev); - if (ret) { - printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", - WATCHDOG_MINOR); - goto outreboot; - } - - eurwdt_unlock_chip(); - - ret = 0; - printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" - " - timeout event: %s\n", - io, irq, (!strcmp("int", ev) ? "int" : "reboot")); - -out: - return ret; - -outreboot: - unregister_reboot_notifier(&eurwdt_notifier); - -outreg: - release_region(io, 2); - -outirq: - free_irq(irq, NULL); - goto out; -} - -module_init(eurwdt_init); -module_exit(eurwdt_exit); - -MODULE_AUTHOR("Rodolfo Giometti"); -MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/i6300esb.c b/drivers/char/watchdog/i6300esb.c deleted file mode 100644 index c598250..0000000 --- a/drivers/char/watchdog/i6300esb.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * i6300esb: Watchdog timer driver for Intel 6300ESB chipset - * - * (c) Copyright 2004 Google Inc. - * (c) Copyright 2005 David Härdeman - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * based on i810-tco.c which is in turn based on softdog.c - * - * The timer is implemented in the following I/O controller hubs: - * (See the intel documentation on http://developer.intel.com.) - * 6300ESB chip : document number 300641-003 - * - * 2004YYZZ Ross Biro - * Initial version 0.01 - * 2004YYZZ Ross Biro - * Version 0.02 - * 20050210 David Härdeman - * Ported driver to kernel 2.6 - */ - -/* - * Includes, defines, variables, module parameters, ... - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* Module and version information */ -#define ESB_VERSION "0.03" -#define ESB_MODULE_NAME "i6300ESB timer" -#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION -#define PFX ESB_MODULE_NAME ": " - -/* PCI configuration registers */ -#define ESB_CONFIG_REG 0x60 /* Config register */ -#define ESB_LOCK_REG 0x68 /* WDT lock register */ - -/* Memory mapped registers */ -#define ESB_TIMER1_REG BASEADDR + 0x00 /* Timer1 value after each reset */ -#define ESB_TIMER2_REG BASEADDR + 0x04 /* Timer2 value after each reset */ -#define ESB_GINTSR_REG BASEADDR + 0x08 /* General Interrupt Status Register */ -#define ESB_RELOAD_REG BASEADDR + 0x0c /* Reload register */ - -/* Lock register bits */ -#define ESB_WDT_FUNC ( 0x01 << 2 ) /* Watchdog functionality */ -#define ESB_WDT_ENABLE ( 0x01 << 1 ) /* Enable WDT */ -#define ESB_WDT_LOCK ( 0x01 << 0 ) /* Lock (nowayout) */ - -/* Config register bits */ -#define ESB_WDT_REBOOT ( 0x01 << 5 ) /* Enable reboot on timeout */ -#define ESB_WDT_FREQ ( 0x01 << 2 ) /* Decrement frequency */ -#define ESB_WDT_INTTYPE ( 0x11 << 0 ) /* Interrupt type on timer1 timeout */ - -/* Reload register bits */ -#define ESB_WDT_RELOAD ( 0x01 << 8 ) /* prevent timeout */ - -/* Magic constants */ -#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ -#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ - -/* internal variables */ -static void __iomem *BASEADDR; -static spinlock_t esb_lock; /* Guards the hardware */ -static unsigned long timer_alive; -static struct pci_dev *esb_pci; -static unsigned short triggered; /* The status of the watchdog upon boot */ -static char esb_expect_close; - -/* module parameters */ -#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (1 (2 * 0x03ff)) - return -EINVAL; - - spin_lock(&esb_lock); - - /* We shift by 9, so if we are passed a value of 1 sec, - * val will be 1 << 9 = 512, then write that to two - * timers => 2 * 512 = 1024 (which is decremented at 1KHz) - */ - val = time << 9; - - /* Write timer 1 */ - esb_unlock_registers(); - writel(val, ESB_TIMER1_REG); - - /* Write timer 2 */ - esb_unlock_registers(); - writel(val, ESB_TIMER2_REG); - - /* Reload */ - esb_unlock_registers(); - writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); - - /* FIXME: Do we need to flush everything out? */ - - /* Done */ - heartbeat = time; - spin_unlock(&esb_lock); - return 0; -} - -static int esb_timer_read (void) -{ - u32 count; - - /* This isn't documented, and doesn't take into - * acount which stage is running, but it looks - * like a 20 bit count down, so we might as well report it. - */ - pci_read_config_dword(esb_pci, 0x64, &count); - return (int)count; -} - -/* - * /dev/watchdog handling - */ - -static int esb_open (struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &timer_alive)) - return -EBUSY; - - /* Reload and activate timer */ - esb_timer_keepalive (); - esb_timer_start (); - - return nonseekable_open(inode, file); -} - -static int esb_release (struct inode *inode, struct file *file) -{ - /* Shut off the timer. */ - if (esb_expect_close == 42) { - esb_timer_stop (); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - esb_timer_keepalive (); - } - clear_bit(0, &timer_alive); - esb_expect_close = 0; - return 0; -} - -static ssize_t esb_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... */ - esb_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') - esb_expect_close = 42; - } - } - - /* someone wrote to us, we should reload the timer */ - esb_timer_keepalive (); - } - return len; -} - -static int esb_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 = ESB_MODULE_NAME, - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, - sizeof (ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - return put_user (esb_timer_read(), p); - - case WDIOC_GETBOOTSTATUS: - return put_user (triggered, p); - - case WDIOC_KEEPALIVE: - esb_timer_keepalive (); - return 0; - - case WDIOC_SETOPTIONS: - { - if (get_user (new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - esb_timer_stop (); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - esb_timer_keepalive (); - esb_timer_start (); - retval = 0; - } - - return retval; - } - - case WDIOC_SETTIMEOUT: - { - if (get_user(new_heartbeat, p)) - return -EFAULT; - - if (esb_timer_set_heartbeat(new_heartbeat)) - return -EINVAL; - - esb_timer_keepalive (); - /* Fall */ - } - - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - - default: - return -ENOTTY; - } -} - -/* - * Notify system - */ - -static int esb_notify_sys (struct notifier_block *this, unsigned long code, void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) { - /* Turn the WDT off */ - esb_timer_stop (); - } - - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations esb_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = esb_write, - .ioctl = esb_ioctl, - .open = esb_open, - .release = esb_release, -}; - -static struct miscdevice esb_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &esb_fops, -}; - -static struct notifier_block esb_notifier = { - .notifier_call = esb_notify_sys, -}; - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id esb_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), }, - { 0, }, /* End of list */ -}; -MODULE_DEVICE_TABLE (pci, esb_pci_tbl); - -/* - * Init & exit routines - */ - -static unsigned char __init esb_getdevice (void) -{ - u8 val1; - unsigned short val2; - - struct pci_dev *dev = NULL; - /* - * Find the PCI device - */ - - for_each_pci_dev(dev) { - if (pci_match_id(esb_pci_tbl, dev)) { - esb_pci = dev; - break; - } - } - - if (esb_pci) { - if (pci_enable_device(esb_pci)) { - printk (KERN_ERR PFX "failed to enable device\n"); - goto err_devput; - } - - if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) { - printk (KERN_ERR PFX "failed to request region\n"); - goto err_disable; - } - - BASEADDR = ioremap(pci_resource_start(esb_pci, 0), - pci_resource_len(esb_pci, 0)); - if (BASEADDR == NULL) { - /* Something's wrong here, BASEADDR has to be set */ - printk (KERN_ERR PFX "failed to get BASEADDR\n"); - goto err_release; - } - - /* - * The watchdog has two timers, it can be setup so that the - * expiry of timer1 results in an interrupt and the expiry of - * timer2 results in a reboot. We set it to not generate - * any interrupts as there is not much we can do with it - * right now. - * - * We also enable reboots and set the timer frequency to - * the PCI clock divided by 2^15 (approx 1KHz). - */ - pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); - - /* Check that the WDT isn't already locked */ - pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); - if (val1 & ESB_WDT_LOCK) - printk (KERN_WARNING PFX "nowayout already set\n"); - - /* Set the timer to watchdog mode and disable it for now */ - pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); - - /* Check if the watchdog was previously triggered */ - esb_unlock_registers(); - val2 = readw(ESB_RELOAD_REG); - triggered = (val2 & (0x01 << 9) >> 9); - - /* Reset trigger flag and timers */ - esb_unlock_registers(); - writew((0x11 << 8), ESB_RELOAD_REG); - - /* Done */ - return 1; - -err_release: - pci_release_region(esb_pci, 0); -err_disable: - pci_disable_device(esb_pci); -err_devput: - pci_dev_put(esb_pci); - } - return 0; -} - -static int __init watchdog_init (void) -{ - int ret; - - spin_lock_init(&esb_lock); - - /* Check whether or not the hardware watchdog is there */ - if (!esb_getdevice () || esb_pci == NULL) - return -ENODEV; - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (esb_timer_set_heartbeat (heartbeat)) { - esb_timer_set_heartbeat (WATCHDOG_HEARTBEAT); - printk(KERN_INFO PFX "heartbeat value must be 1. - * - * 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. - */ - -/* - * Includes, defines, variables, module parameters, ... - */ - -/* Module and version information */ -#define DRV_NAME "iTCO_vendor_support" -#define DRV_VERSION "1.01" -#define DRV_RELDATE "11-Nov-2006" -#define PFX DRV_NAME ": " - -/* Includes */ -#include /* For module specific items */ -#include /* For new moduleparam's */ -#include /* For standard types (like size_t) */ -#include /* For the -ENODEV/... values */ -#include /* For printk/panic/... */ -#include /* For __init/__exit/... */ -#include /* For io-port access */ - -#include /* For inb/outb/... */ - -/* iTCO defines */ -#define SMI_EN acpibase + 0x30 /* SMI Control and Enable Register */ -#define TCOBASE acpibase + 0x60 /* TCO base address */ -#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ - -/* List of vendor support modes */ -#define SUPERMICRO_OLD_BOARD 1 /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */ -#define SUPERMICRO_NEW_BOARD 2 /* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */ - -static int vendorsupport = 0; -module_param(vendorsupport, int, 0); -MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+"); - -/* - * Vendor Specific Support - */ - -/* - * Vendor Support: 1 - * Board: Super Micro Computer Inc. 370SSE+-OEM1/P3TSSE - * iTCO chipset: ICH2 - * - * Code contributed by: R. Seretny - * Documentation obtained by R. Seretny from SuperMicro Technical Support - * - * To enable Watchdog function: - * BIOS setup -> Power -> TCO Logic SMI Enable -> Within5Minutes - * This setting enables SMI to clear the watchdog expired flag. - * If BIOS or CPU fail which may cause SMI hang, then system will - * reboot. When application starts to use watchdog function, - * application has to take over the control from SMI. - * - * For P3TSSE, J36 jumper needs to be removed to enable the Watchdog - * function. - * - * Note: The system will reboot when Expire Flag is set TWICE. - * So, if the watchdog timer is 20 seconds, then the maximum hang - * time is about 40 seconds, and the minimum hang time is about - * 20.6 seconds. - */ - -static void supermicro_old_pre_start(unsigned long acpibase) -{ - unsigned long val32; - - val32 = inl(SMI_EN); - val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); /* Needed to activate watchdog */ -} - -static void supermicro_old_pre_stop(unsigned long acpibase) -{ - unsigned long val32; - - val32 = inl(SMI_EN); - val32 &= 0x00002000; /* Turn on SMI clearing watchdog */ - outl(val32, SMI_EN); /* Needed to deactivate watchdog */ -} - -static void supermicro_old_pre_keepalive(unsigned long acpibase) -{ - /* Reload TCO Timer (done in iTCO_wdt_keepalive) + */ - /* Clear "Expire Flag" (Bit 3 of TC01_STS register) */ - outb(0x08, TCO1_STS); -} - -/* - * Vendor Support: 2 - * Board: Super Micro Computer Inc. P4SBx, P4DPx - * iTCO chipset: ICH4 - * - * Code contributed by: R. Seretny - * Documentation obtained by R. Seretny from SuperMicro Technical Support - * - * To enable Watchdog function: - * 1. BIOS - * For P4SBx: - * BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature - * For P4DPx: - * BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog - * This setting enables or disables Watchdog function. When enabled, the - * default watchdog timer is set to be 5 minutes (about 4’35â€). It is - * enough to load and run the OS. The application (service or driver) has - * to take over the control once OS is running up and before watchdog - * expires. - * - * 2. JUMPER - * For P4SBx: JP39 - * For P4DPx: JP37 - * This jumper is used for safety. Closed is enabled. This jumper - * prevents user enables watchdog in BIOS by accident. - * - * To enable Watch Dog function, both BIOS and JUMPER must be enabled. - * - * The documentation lists motherboards P4SBx and P4DPx series as of - * 20-March-2002. However, this code works flawlessly with much newer - * motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82). - * - * The original iTCO driver as written does not actually reset the - * watchdog timer on these machines, as a result they reboot after five - * minutes. - * - * NOTE: You may leave the Watchdog function disabled in the SuperMicro - * BIOS to avoid a "boot-race"... This driver will enable watchdog - * functionality even if it's disabled in the BIOS once the /dev/watchdog - * file is opened. - */ - -/* I/O Port's */ -#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */ -#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */ - -/* Control Register's */ -#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */ -#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */ - -#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */ - -#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */ - -#define SM_ENDWATCH 0xAA /* Watchdog lock control page */ - -#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */ - /* (Bit 3: 0 = seconds, 1 = minutes */ - -#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */ - -#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */ - /* Bit 6: timer is reset by kbd interrupt */ - /* Bit 7: timer is reset by mouse interrupt */ - -static void supermicro_new_unlock_watchdog(void) -{ - outb(SM_WATCHPAGE, SM_REGINDEX); /* Write 0x87 to port 0x2e twice */ - outb(SM_WATCHPAGE, SM_REGINDEX); - - outb(SM_CTLPAGESW, SM_REGINDEX); /* Switch to watchdog control page */ - outb(SM_CTLPAGE, SM_DATAIO); -} - -static void supermicro_new_lock_watchdog(void) -{ - outb(SM_ENDWATCH, SM_REGINDEX); -} - -static void supermicro_new_pre_start(unsigned int heartbeat) -{ - unsigned int val; - - supermicro_new_unlock_watchdog(); - - /* Watchdog timer setting needs to be in seconds*/ - outb(SM_COUNTMODE, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0xF7; - outb(val, SM_DATAIO); - - /* Write heartbeat interval to WDOG */ - outb (SM_WATCHTIMER, SM_REGINDEX); - outb((heartbeat & 255), SM_DATAIO); - - /* Make sure keyboard/mouse interrupts don't interfere */ - outb(SM_RESETCONTROL, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0x3f; - outb(val, SM_DATAIO); - - /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */ - outb(SM_WATCHENABLE, SM_REGINDEX); - val = inb(SM_DATAIO); - val |= 0x01; - outb(val, SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -static void supermicro_new_pre_stop(void) -{ - unsigned int val; - - supermicro_new_unlock_watchdog(); - - /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */ - outb(SM_WATCHENABLE, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0xFE; - outb(val, SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) -{ - supermicro_new_unlock_watchdog(); - - /* reset watchdog timeout to heartveat value */ - outb(SM_WATCHTIMER, SM_REGINDEX); - outb((heartbeat & 255), SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -/* - * Generic Support Functions - */ - -void iTCO_vendor_pre_start(unsigned long acpibase, - unsigned int heartbeat) -{ - if (vendorsupport == SUPERMICRO_OLD_BOARD) - supermicro_old_pre_start(acpibase); - else if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_start(heartbeat); -} -EXPORT_SYMBOL(iTCO_vendor_pre_start); - -void iTCO_vendor_pre_stop(unsigned long acpibase) -{ - if (vendorsupport == SUPERMICRO_OLD_BOARD) - supermicro_old_pre_stop(acpibase); - else if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_stop(); -} -EXPORT_SYMBOL(iTCO_vendor_pre_stop); - -void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat) -{ - if (vendorsupport == SUPERMICRO_OLD_BOARD) - supermicro_old_pre_keepalive(acpibase); - else if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_set_heartbeat(heartbeat); -} -EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); - -void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat) -{ - if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_set_heartbeat(heartbeat); -} -EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat); - -int iTCO_vendor_check_noreboot_on(void) -{ - switch(vendorsupport) { - case SUPERMICRO_OLD_BOARD: - return 0; - default: - return 1; - } -} -EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on); - -static int __init iTCO_vendor_init_module(void) -{ - printk (KERN_INFO PFX "vendor-support=%d\n", vendorsupport); - return 0; -} - -static void __exit iTCO_vendor_exit_module(void) -{ - printk (KERN_INFO PFX "Module Unloaded\n"); -} - -module_init(iTCO_vendor_init_module); -module_exit(iTCO_vendor_exit_module); - -MODULE_AUTHOR("Wim Van Sebroeck , R. Seretny "); -MODULE_DESCRIPTION("Intel TCO Vendor Specific WatchDog Timer Driver Support"); -MODULE_VERSION(DRV_VERSION); -MODULE_LICENSE("GPL"); - diff --git a/drivers/char/watchdog/iTCO_wdt.c b/drivers/char/watchdog/iTCO_wdt.c deleted file mode 100644 index cd5a565..0000000 --- a/drivers/char/watchdog/iTCO_wdt.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets) - * - * (c) Copyright 2006-2007 Wim Van Sebroeck . - * - * 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. - * - * The TCO watchdog is implemented in the following I/O controller hubs: - * (See the intel documentation on http://developer.intel.com.) - * 82801AA (ICH) : document number 290655-003, 290677-014, - * 82801AB (ICHO) : document number 290655-003, 290677-014, - * 82801BA (ICH2) : document number 290687-002, 298242-027, - * 82801BAM (ICH2-M) : document number 290687-002, 298242-027, - * 82801CA (ICH3-S) : document number 290733-003, 290739-013, - * 82801CAM (ICH3-M) : document number 290716-001, 290718-007, - * 82801DB (ICH4) : document number 290744-001, 290745-020, - * 82801DBM (ICH4-M) : document number 252337-001, 252663-005, - * 82801E (C-ICH) : document number 273599-001, 273645-002, - * 82801EB (ICH5) : document number 252516-001, 252517-003, - * 82801ER (ICH5R) : document number 252516-001, 252517-003, - * 82801FB (ICH6) : document number 301473-002, 301474-007, - * 82801FR (ICH6R) : document number 301473-002, 301474-007, - * 82801FBM (ICH6-M) : document number 301473-002, 301474-007, - * 82801FW (ICH6W) : document number 301473-001, 301474-007, - * 82801FRW (ICH6RW) : document number 301473-001, 301474-007, - * 82801GB (ICH7) : document number 307013-002, 307014-009, - * 82801GR (ICH7R) : document number 307013-002, 307014-009, - * 82801GDH (ICH7DH) : 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, - * 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, - * 82801IB (ICH9) : document number 316972-001, 316973-001, - * 82801IR (ICH9R) : document number 316972-001, 316973-001, - * 82801IH (ICH9DH) : document number 316972-001, 316973-001, - * 6300ESB (6300ESB) : document number 300641-003, 300884-010, - * 631xESB (631xESB) : document number 313082-001, 313075-005, - * 632xESB (632xESB) : document number 313082-001, 313075-005 - */ - -/* - * Includes, defines, variables, module parameters, ... - */ - -/* Module and version information */ -#define DRV_NAME "iTCO_wdt" -#define DRV_VERSION "1.02" -#define DRV_RELDATE "26-Jul-2007" -#define PFX DRV_NAME ": " - -/* Includes */ -#include /* For module specific items */ -#include /* For new moduleparam's */ -#include /* For standard types (like size_t) */ -#include /* For the -ENODEV/... values */ -#include /* For printk/panic/... */ -#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ -#include /* For the watchdog specific items */ -#include /* For __init/__exit/... */ -#include /* For file operations */ -#include /* For platform_driver framework */ -#include /* For pci functions */ -#include /* For io-port access */ -#include /* For spin_lock/spin_unlock/... */ - -#include /* For copy_to_user/put_user/... */ -#include /* For inb/outb/... */ - -/* TCO related info */ -enum iTCO_chipsets { - TCO_ICH = 0, /* ICH */ - TCO_ICH0, /* ICH0 */ - TCO_ICH2, /* ICH2 */ - TCO_ICH2M, /* ICH2-M */ - TCO_ICH3, /* ICH3-S */ - TCO_ICH3M, /* ICH3-M */ - TCO_ICH4, /* ICH4 */ - TCO_ICH4M, /* ICH4-M */ - TCO_CICH, /* C-ICH */ - TCO_ICH5, /* ICH5 & ICH5R */ - TCO_6300ESB, /* 6300ESB */ - TCO_ICH6, /* ICH6 & ICH6R */ - TCO_ICH6M, /* ICH6-M */ - TCO_ICH6W, /* ICH6W & ICH6RW */ - TCO_ICH7, /* ICH7 & ICH7R */ - TCO_ICH7M, /* ICH7-M */ - TCO_ICH7MDH, /* ICH7-M DH */ - TCO_ICH8, /* ICH8 & ICH8R */ - TCO_ICH8DH, /* ICH8DH */ - TCO_ICH8DO, /* ICH8DO */ - TCO_ICH9, /* ICH9 */ - TCO_ICH9R, /* ICH9R */ - TCO_ICH9DH, /* ICH9DH */ - TCO_631XESB, /* 631xESB/632xESB */ -}; - -static struct { - char *name; - unsigned int iTCO_version; -} iTCO_chipset_info[] __devinitdata = { - {"ICH", 1}, - {"ICH0", 1}, - {"ICH2", 1}, - {"ICH2-M", 1}, - {"ICH3-S", 1}, - {"ICH3-M", 1}, - {"ICH4", 1}, - {"ICH4-M", 1}, - {"C-ICH", 1}, - {"ICH5 or ICH5R", 1}, - {"6300ESB", 1}, - {"ICH6 or ICH6R", 2}, - {"ICH6-M", 2}, - {"ICH6W or ICH6RW", 2}, - {"ICH7 or ICH7R", 2}, - {"ICH7-M", 2}, - {"ICH7-M DH", 2}, - {"ICH8 or ICH8R", 2}, - {"ICH8DH", 2}, - {"ICH8DO", 2}, - {"ICH9", 2}, - {"ICH9R", 2}, - {"ICH9DH", 2}, - {"631xESB/632xESB", 2}, - {NULL,0} -}; - -/* - * This data only exists for exporting the supported PCI ids - * via MODULE_DEVICE_TABLE. We do not actually register a - * pci_driver, because the I/O Controller Hub has also other - * functions that probably will be registered by other drivers. - */ -static struct pci_device_id iTCO_wdt_pci_tbl[] = { - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH0 }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2 }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2M }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3 }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3M }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4 }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4M }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_CICH }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH5 }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_6300ESB }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6 }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6M }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6W }, - { 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_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 }, - { PCI_VENDOR_ID_INTEL, 0x2918, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH9 }, - { PCI_VENDOR_ID_INTEL, 0x2916, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH9R }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH9DH }, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { PCI_VENDOR_ID_INTEL, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, - { 0, }, /* End of list */ -}; -MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl); - -/* Address definitions for the TCO */ -#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 /* TCO base address */ -#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 /* SMI Control and Enable Register */ - -#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 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 */ - 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) */ - spinlock_t io_lock; /* the lock for io operations */ - struct pci_dev *pdev; /* the PCI-device */ -} iTCO_wdt_private; - -static struct platform_device *iTCO_wdt_platform_device; /* the watchdog platform device */ - -/* module parameters */ -#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ -static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2 0 = The TCO timer is enabled to count */ - val = inw(TCO1_CNT); - val &= 0xf7ff; - outw(val, TCO1_CNT); - val = inw(TCO1_CNT); - spin_unlock(&iTCO_wdt_private.io_lock); - - if (val & 0x0800) - return -1; - return 0; -} - -static int iTCO_wdt_stop(void) -{ - unsigned int val; - - spin_lock(&iTCO_wdt_private.io_lock); - - iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE); - - /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ - val = inw(TCO1_CNT); - val |= 0x0800; - outw(val, TCO1_CNT); - val = inw(TCO1_CNT); - - /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - iTCO_wdt_set_NO_REBOOT_bit(); - - spin_unlock(&iTCO_wdt_private.io_lock); - - if ((val & 0x0800) == 0) - return -1; - return 0; -} - -static int iTCO_wdt_keepalive(void) -{ - spin_lock(&iTCO_wdt_private.io_lock); - - iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat); - - /* Reload the timer by writing to the TCO Timer Counter register */ - if (iTCO_wdt_private.iTCO_version == 2) { - outw(0x01, TCO_RLD); - } else if (iTCO_wdt_private.iTCO_version == 1) { - outb(0x01, TCO_RLD); - } - - spin_unlock(&iTCO_wdt_private.io_lock); - return 0; -} - -static int iTCO_wdt_set_heartbeat(int t) -{ - unsigned int val16; - unsigned char val8; - unsigned int tmrval; - - tmrval = seconds_to_ticks(t); - /* from the specs: */ - /* "Values of 0h-3h are ignored and should not be attempted" */ - if (tmrval < 0x04) - return -EINVAL; - if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) || - ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) - return -EINVAL; - - iTCO_vendor_pre_set_heartbeat(tmrval); - - /* Write new heartbeat to watchdog */ - if (iTCO_wdt_private.iTCO_version == 2) { - spin_lock(&iTCO_wdt_private.io_lock); - val16 = inw(TCOv2_TMR); - val16 &= 0xfc00; - val16 |= tmrval; - outw(val16, TCOv2_TMR); - val16 = inw(TCOv2_TMR); - spin_unlock(&iTCO_wdt_private.io_lock); - - if ((val16 & 0x3ff) != tmrval) - return -EINVAL; - } else if (iTCO_wdt_private.iTCO_version == 1) { - spin_lock(&iTCO_wdt_private.io_lock); - val8 = inb(TCOv1_TMR); - val8 &= 0xc0; - val8 |= (tmrval & 0xff); - outb(val8, TCOv1_TMR); - val8 = inb(TCOv1_TMR); - spin_unlock(&iTCO_wdt_private.io_lock); - - if ((val8 & 0x3f) != tmrval) - return -EINVAL; - } - - heartbeat = t; - return 0; -} - -static int iTCO_wdt_get_timeleft (int *time_left) -{ - unsigned int val16; - unsigned char val8; - - /* read the TCO Timer */ - if (iTCO_wdt_private.iTCO_version == 2) { - spin_lock(&iTCO_wdt_private.io_lock); - val16 = inw(TCO_RLD); - val16 &= 0x3ff; - spin_unlock(&iTCO_wdt_private.io_lock); - - *time_left = (val16 * 6) / 10; - } else if (iTCO_wdt_private.iTCO_version == 1) { - spin_lock(&iTCO_wdt_private.io_lock); - val8 = inb(TCO_RLD); - val8 &= 0x3f; - spin_unlock(&iTCO_wdt_private.io_lock); - - *time_left = (val8 * 6) / 10; - } else - return -EINVAL; - return 0; -} - -/* - * /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, -}; - -/* - * Init & exit routines - */ - -static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent, struct platform_device *dev) -{ - int ret; - u32 base_address; - unsigned long RCBA; - unsigned long val32; - - /* - * Find the ACPI/PM base I/O address which is the base - * for the TCO registers (TCOBASE=ACPIBASE + 0x60) - * ACPIBASE is bits [15:7] from 0x40-0x43 - */ - pci_read_config_dword(pdev, 0x40, &base_address); - base_address &= 0x0000ff80; - if (base_address == 0x00000000) { - /* Something's wrong here, ACPIBASE has to be set */ - printk(KERN_ERR PFX "failed to get TCOBASE address\n"); - pci_dev_put(pdev); - return -ENODEV; - } - iTCO_wdt_private.iTCO_version = iTCO_chipset_info[ent->driver_data].iTCO_version; - iTCO_wdt_private.ACPIBASE = base_address; - iTCO_wdt_private.pdev = pdev; - - /* Get the Memory-Mapped GCS register, we need it for the NO_REBOOT flag (TCO v2) */ - /* To get access to it you have to read RCBA from PCI Config space 0xf0 - and use it as base. GCS = RCBA + ICH6_GCS(0x3410). */ - 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); - } - - /* Check chipset's NO_REBOOT bit */ - if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { - printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); - ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ - goto out; - } - - /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ - iTCO_wdt_set_NO_REBOOT_bit(); - - /* Set the TCO_EN bit in SMI_EN register */ - if (!request_region(SMI_EN, 4, "iTCO_wdt")) { - printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n", - SMI_EN ); - ret = -EIO; - goto out; - } - val32 = inl(SMI_EN); - val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ - outl(val32, SMI_EN); - release_region(SMI_EN, 4); - - /* The TCO I/O registers reside in a 32-byte range pointed to by the TCOBASE value */ - if (!request_region (TCOBASE, 0x20, "iTCO_wdt")) { - printk (KERN_ERR PFX "I/O address 0x%04lx already in use\n", - TCOBASE); - ret = -EIO; - goto out; - } - - printk(KERN_INFO PFX "Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n", - iTCO_chipset_info[ent->driver_data].name, - iTCO_chipset_info[ent->driver_data].iTCO_version, - TCOBASE); - - /* Clear out the (probably old) status */ - outb(0, TCO1_STS); - outb(3, TCO2_STS); - - /* Make sure the watchdog is not running */ - iTCO_wdt_stop(); - - /* 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); - printk(KERN_INFO PFX "heartbeat value must be 2"); -MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver"); -MODULE_VERSION(DRV_VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/ib700wdt.c b/drivers/char/watchdog/ib700wdt.c deleted file mode 100644 index c3a60f5..0000000 --- a/drivers/char/watchdog/ib700wdt.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * IB700 Single Board Computer WDT driver - * - * (c) Copyright 2001 Charles Howes - * - * Based on advantechwdt.c which is based on acquirewdt.c which - * is based on wdt.c. - * - * (c) Copyright 2000-2001 Marek Michalkiewicz - * - * Based on acquirewdt.c which is based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * Added timeout module option to override default - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static struct platform_device *ibwdt_platform_device; -static unsigned long ibwdt_is_open; -static spinlock_t ibwdt_lock; -static char expect_close; - -/* Module information */ -#define DRV_NAME "ib700wdt" -#define PFX DRV_NAME ": " - -/* - * - * Watchdog Timer Configuration - * - * The function of the watchdog timer is to reset the system - * automatically and is defined at I/O port 0443H. To enable the - * watchdog timer and allow the system to reset, write I/O port 0443H. - * To disable the timer, write I/O port 0441H for the system to stop the - * watchdog function. The timer has a tolerance of 20% for its - * intervals. - * - * The following describes how the timer should be programmed. - * - * Enabling Watchdog: - * MOV AX,000FH (Choose the values from 0 to F) - * MOV DX,0443H - * OUT DX,AX - * - * Disabling Watchdog: - * MOV AX,000FH (Any value is fine.) - * MOV DX,0441H - * OUT DX,AX - * - * Watchdog timer control table: - * Level Value Time/sec | Level Value Time/sec - * 1 F 0 | 9 7 16 - * 2 E 2 | 10 6 18 - * 3 D 4 | 11 5 20 - * 4 C 6 | 12 4 22 - * 5 B 8 | 13 3 24 - * 6 A 10 | 14 2 26 - * 7 9 12 | 15 1 28 - * 8 8 14 | 16 0 30 - * - */ - -static int wd_times[] = { - 30, /* 0x0 */ - 28, /* 0x1 */ - 26, /* 0x2 */ - 24, /* 0x3 */ - 22, /* 0x4 */ - 20, /* 0x5 */ - 18, /* 0x6 */ - 16, /* 0x7 */ - 14, /* 0x8 */ - 12, /* 0x9 */ - 10, /* 0xA */ - 8, /* 0xB */ - 6, /* 0xC */ - 4, /* 0xD */ - 2, /* 0xE */ - 0, /* 0xF */ -}; - -#define WDT_STOP 0x441 -#define WDT_START 0x443 - -/* Default timeout */ -#define WD_TIMO 0 /* 30 seconds +/- 20%, from table */ - -static int wd_margin = WD_TIMO; - -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) ")"); - - -/* - * Watchdog Operations - */ - -static void -ibwdt_ping(void) -{ - spin_lock(&ibwdt_lock); - - /* Write a watchdog value */ - outb_p(wd_margin, WDT_START); - - spin_unlock(&ibwdt_lock); -} - -static void -ibwdt_disable(void) -{ - spin_lock(&ibwdt_lock); - outb_p(0, WDT_STOP); - spin_unlock(&ibwdt_lock); -} - -static int -ibwdt_set_heartbeat(int t) -{ - int i; - - if ((t < 0) || (t > 30)) - return -EINVAL; - - for (i = 0x0F; i > -1; i--) - if (wd_times[i] > t) - break; - wd_margin = i; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t -ibwdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - if (count) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - ibwdt_ping(); - } - return count; -} - -static int -ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int new_margin; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "IB700 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: - ibwdt_ping(); - break; - - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, p)) - return -EFAULT; - if (ibwdt_set_heartbeat(new_margin)) - return -EINVAL; - ibwdt_ping(); - /* Fall */ - - case WDIOC_GETTIMEOUT: - return put_user(wd_times[wd_margin], p); - - case WDIOC_SETOPTIONS: - { - int options, retval = -EINVAL; - - if (get_user(options, p)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - ibwdt_disable(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - ibwdt_ping(); - retval = 0; - } - - return retval; - } - - default: - return -ENOTTY; - } - return 0; -} - -static int -ibwdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &ibwdt_is_open)) { - return -EBUSY; - } - if (nowayout) - __module_get(THIS_MODULE); - - /* Activate */ - ibwdt_ping(); - return nonseekable_open(inode, file); -} - -static int -ibwdt_close(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - ibwdt_disable(); - } else { - printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n"); - ibwdt_ping(); - } - clear_bit(0, &ibwdt_is_open); - expect_close = 0; - return 0; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations ibwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = ibwdt_write, - .ioctl = ibwdt_ioctl, - .open = ibwdt_open, - .release = ibwdt_close, -}; - -static struct miscdevice ibwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ibwdt_fops, -}; - -/* - * Init & exit routines - */ - -static int __devinit ibwdt_probe(struct platform_device *dev) -{ - int res; - - spin_lock_init(&ibwdt_lock); - -#if WDT_START != WDT_STOP - if (!request_region(WDT_STOP, 1, "IB700 WDT")) { - printk (KERN_ERR PFX "STOP method I/O %X is not available.\n", WDT_STOP); - res = -EIO; - goto out_nostopreg; - } -#endif - - if (!request_region(WDT_START, 1, "IB700 WDT")) { - printk (KERN_ERR PFX "START method I/O %X is not available.\n", WDT_START); - res = -EIO; - goto out_nostartreg; - } - - res = misc_register(&ibwdt_miscdev); - if (res) { - printk (KERN_ERR PFX "failed to register misc device\n"); - goto out_nomisc; - } - return 0; - -out_nomisc: - release_region(WDT_START, 1); -out_nostartreg: -#if WDT_START != WDT_STOP - release_region(WDT_STOP, 1); -#endif -out_nostopreg: - return res; -} - -static int __devexit ibwdt_remove(struct platform_device *dev) -{ - misc_deregister(&ibwdt_miscdev); - release_region(WDT_START,1); -#if WDT_START != WDT_STOP - release_region(WDT_STOP,1); -#endif - return 0; -} - -static void ibwdt_shutdown(struct platform_device *dev) -{ - /* Turn the WDT off if we have a soft shutdown */ - ibwdt_disable(); -} - -static struct platform_driver ibwdt_driver = { - .probe = ibwdt_probe, - .remove = __devexit_p(ibwdt_remove), - .shutdown = ibwdt_shutdown, - .driver = { - .owner = THIS_MODULE, - .name = DRV_NAME, - }, -}; - -static int __init ibwdt_init(void) -{ - int err; - - printk(KERN_INFO PFX "WDT driver for IB700 single board computer initialising.\n"); - - err = platform_driver_register(&ibwdt_driver); - if (err) - return err; - - ibwdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(ibwdt_platform_device)) { - err = PTR_ERR(ibwdt_platform_device); - goto unreg_platform_driver; - } - - return 0; - -unreg_platform_driver: - platform_driver_unregister(&ibwdt_driver); - return err; -} - -static void __exit ibwdt_exit(void) -{ - platform_device_unregister(ibwdt_platform_device); - platform_driver_unregister(&ibwdt_driver); - printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); -} - -module_init(ibwdt_init); -module_exit(ibwdt_exit); - -MODULE_AUTHOR("Charles Howes "); -MODULE_DESCRIPTION("IB700 SBC watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -/* end of ib700wdt.c */ diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c deleted file mode 100644 index 94155f6..0000000 --- a/drivers/char/watchdog/ibmasr.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * IBM Automatic Server Restart driver. - * - * Copyright (c) 2005 Andrey Panin - * - * Based on driver written by Pete Reynolds. - * Copyright (c) IBM Corporation, 1998-2004. - * - * This software may be used and distributed according to the terms - * of the GNU Public License, incorporated herein by reference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -enum { - ASMTYPE_UNKNOWN, - ASMTYPE_TOPAZ, - ASMTYPE_JASPER, - ASMTYPE_PEARL, - ASMTYPE_JUNIPER, - ASMTYPE_SPRUCE, -}; - -#define PFX "ibmasr: " - -#define TOPAZ_ASR_REG_OFFSET 4 -#define TOPAZ_ASR_TOGGLE 0x40 -#define TOPAZ_ASR_DISABLE 0x80 - -/* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */ -#define PEARL_BASE 0xe04 -#define PEARL_WRITE 0xe06 -#define PEARL_READ 0xe07 - -#define PEARL_ASR_DISABLE_MASK 0x80 /* bit 7: disable = 1, enable = 0 */ -#define PEARL_ASR_TOGGLE_MASK 0x40 /* bit 6: 0, then 1, then 0 */ - -/* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */ -#define JASPER_ASR_REG_OFFSET 0x38 - -#define JASPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1, enable = 0 */ -#define JASPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ - -#define JUNIPER_BASE_ADDRESS 0x54b /* Base address of Juniper ASR */ -#define JUNIPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1 enable = 0 */ -#define JUNIPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ - -#define SPRUCE_BASE_ADDRESS 0x118e /* Base address of Spruce ASR */ -#define SPRUCE_ASR_DISABLE_MASK 0x01 /* bit 1: disable = 1 enable = 0 */ -#define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */ - - -static int nowayout = WATCHDOG_NOWAYOUT; - -static unsigned long asr_is_open; -static char asr_expect_close; - -static unsigned int asr_type, asr_base, asr_length; -static unsigned int asr_read_addr, asr_write_addr; -static unsigned char asr_toggle_mask, asr_disable_mask; - -static void asr_toggle(void) -{ - unsigned char reg = inb(asr_read_addr); - - outb(reg & ~asr_toggle_mask, asr_write_addr); - reg = inb(asr_read_addr); - - outb(reg | asr_toggle_mask, asr_write_addr); - reg = inb(asr_read_addr); - - outb(reg & ~asr_toggle_mask, asr_write_addr); - reg = inb(asr_read_addr); -} - -static void asr_enable(void) -{ - unsigned char reg; - - if (asr_type == ASMTYPE_TOPAZ) { - /* asr_write_addr == asr_read_addr */ - reg = inb(asr_read_addr); - outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE), - asr_read_addr); - } else { - /* - * First make sure the hardware timer is reset by toggling - * ASR hardware timer line. - */ - asr_toggle(); - - reg = inb(asr_read_addr); - outb(reg & ~asr_disable_mask, asr_write_addr); - } - reg = inb(asr_read_addr); -} - -static void asr_disable(void) -{ - unsigned char reg = inb(asr_read_addr); - - if (asr_type == ASMTYPE_TOPAZ) - /* asr_write_addr == asr_read_addr */ - outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE, - asr_read_addr); - else { - outb(reg | asr_toggle_mask, asr_write_addr); - reg = inb(asr_read_addr); - - outb(reg | asr_disable_mask, asr_write_addr); - } - reg = inb(asr_read_addr); -} - -static int __init asr_get_base_address(void) -{ - unsigned char low, high; - const char *type = ""; - - asr_length = 1; - - switch (asr_type) { - case ASMTYPE_TOPAZ: - /* SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F) */ - outb(0x07, 0x2e); - outb(0x07, 0x2f); - - /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */ - outb(0x60, 0x2e); - high = inb(0x2f); - - /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */ - outb(0x61, 0x2e); - low = inb(0x2f); - - asr_base = (high << 16) | low; - asr_read_addr = asr_write_addr = - asr_base + TOPAZ_ASR_REG_OFFSET; - asr_length = 5; - - break; - - case ASMTYPE_JASPER: - type = "Jaspers "; - - /* FIXME: need to use pci_config_lock here, but it's not exported */ - -/* spin_lock_irqsave(&pci_config_lock, flags);*/ - - /* Select the SuperIO chip in the PCI I/O port register */ - outl(0x8000f858, 0xcf8); - - /* - * Read the base address for the SuperIO chip. - * Only the lower 16 bits are valid, but the address is word - * aligned so the last bit must be masked off. - */ - asr_base = inl(0xcfc) & 0xfffe; - -/* spin_unlock_irqrestore(&pci_config_lock, flags);*/ - - asr_read_addr = asr_write_addr = - asr_base + JASPER_ASR_REG_OFFSET; - asr_toggle_mask = JASPER_ASR_TOGGLE_MASK; - asr_disable_mask = JASPER_ASR_DISABLE_MASK; - asr_length = JASPER_ASR_REG_OFFSET + 1; - - break; - - case ASMTYPE_PEARL: - type = "Pearls "; - asr_base = PEARL_BASE; - asr_read_addr = PEARL_READ; - asr_write_addr = PEARL_WRITE; - asr_toggle_mask = PEARL_ASR_TOGGLE_MASK; - asr_disable_mask = PEARL_ASR_DISABLE_MASK; - asr_length = 4; - break; - - case ASMTYPE_JUNIPER: - type = "Junipers "; - asr_base = JUNIPER_BASE_ADDRESS; - asr_read_addr = asr_write_addr = asr_base; - asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK; - asr_disable_mask = JUNIPER_ASR_DISABLE_MASK; - break; - - case ASMTYPE_SPRUCE: - type = "Spruce's "; - asr_base = SPRUCE_BASE_ADDRESS; - asr_read_addr = asr_write_addr = asr_base; - asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK; - asr_disable_mask = SPRUCE_ASR_DISABLE_MASK; - break; - } - - if (!request_region(asr_base, asr_length, "ibmasr")) { - printk(KERN_ERR PFX "address %#x already in use\n", - asr_base); - return -EBUSY; - } - - printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base); - - return 0; -} - - -static ssize_t asr_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (count) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - asr_expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - asr_expect_close = 42; - } - } - asr_toggle(); - } - return count; -} - -static int asr_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - static const struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .identity = "IBM ASR" - }; - void __user *argp = (void __user *)arg; - int __user *p = argp; - int heartbeat; - - 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: - asr_toggle(); - return 0; - - /* - * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT - * and WDIOC_GETTIMEOUT always returns 256. - */ - case WDIOC_GETTIMEOUT: - heartbeat = 256; - return put_user(heartbeat, p); - - case WDIOC_SETOPTIONS: { - int new_options, retval = -EINVAL; - - if (get_user(new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - asr_disable(); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - asr_enable(); - asr_toggle(); - retval = 0; - } - - return retval; - } - } - - return -ENOTTY; -} - -static int asr_open(struct inode *inode, struct file *file) -{ - if(test_and_set_bit(0, &asr_is_open)) - return -EBUSY; - - asr_toggle(); - asr_enable(); - - return nonseekable_open(inode, file); -} - -static int asr_release(struct inode *inode, struct file *file) -{ - if (asr_expect_close == 42) - asr_disable(); - else { - printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); - asr_toggle(); - } - clear_bit(0, &asr_is_open); - asr_expect_close = 0; - return 0; -} - -static const struct file_operations asr_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = asr_write, - .ioctl = asr_ioctl, - .open = asr_open, - .release = asr_release, -}; - -static struct miscdevice asr_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &asr_fops, -}; - - -struct ibmasr_id { - const char *desc; - int type; -}; - -static struct ibmasr_id __initdata ibmasr_id_table[] = { - { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ }, - { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL }, - { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER }, - { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER }, - { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE }, - { NULL } -}; - -static int __init ibmasr_init(void) -{ - struct ibmasr_id *id; - int rc; - - for (id = ibmasr_id_table; id->desc; id++) { - if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) { - asr_type = id->type; - break; - } - } - - if (!asr_type) - return -ENODEV; - - rc = asr_get_base_address(); - if (rc) - return rc; - - rc = misc_register(&asr_miscdev); - if (rc < 0) { - release_region(asr_base, asr_length); - printk(KERN_ERR PFX "failed to register misc device\n"); - return rc; - } - - return 0; -} - -static void __exit ibmasr_exit(void) -{ - if (!nowayout) - asr_disable(); - - misc_deregister(&asr_miscdev); - - release_region(asr_base, asr_length); -} - -module_init(ibmasr_init); -module_exit(ibmasr_exit); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -MODULE_DESCRIPTION("IBM Automatic Server Restart driver"); -MODULE_AUTHOR("Andrey Panin"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/indydog.c b/drivers/char/watchdog/indydog.c deleted file mode 100644 index 788245b..0000000 --- a/drivers/char/watchdog/indydog.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * IndyDog 0.3 A Hardware Watchdog Device for SGI IP22 - * - * (c) Copyright 2002 Guido Guenther , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * based on softdog.c by Alan Cox - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PFX "indydog: " -static int indydog_alive; - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default 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 indydog_start(void) -{ - u32 mc_ctrl0 = sgimc->cpuctrl0; - - mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG; - sgimc->cpuctrl0 = mc_ctrl0; -} - -static void indydog_stop(void) -{ - u32 mc_ctrl0 = sgimc->cpuctrl0; - - mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; - sgimc->cpuctrl0 = mc_ctrl0; - - printk(KERN_INFO PFX "Stopped watchdog timer.\n"); -} - -static void indydog_ping(void) -{ - sgimc->watchdogt = 0; -} - -/* - * Allow only one person to hold it open - */ -static int indydog_open(struct inode *inode, struct file *file) -{ - if (indydog_alive) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - /* Activate timer */ - indydog_start(); - indydog_ping(); - - indydog_alive = 1; - printk(KERN_INFO "Started watchdog timer.\n"); - - return nonseekable_open(inode, file); -} - -static int indydog_release(struct inode *inode, struct file *file) -{ - /* Shut off the timer. - * Lock it in if it's a module and we defined ...NOWAYOUT */ - if (!nowayout) - indydog_stop(); /* Turn the WDT off */ - - indydog_alive = 0; - - return 0; -} - -static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - /* Refresh the timer. */ - if (len) { - indydog_ping(); - } - return len; -} - -static int indydog_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int options, retval = -EINVAL; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = "Hardware Watchdog for SGI IP22", - }; - - switch (cmd) { - default: - return -ENOTTY; - case WDIOC_GETSUPPORT: - if (copy_to_user((struct watchdog_info *)arg, - &ident, sizeof(ident))) - return -EFAULT; - return 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0,(int *)arg); - case WDIOC_KEEPALIVE: - indydog_ping(); - return 0; - case WDIOC_GETTIMEOUT: - return put_user(WATCHDOG_TIMEOUT,(int *)arg); - case WDIOC_SETOPTIONS: - { - if (get_user(options, (int *)arg)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - indydog_stop(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - indydog_start(); - retval = 0; - } - - return retval; - } - } -} - -static int indydog_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - indydog_stop(); /* Turn the WDT off */ - - return NOTIFY_DONE; -} - -static const struct file_operations indydog_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = indydog_write, - .ioctl = indydog_ioctl, - .open = indydog_open, - .release = indydog_release, -}; - -static struct miscdevice indydog_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &indydog_fops, -}; - -static struct notifier_block indydog_notifier = { - .notifier_call = indydog_notify_sys, -}; - -static char banner[] __initdata = - KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n"; - -static int __init watchdog_init(void) -{ - int ret; - - ret = register_reboot_notifier(&indydog_notifier); - if (ret) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - ret); - return ret; - } - - ret = misc_register(&indydog_miscdev); - if (ret) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - unregister_reboot_notifier(&indydog_notifier); - return ret; - } - - printk(banner); - - return 0; -} - -static void __exit watchdog_exit(void) -{ - misc_deregister(&indydog_miscdev); - unregister_reboot_notifier(&indydog_notifier); -} - -module_init(watchdog_init); -module_exit(watchdog_exit); - -MODULE_AUTHOR("Guido Guenther "); -MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/iop_wdt.c b/drivers/char/watchdog/iop_wdt.c deleted file mode 100644 index bbbd91a..0000000 --- a/drivers/char/watchdog/iop_wdt.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * drivers/char/watchdog/iop_wdt.c - * - * WDT driver for Intel I/O Processors - * Copyright (C) 2005, Intel Corporation. - * - * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - * - * Curt E Bruns - * Peter Milne - * Dan Williams - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int nowayout = WATCHDOG_NOWAYOUT; -static unsigned long wdt_status; -static unsigned long boot_status; - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 -#define WDT_ENABLED 2 - -static unsigned long iop_watchdog_timeout(void) -{ - return (0xffffffffUL / get_iop_tick_rate()); -} - -/** - * wdt_supports_disable - determine if we are accessing a iop13xx watchdog - * or iop3xx by whether it has a disable command - */ -static int wdt_supports_disable(void) -{ - int can_disable; - - if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) - can_disable = 1; - else - can_disable = 0; - - return can_disable; -} - -static void wdt_enable(void) -{ - /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF - * Takes approx. 10.7s to timeout - */ - write_wdtcr(IOP_WDTCR_EN_ARM); - write_wdtcr(IOP_WDTCR_EN); -} - -/* returns 0 if the timer was successfully disabled */ -static int wdt_disable(void) -{ - /* Stop Counting */ - if (wdt_supports_disable()) { - write_wdtcr(IOP_WDTCR_DIS_ARM); - write_wdtcr(IOP_WDTCR_DIS); - clear_bit(WDT_ENABLED, &wdt_status); - printk(KERN_INFO "WATCHDOG: Disabled\n"); - return 0; - } else - return 1; -} - -static int iop_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - wdt_enable(); - - set_bit(WDT_ENABLED, &wdt_status); - - return nonseekable_open(inode, file); -} - -static ssize_t -iop_wdt_write(struct file *file, const char *data, size_t len, - loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_enable(); - } - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, - .identity = "iop watchdog", -}; - -static int -iop_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int options; - int ret = -ENOTTY; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user - ((struct watchdog_info *)arg, &ident, sizeof ident)) - ret = -EFAULT; - else - ret = 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int *)arg); - break; - - case WDIOC_GETTIMEOUT: - ret = put_user(iop_watchdog_timeout(), (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - - case WDIOC_SETOPTIONS: - if (get_user(options, (int *)arg)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - if (!nowayout) { - if (wdt_disable() == 0) { - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - ret = 0; - } else - ret = -ENXIO; - } else - ret = 0; - } - - if (options & WDIOS_ENABLECARD) { - wdt_enable(); - ret = 0; - } - break; - } - - return ret; -} - -static int iop_wdt_release(struct inode *inode, struct file *file) -{ - int state = 1; - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - if (test_bit(WDT_ENABLED, &wdt_status)) - state = wdt_disable(); - - /* if the timer is not disbaled reload and notify that we are still - * going down - */ - if (state != 0) { - wdt_enable(); - printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " - "reset in %lu seconds\n", iop_watchdog_timeout()); - } - - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - -static const struct file_operations iop_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = iop_wdt_write, - .ioctl = iop_wdt_ioctl, - .open = iop_wdt_open, - .release = iop_wdt_release, -}; - -static struct miscdevice iop_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &iop_wdt_fops, -}; - -static int __init iop_wdt_init(void) -{ - int ret; - - ret = misc_register(&iop_wdt_miscdev); - if (ret == 0) - printk("iop watchdog timer: timeout %lu sec\n", - iop_watchdog_timeout()); - - /* check if the reset was caused by the watchdog timer */ - boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; - - /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset - * NOTE: An IB Reset will Reset both cores in the IOP342 - */ - write_wdtsr(IOP13XX_WDTCR_IB_RESET); - - return ret; -} - -static void __exit iop_wdt_exit(void) -{ - misc_deregister(&iop_wdt_miscdev); -} - -module_init(iop_wdt_init); -module_exit(iop_wdt_exit); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - -MODULE_AUTHOR("Curt E Bruns "); -MODULE_DESCRIPTION("iop watchdog timer driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/ixp2000_wdt.c b/drivers/char/watchdog/ixp2000_wdt.c deleted file mode 100644 index dc7548d..0000000 --- a/drivers/char/watchdog/ixp2000_wdt.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * drivers/char/watchdog/ixp2000_wdt.c - * - * Watchdog driver for Intel IXP2000 network processors - * - * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek. - * The original version carries these notices: - * - * Author: Deepak Saxena - * - * Copyright 2004 (c) MontaVista, Software, Inc. - * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static int nowayout = WATCHDOG_NOWAYOUT; -static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */ -static unsigned long wdt_status; - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 - -static unsigned long wdt_tick_rate; - -static void -wdt_enable(void) -{ - ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE); - ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE); - ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate); - ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE); -} - -static void -wdt_disable(void) -{ - ixp2000_reg_write(IXP2000_T4_CTL, 0); -} - -static void -wdt_keepalive(void) -{ - ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate); -} - -static int -ixp2000_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - wdt_enable(); - - return nonseekable_open(inode, file); -} - -static ssize_t -ixp2000_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_keepalive(); - } - - return len; -} - - -static struct watchdog_info ident = { - .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING, - .identity = "IXP2000 Watchdog", -}; - -static int -ixp2000_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - int time; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); - if (ret) - break; - - if (time <= 0 || time > 60) { - ret = -EINVAL; - break; - } - - heartbeat = time; - wdt_keepalive(); - /* Fall through */ - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - } - - return ret; -} - -static int -ixp2000_wdt_release(struct inode *inode, struct file *file) -{ - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { - wdt_disable(); - } else { - printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " - "timer will not stop\n"); - } - - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - - -static const struct file_operations ixp2000_wdt_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = ixp2000_wdt_write, - .ioctl = ixp2000_wdt_ioctl, - .open = ixp2000_wdt_open, - .release = ixp2000_wdt_release, -}; - -static struct miscdevice ixp2000_wdt_miscdev = -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ixp2000_wdt_fops, -}; - -static int __init ixp2000_wdt_init(void) -{ - if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { - printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n"); - return -EIO; - } - - wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256; - - return misc_register(&ixp2000_wdt_miscdev); -} - -static void __exit ixp2000_wdt_exit(void) -{ - misc_deregister(&ixp2000_wdt_miscdev); -} - -module_init(ixp2000_wdt_init); -module_exit(ixp2000_wdt_exit); - -MODULE_AUTHOR("Deepak Saxena "); -MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - diff --git a/drivers/char/watchdog/ixp4xx_wdt.c b/drivers/char/watchdog/ixp4xx_wdt.c deleted file mode 100644 index 5864bb8..0000000 --- a/drivers/char/watchdog/ixp4xx_wdt.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * drivers/char/watchdog/ixp4xx_wdt.c - * - * Watchdog driver for Intel IXP4xx network processors - * - * Author: Deepak Saxena - * - * Copyright 2004 (c) MontaVista, Software, Inc. - * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static int nowayout = WATCHDOG_NOWAYOUT; -static int heartbeat = 60; /* (secs) Default is 1 minute */ -static unsigned long wdt_status; -static unsigned long boot_status; - -#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL) - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 - -static void -wdt_enable(void) -{ - *IXP4XX_OSWK = IXP4XX_WDT_KEY; - *IXP4XX_OSWE = 0; - *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat; - *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE; - *IXP4XX_OSWK = 0; -} - -static void -wdt_disable(void) -{ - *IXP4XX_OSWK = IXP4XX_WDT_KEY; - *IXP4XX_OSWE = 0; - *IXP4XX_OSWK = 0; -} - -static int -ixp4xx_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - wdt_enable(); - - return nonseekable_open(inode, file); -} - -static ssize_t -ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_enable(); - } - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "IXP4xx Watchdog", -}; - - -static int -ixp4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - int time; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int *)arg); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); - if (ret) - break; - - if (time <= 0 || time > 60) { - ret = -EINVAL; - break; - } - - heartbeat = time; - wdt_enable(); - /* Fall through */ - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - } - return ret; -} - -static int -ixp4xx_wdt_release(struct inode *inode, struct file *file) -{ - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { - wdt_disable(); - } else { - printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " - "timer will not stop\n"); - } - - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - - -static const struct file_operations ixp4xx_wdt_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = ixp4xx_wdt_write, - .ioctl = ixp4xx_wdt_ioctl, - .open = ixp4xx_wdt_open, - .release = ixp4xx_wdt_release, -}; - -static struct miscdevice ixp4xx_wdt_miscdev = -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ixp4xx_wdt_fops, -}; - -static int __init ixp4xx_wdt_init(void) -{ - int ret; - unsigned long processor_id; - - asm("mrc p15, 0, %0, cr0, cr0, 0;" : "=r"(processor_id) :); - if (!(processor_id & 0xf) && !cpu_is_ixp46x()) { - printk("IXP4XXX Watchdog: Rev. A0 IXP42x CPU detected - " - "watchdog disabled\n"); - - return -ENODEV; - } - - ret = misc_register(&ixp4xx_wdt_miscdev); - if (ret == 0) - printk("IXP4xx Watchdog Timer: heartbeat %d sec\n", heartbeat); - - boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ? - WDIOF_CARDRESET : 0; - - return ret; -} - -static void __exit ixp4xx_wdt_exit(void) -{ - misc_deregister(&ixp4xx_wdt_miscdev); -} - - -module_init(ixp4xx_wdt_init); -module_exit(ixp4xx_wdt_exit); - -MODULE_AUTHOR("Deepak Saxena "); -MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - diff --git a/drivers/char/watchdog/ks8695_wdt.c b/drivers/char/watchdog/ks8695_wdt.c deleted file mode 100644 index 7150fb9..0000000 --- a/drivers/char/watchdog/ks8695_wdt.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Watchdog driver for Kendin/Micrel KS8695. - * - * (C) 2007 Andrew Victor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define WDT_DEFAULT_TIME 5 /* seconds */ -#define WDT_MAX_TIME 171 /* seconds */ - -static int wdt_time = WDT_DEFAULT_TIME; -static int nowayout = WATCHDOG_NOWAYOUT; - -module_param(wdt_time, int, 0); -MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); - -#ifdef CONFIG_WATCHDOG_NOWAYOUT -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#endif - - -static unsigned long ks8695wdt_busy; - -/* ......................................................................... */ - -/* - * Disable the watchdog. - */ -static void inline ks8695_wdt_stop(void) -{ - unsigned long tmcon; - - /* disable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); -} - -/* - * Enable and reset the watchdog. - */ -static void inline ks8695_wdt_start(void) -{ - unsigned long tmcon; - unsigned long tval = wdt_time * CLOCK_TICK_RATE; - - /* disable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - - /* program timer0 */ - __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC); - - /* re-enable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); -} - -/* - * Reload the watchdog timer. (ie, pat the watchdog) - */ -static void inline ks8695_wdt_reload(void) -{ - unsigned long tmcon; - - /* disable, then re-enable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); -} - -/* - * Change the watchdog time interval. - */ -static int ks8695_wdt_settimeout(int new_time) -{ - /* - * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz - * - * Since WDV is a 16-bit counter, the maximum period is - * 65536 / 0.256 = 256 seconds. - */ - if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) - return -EINVAL; - - /* Set new watchdog time. It will be used when ks8695_wdt_start() is called. */ - wdt_time = new_time; - return 0; -} - -/* ......................................................................... */ - -/* - * Watchdog device is opened, and watchdog starts running. - */ -static int ks8695_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &ks8695wdt_busy)) - return -EBUSY; - - ks8695_wdt_start(); - return nonseekable_open(inode, file); -} - -/* - * Close the watchdog device. - * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also - * disabled. - */ -static int ks8695_wdt_close(struct inode *inode, struct file *file) -{ - if (!nowayout) - ks8695_wdt_stop(); /* Disable the watchdog when file is closed */ - - clear_bit(0, &ks8695wdt_busy); - return 0; -} - -static struct watchdog_info ks8695_wdt_info = { - .identity = "ks8695 watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, -}; - -/* - * Handle commands from user-space. - */ -static int ks8695_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_value; - - switch(cmd) { - case WDIOC_KEEPALIVE: - ks8695_wdt_reload(); /* pat the watchdog */ - return 0; - - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ks8695_wdt_info, sizeof(ks8695_wdt_info)) ? -EFAULT : 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_value, p)) - return -EFAULT; - - if (ks8695_wdt_settimeout(new_value)) - return -EINVAL; - - /* Enable new time value */ - ks8695_wdt_start(); - - /* Return current value */ - return put_user(wdt_time, p); - - case WDIOC_GETTIMEOUT: - return put_user(wdt_time, p); - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_SETOPTIONS: - if (get_user(new_value, p)) - return -EFAULT; - - if (new_value & WDIOS_DISABLECARD) - ks8695_wdt_stop(); - if (new_value & WDIOS_ENABLECARD) - ks8695_wdt_start(); - return 0; - - default: - return -ENOTTY; - } -} - -/* - * Pat the watchdog whenever device is written to. - */ -static ssize_t ks8695_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - ks8695_wdt_reload(); /* pat the watchdog */ - return len; -} - -/* ......................................................................... */ - -static const struct file_operations ks8695wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = ks8695_wdt_ioctl, - .open = ks8695_wdt_open, - .release = ks8695_wdt_close, - .write = ks8695_wdt_write, -}; - -static struct miscdevice ks8695wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ks8695wdt_fops, -}; - -static int __init ks8695wdt_probe(struct platform_device *pdev) -{ - int res; - - if (ks8695wdt_miscdev.parent) - return -EBUSY; - ks8695wdt_miscdev.parent = &pdev->dev; - - res = misc_register(&ks8695wdt_miscdev); - if (res) - return res; - - printk("KS8695 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : ""); - return 0; -} - -static int __exit ks8695wdt_remove(struct platform_device *pdev) -{ - int res; - - res = misc_deregister(&ks8695wdt_miscdev); - if (!res) - ks8695wdt_miscdev.parent = NULL; - - return res; -} - -static void ks8695wdt_shutdown(struct platform_device *pdev) -{ - ks8695_wdt_stop(); -} - -#ifdef CONFIG_PM - -static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ - ks8695_wdt_stop(); - return 0; -} - -static int ks8695wdt_resume(struct platform_device *pdev) -{ - if (ks8695wdt_busy) - ks8695_wdt_start(); - return 0; -} - -#else -#define ks8695wdt_suspend NULL -#define ks8695wdt_resume NULL -#endif - -static struct platform_driver ks8695wdt_driver = { - .probe = ks8695wdt_probe, - .remove = __exit_p(ks8695wdt_remove), - .shutdown = ks8695wdt_shutdown, - .suspend = ks8695wdt_suspend, - .resume = ks8695wdt_resume, - .driver = { - .name = "ks8695_wdt", - .owner = THIS_MODULE, - }, -}; - -static int __init ks8695_wdt_init(void) -{ - /* Check that the heartbeat value is within range; if not reset to the default */ - if (ks8695_wdt_settimeout(wdt_time)) { - ks8695_wdt_settimeout(WDT_DEFAULT_TIME); - pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i, using %d\n", wdt_time, WDT_MAX_TIME); - } - - return platform_driver_register(&ks8695wdt_driver); -} - -static void __exit ks8695_wdt_exit(void) -{ - platform_driver_unregister(&ks8695wdt_driver); -} - -module_init(ks8695_wdt_init); -module_exit(ks8695_wdt_exit); - -MODULE_AUTHOR("Andrew Victor"); -MODULE_DESCRIPTION("Watchdog driver for KS8695"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c deleted file mode 100644 index 6d35bb1..0000000 --- a/drivers/char/watchdog/machzwd.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * MachZ ZF-Logic Watchdog Timer driver for Linux - * - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * The author does 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. - * - * Author: Fernando Fuganti - * - * Based on sbc60xxwdt.c by Jakob Oestergaard - * - * - * We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the - * following periods: - * wd#1 - 2 seconds; - * wd#2 - 7.2 ms; - * After the expiration of wd#1, it can generate a NMI, SCI, SMI, or - * a system RESET and it starts wd#2 that unconditionaly will RESET - * the system when the counter reaches zero. - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* ports */ -#define ZF_IOBASE 0x218 -#define INDEX 0x218 -#define DATA_B 0x219 -#define DATA_W 0x21A -#define DATA_D 0x21A - -/* indexes */ /* size */ -#define ZFL_VERSION 0x02 /* 16 */ -#define CONTROL 0x10 /* 16 */ -#define STATUS 0x12 /* 8 */ -#define COUNTER_1 0x0C /* 16 */ -#define COUNTER_2 0x0E /* 8 */ -#define PULSE_LEN 0x0F /* 8 */ - -/* controls */ -#define ENABLE_WD1 0x0001 -#define ENABLE_WD2 0x0002 -#define RESET_WD1 0x0010 -#define RESET_WD2 0x0020 -#define GEN_SCI 0x0100 -#define GEN_NMI 0x0200 -#define GEN_SMI 0x0400 -#define GEN_RESET 0x0800 - - -/* utilities */ - -#define WD1 0 -#define WD2 1 - -#define zf_writew(port, data) { outb(port, INDEX); outw(data, DATA_W); } -#define zf_writeb(port, data) { outb(port, INDEX); outb(data, DATA_B); } -#define zf_get_ZFL_version() zf_readw(ZFL_VERSION) - - -static unsigned short zf_readw(unsigned char port) -{ - outb(port, INDEX); - return inw(DATA_W); -} - - -MODULE_AUTHOR("Fernando Fuganti "); -MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -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) ")"); - -#define PFX "machzwd" - -static struct watchdog_info zf_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "ZF-Logic watchdog", -}; - - -/* - * action refers to action taken when watchdog resets - * 0 = GEN_RESET - * 1 = GEN_SMI - * 2 = GEN_NMI - * 3 = GEN_SCI - * defaults to GEN_RESET (0) - */ -static int action = 0; -module_param(action, int, 0); -MODULE_PARM_DESC(action, "after watchdog resets, generate: 0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI"); - -static void zf_ping(unsigned long data); - -static int zf_action = GEN_RESET; -static unsigned long zf_is_open; -static char zf_expect_close; -static spinlock_t zf_lock; -static spinlock_t zf_port_lock; -static DEFINE_TIMER(zf_timer, zf_ping, 0, 0); -static unsigned long next_heartbeat = 0; - - -/* timeout for user land heart beat (10 seconds) */ -#define ZF_USER_TIMEO (HZ*10) - -/* timeout for hardware watchdog (~500ms) */ -#define ZF_HW_TIMEO (HZ/2) - -/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */ -#define ZF_CTIMEOUT 0xffff - -#ifndef ZF_DEBUG -# define dprintk(format, args...) -#else -# define dprintk(format, args...) printk(KERN_DEBUG PFX ":%s:%d: " format, __FUNCTION__, __LINE__ , ## args) -#endif - - -static inline void zf_set_status(unsigned char new) -{ - zf_writeb(STATUS, new); -} - - -/* CONTROL register functions */ - -static inline unsigned short zf_get_control(void) -{ - return zf_readw(CONTROL); -} - -static inline void zf_set_control(unsigned short new) -{ - zf_writew(CONTROL, new); -} - - -/* WD#? counter functions */ -/* - * Just set counter value - */ - -static inline void zf_set_timer(unsigned short new, unsigned char n) -{ - switch(n){ - case WD1: - zf_writew(COUNTER_1, new); - case WD2: - zf_writeb(COUNTER_2, new > 0xff ? 0xff : new); - default: - return; - } -} - -/* - * stop hardware timer - */ -static void zf_timer_off(void) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - /* stop internal ping */ - del_timer_sync(&zf_timer); - - spin_lock_irqsave(&zf_port_lock, flags); - /* stop watchdog timer */ - ctrl_reg = zf_get_control(); - ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */ - ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - printk(KERN_INFO PFX ": Watchdog timer is now disabled\n"); -} - - -/* - * start hardware timer - */ -static void zf_timer_on(void) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - spin_lock_irqsave(&zf_port_lock, flags); - - zf_writeb(PULSE_LEN, 0xff); - - zf_set_timer(ZF_CTIMEOUT, WD1); - - /* user land ping */ - next_heartbeat = jiffies + ZF_USER_TIMEO; - - /* start the timer for internal ping */ - mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); - - /* start watchdog timer */ - ctrl_reg = zf_get_control(); - ctrl_reg |= (ENABLE_WD1|zf_action); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - printk(KERN_INFO PFX ": Watchdog timer is now enabled\n"); -} - - -static void zf_ping(unsigned long data) -{ - unsigned int ctrl_reg = 0; - unsigned long flags; - - zf_writeb(COUNTER_2, 0xff); - - if(time_before(jiffies, next_heartbeat)){ - - dprintk("time_before: %ld\n", next_heartbeat - jiffies); - - /* - * reset event is activated by transition from 0 to 1 on - * RESET_WD1 bit and we assume that it is already zero... - */ - - spin_lock_irqsave(&zf_port_lock, flags); - ctrl_reg = zf_get_control(); - ctrl_reg |= RESET_WD1; - zf_set_control(ctrl_reg); - - /* ...and nothing changes until here */ - ctrl_reg &= ~(RESET_WD1); - zf_set_control(ctrl_reg); - spin_unlock_irqrestore(&zf_port_lock, flags); - - mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); - }else{ - printk(KERN_CRIT PFX ": I will reset your machine\n"); - } -} - -static ssize_t zf_write(struct file *file, const char __user *buf, size_t count, - loff_t *ppos) -{ - /* See if we got the magic character */ - if(count){ - - /* - * no need to check for close confirmation - * no way to disable watchdog ;) - */ - if (!nowayout) { - size_t ofs; - - /* - * note: just in case someone wrote the magic character - * five months ago... - */ - zf_expect_close = 0; - - /* now scan */ - for (ofs = 0; ofs != count; ofs++){ - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V'){ - zf_expect_close = 42; - dprintk("zf_expect_close = 42\n"); - } - } - } - - /* - * Well, anyhow someone wrote to us, - * we should return that favour - */ - next_heartbeat = jiffies + ZF_USER_TIMEO; - dprintk("user ping at %ld\n", jiffies); - - } - - return count; -} - -static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &zf_info, sizeof(zf_info))) - return -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_KEEPALIVE: - zf_ping(0); - break; - - default: - return -ENOTTY; - } - - return 0; -} - -static int zf_open(struct inode *inode, struct file *file) -{ - spin_lock(&zf_lock); - if(test_and_set_bit(0, &zf_is_open)) { - spin_unlock(&zf_lock); - return -EBUSY; - } - - if (nowayout) - __module_get(THIS_MODULE); - - spin_unlock(&zf_lock); - - zf_timer_on(); - - return nonseekable_open(inode, file); -} - -static int zf_close(struct inode *inode, struct file *file) -{ - if(zf_expect_close == 42){ - zf_timer_off(); - } else { - del_timer(&zf_timer); - printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n"); - } - - spin_lock(&zf_lock); - clear_bit(0, &zf_is_open); - spin_unlock(&zf_lock); - - zf_expect_close = 0; - - return 0; -} - -/* - * Notifier for system down - */ - -static int zf_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code == SYS_DOWN || code == SYS_HALT){ - zf_timer_off(); - } - - return NOTIFY_DONE; -} - - - - -static const struct file_operations zf_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = zf_write, - .ioctl = zf_ioctl, - .open = zf_open, - .release = zf_close, -}; - -static struct miscdevice zf_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &zf_fops, -}; - - -/* - * The device needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ -static struct notifier_block zf_notifier = { - .notifier_call = zf_notify_sys, -}; - -static void __init zf_show_action(int act) -{ - char *str[] = { "RESET", "SMI", "NMI", "SCI" }; - - printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]); -} - -static int __init zf_init(void) -{ - int ret; - - printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n"); - - ret = zf_get_ZFL_version(); - if ((!ret) || (ret == 0xffff)) { - printk(KERN_WARNING PFX ": no ZF-Logic found\n"); - return -ENODEV; - } - - if((action <= 3) && (action >= 0)){ - zf_action = zf_action>>action; - } else - action = 0; - - zf_show_action(action); - - spin_lock_init(&zf_lock); - spin_lock_init(&zf_port_lock); - - if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){ - printk(KERN_ERR "cannot reserve I/O ports at %d\n", - ZF_IOBASE); - ret = -EBUSY; - goto no_region; - } - - ret = register_reboot_notifier(&zf_notifier); - if(ret){ - printk(KERN_ERR "can't register reboot notifier (err=%d)\n", - ret); - goto no_reboot; - } - - ret = misc_register(&zf_miscdev); - if (ret){ - printk(KERN_ERR "can't misc_register on minor=%d\n", - WATCHDOG_MINOR); - goto no_misc; - } - - zf_set_status(0); - zf_set_control(0); - - return 0; - -no_misc: - unregister_reboot_notifier(&zf_notifier); -no_reboot: - release_region(ZF_IOBASE, 3); -no_region: - return ret; -} - - -static void __exit zf_exit(void) -{ - zf_timer_off(); - - misc_deregister(&zf_miscdev); - unregister_reboot_notifier(&zf_notifier); - release_region(ZF_IOBASE, 3); -} - -module_init(zf_init); -module_exit(zf_exit); diff --git a/drivers/char/watchdog/mixcomwd.c b/drivers/char/watchdog/mixcomwd.c deleted file mode 100644 index 1adf1d5..0000000 --- a/drivers/char/watchdog/mixcomwd.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * MixCom Watchdog: A Simple Hardware Watchdog Device - * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis - * - * Author: Gergely Madarasz - * - * Copyright (c) 1999 ITConsult-Pro Co. - * - * 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. - * - * Version 0.1 (99/04/15): - * - first version - * - * Version 0.2 (99/06/16): - * - added kernel timer watchdog ping after close - * since the hardware does not support watchdog shutdown - * - * Version 0.3 (99/06/21): - * - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls - * - * Version 0.3.1 (99/06/22): - * - allow module removal while internal timer is active, - * print warning about probable reset - * - * Version 0.4 (99/11/15): - * - support for one more type board - * - * Version 0.5 (2001/12/14) Matt Domsch - * - added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * - * Version 0.6 (2002/04/12): Rob Radez - * - make mixcomwd_opened unsigned, - * removed lock_kernel/unlock_kernel from mixcomwd_release, - * modified ioctl a bit to conform to API - * - */ - -#define VERSION "0.6" -#define WATCHDOG_NAME "mixcomwd" -#define PFX WATCHDOG_NAME ": " - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * We have two types of cards that can be probed: - * 1) The Mixcom cards: these cards can be found at addresses - * 0x180, 0x280, 0x380 with an additional offset of 0xc10. - * (Or 0xd90, 0xe90, 0xf90). - * 2) The FlashCOM cards: these cards can be set up at - * 0x300 -> 0x378, in 0x8 jumps with an offset of 0x04. - * (Or 0x304 -> 0x37c in 0x8 jumps). - * Each card has it's own ID. - */ -#define MIXCOM_ID 0x11 -#define FLASHCOM_ID 0x18 -static struct { - int ioport; - int id; -} mixcomwd_io_info[] __devinitdata = { - /* The Mixcom cards */ - {0x0d90, MIXCOM_ID}, - {0x0e90, MIXCOM_ID}, - {0x0f90, MIXCOM_ID}, - /* The FlashCOM cards */ - {0x0304, FLASHCOM_ID}, - {0x030c, FLASHCOM_ID}, - {0x0314, FLASHCOM_ID}, - {0x031c, FLASHCOM_ID}, - {0x0324, FLASHCOM_ID}, - {0x032c, FLASHCOM_ID}, - {0x0334, FLASHCOM_ID}, - {0x033c, FLASHCOM_ID}, - {0x0344, FLASHCOM_ID}, - {0x034c, FLASHCOM_ID}, - {0x0354, FLASHCOM_ID}, - {0x035c, FLASHCOM_ID}, - {0x0364, FLASHCOM_ID}, - {0x036c, FLASHCOM_ID}, - {0x0374, FLASHCOM_ID}, - {0x037c, FLASHCOM_ID}, - /* The end of the list */ - {0x0000, 0}, -}; - -static void mixcomwd_timerfun(unsigned long d); - -static unsigned long mixcomwd_opened; /* long req'd for setbit --RR */ - -static int watchdog_port; -static int mixcomwd_timer_alive; -static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun, 0, 0); -static char expect_close; - -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 mixcomwd_ping(void) -{ - outb_p(55,watchdog_port); - return; -} - -static void mixcomwd_timerfun(unsigned long d) -{ - mixcomwd_ping(); - - mod_timer(&mixcomwd_timer, jiffies + 5 * HZ); -} - -/* - * Allow only one person to hold it open - */ - -static int mixcomwd_open(struct inode *inode, struct file *file) -{ - if(test_and_set_bit(0,&mixcomwd_opened)) { - return -EBUSY; - } - mixcomwd_ping(); - - if (nowayout) { - /* - * fops_get() code via open() has already done - * a try_module_get() so it is safe to do the - * __module_get(). - */ - __module_get(THIS_MODULE); - } else { - if(mixcomwd_timer_alive) { - del_timer(&mixcomwd_timer); - mixcomwd_timer_alive=0; - } - } - return nonseekable_open(inode, file); -} - -static int mixcomwd_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - if(mixcomwd_timer_alive) { - printk(KERN_ERR PFX "release called while internal timer alive"); - return -EBUSY; - } - mixcomwd_timer_alive=1; - mod_timer(&mixcomwd_timer, jiffies + 5 * HZ); - } else { - printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n"); - } - - clear_bit(0,&mixcomwd_opened); - expect_close=0; - return 0; -} - - -static ssize_t mixcomwd_write(struct file *file, const char __user *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 = 42; - } - } - mixcomwd_ping(); - } - return len; -} - -static int mixcomwd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - int status; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "MixCOM watchdog", - }; - - switch(cmd) - { - case WDIOC_GETSTATUS: - status=mixcomwd_opened; - if (!nowayout) { - status|=mixcomwd_timer_alive; - } - if (copy_to_user(p, &status, sizeof(int))) { - return -EFAULT; - } - break; - case WDIOC_GETBOOTSTATUS: - if (copy_to_user(p, &status, sizeof(int))) { - return -EFAULT; - } - break; - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof(ident))) { - return -EFAULT; - } - break; - case WDIOC_KEEPALIVE: - mixcomwd_ping(); - break; - default: - return -ENOTTY; - } - return 0; -} - -static const struct file_operations mixcomwd_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = mixcomwd_write, - .ioctl = mixcomwd_ioctl, - .open = mixcomwd_open, - .release = mixcomwd_release, -}; - -static struct miscdevice mixcomwd_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mixcomwd_fops, -}; - -static int __init checkcard(int port, int card_id) -{ - int id; - - if (!request_region(port, 1, "MixCOM watchdog")) { - return 0; - } - - id=inb_p(port); - if (card_id==MIXCOM_ID) - id &= 0x3f; - - if (id!=card_id) { - release_region(port, 1); - return 0; - } - return 1; -} - -static int __init mixcomwd_init(void) -{ - int i; - int ret; - int found=0; - - for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) { - if (checkcard(mixcomwd_io_info[i].ioport, - mixcomwd_io_info[i].id)) { - found = 1; - watchdog_port = mixcomwd_io_info[i].ioport; - } - } - - if (!found) { - printk(KERN_ERR PFX "No card detected, or port not available.\n"); - return -ENODEV; - } - - ret = misc_register(&mixcomwd_miscdev); - if (ret) - { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto error_misc_register_watchdog; - } - - printk(KERN_INFO "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n", - VERSION, watchdog_port); - - return 0; - -error_misc_register_watchdog: - release_region(watchdog_port, 1); - watchdog_port = 0x0000; - return ret; -} - -static void __exit mixcomwd_exit(void) -{ - if (!nowayout) { - if(mixcomwd_timer_alive) { - printk(KERN_WARNING PFX "I quit now, hardware will" - " probably reboot!\n"); - del_timer_sync(&mixcomwd_timer); - mixcomwd_timer_alive=0; - } - } - misc_deregister(&mixcomwd_miscdev); - release_region(watchdog_port,1); -} - -module_init(mixcomwd_init); -module_exit(mixcomwd_exit); - -MODULE_AUTHOR("Gergely Madarasz "); -MODULE_DESCRIPTION("MixCom Watchdog driver"); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/mpc5200_wdt.c b/drivers/char/watchdog/mpc5200_wdt.c deleted file mode 100644 index 9cfb975..0000000 --- a/drivers/char/watchdog/mpc5200_wdt.c +++ /dev/null @@ -1,286 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define GPT_MODE_WDT (1<<15) -#define GPT_MODE_CE (1<<12) -#define GPT_MODE_MS_TIMER (0x4) - - -struct mpc5200_wdt { - unsigned count; /* timer ticks before watchdog kicks in */ - long ipb_freq; - struct miscdevice miscdev; - struct resource mem; - struct mpc52xx_gpt __iomem *regs; - spinlock_t io_lock; -}; - -/* is_active stores wether or not the /dev/watchdog device is opened */ -static unsigned long is_active; - -/* misc devices don't provide a way, to get back to 'dev' or 'miscdev' from - * file operations, which sucks. But there can be max 1 watchdog anyway, so... - */ -static struct mpc5200_wdt *wdt_global; - - -/* helper to calculate timeout in timer counts */ -static void mpc5200_wdt_set_timeout(struct mpc5200_wdt *wdt, int timeout) -{ - /* use biggest prescaler of 64k */ - wdt->count = (wdt->ipb_freq + 0xffff) / 0x10000 * timeout; - - if (wdt->count > 0xffff) - wdt->count = 0xffff; -} -/* return timeout in seconds (calculated from timer count) */ -static int mpc5200_wdt_get_timeout(struct mpc5200_wdt *wdt) -{ - return wdt->count * 0x10000 / wdt->ipb_freq; -} - - -/* watchdog operations */ -static int mpc5200_wdt_start(struct mpc5200_wdt *wdt) -{ - spin_lock(&wdt->io_lock); - /* disable */ - out_be32(&wdt->regs->mode, 0); - /* set timeout, with maximum prescaler */ - out_be32(&wdt->regs->count, 0x0 | wdt->count); - /* enable watchdog */ - out_be32(&wdt->regs->mode, GPT_MODE_CE | GPT_MODE_WDT | GPT_MODE_MS_TIMER); - spin_unlock(&wdt->io_lock); - - return 0; -} -static int mpc5200_wdt_ping(struct mpc5200_wdt *wdt) -{ - spin_lock(&wdt->io_lock); - /* writing A5 to OCPW resets the watchdog */ - out_be32(&wdt->regs->mode, 0xA5000000 | (0xffffff & in_be32(&wdt->regs->mode))); - spin_unlock(&wdt->io_lock); - return 0; -} -static int mpc5200_wdt_stop(struct mpc5200_wdt *wdt) -{ - spin_lock(&wdt->io_lock); - /* disable */ - out_be32(&wdt->regs->mode, 0); - spin_unlock(&wdt->io_lock); - return 0; -} - - -/* file operations */ -static ssize_t mpc5200_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - struct mpc5200_wdt *wdt = file->private_data; - mpc5200_wdt_ping(wdt); - return 0; -} -static struct watchdog_info mpc5200_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "mpc5200 watchdog on GPT0", -}; -static int mpc5200_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct mpc5200_wdt *wdt = file->private_data; - int __user *data = (int __user *)arg; - int timeout; - int ret = 0; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user(data, &mpc5200_wdt_info, - sizeof(mpc5200_wdt_info)); - if (ret) - ret = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, data); - break; - - case WDIOC_KEEPALIVE: - mpc5200_wdt_ping(wdt); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(timeout, data); - if (ret) - break; - mpc5200_wdt_set_timeout(wdt, timeout); - mpc5200_wdt_start(wdt); - /* fall through and return the timeout */ - - case WDIOC_GETTIMEOUT: - timeout = mpc5200_wdt_get_timeout(wdt); - ret = put_user(timeout, data); - break; - - default: - ret = -ENOTTY; - } - return ret; -} -static int mpc5200_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; - - /* Set and activate the watchdog */ - mpc5200_wdt_set_timeout(wdt_global, 30); - mpc5200_wdt_start(wdt_global); - file->private_data = wdt_global; - return nonseekable_open(inode, file); -} -static int mpc5200_wdt_release(struct inode *inode, struct file *file) -{ -#if WATCHDOG_NOWAYOUT == 0 - struct mpc5200_wdt *wdt = file->private_data; - mpc5200_wdt_stop(wdt); - wdt->count = 0; /* == disabled */ -#endif - clear_bit(0, &is_active); - return 0; -} - -static struct file_operations mpc5200_wdt_fops = { - .owner = THIS_MODULE, - .write = mpc5200_wdt_write, - .ioctl = mpc5200_wdt_ioctl, - .open = mpc5200_wdt_open, - .release = mpc5200_wdt_release, -}; - -/* module operations */ -static int mpc5200_wdt_probe(struct of_device *op, const struct of_device_id *match) -{ - struct mpc5200_wdt *wdt; - int err; - const void *has_wdt; - int size; - - has_wdt = of_get_property(op->node, "has-wdt", NULL); - if (!has_wdt) - return -ENODEV; - - wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); - if (!wdt) - return -ENOMEM; - - wdt->ipb_freq = mpc52xx_find_ipb_freq(op->node); - - err = of_address_to_resource(op->node, 0, &wdt->mem); - if (err) - goto out_free; - size = wdt->mem.end - wdt->mem.start + 1; - if (!request_mem_region(wdt->mem.start, size, "mpc5200_wdt")) { - err = -ENODEV; - goto out_free; - } - wdt->regs = ioremap(wdt->mem.start, size); - if (!wdt->regs) { - err = -ENODEV; - goto out_release; - } - - dev_set_drvdata(&op->dev, wdt); - spin_lock_init(&wdt->io_lock); - - wdt->miscdev = (struct miscdevice) { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpc5200_wdt_fops, - .parent = &op->dev, - }; - wdt_global = wdt; - err = misc_register(&wdt->miscdev); - if (!err) - return 0; - - iounmap(wdt->regs); - out_release: - release_mem_region(wdt->mem.start, size); - out_free: - kfree(wdt); - return err; -} - -static int mpc5200_wdt_remove(struct of_device *op) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - - mpc5200_wdt_stop(wdt); - misc_deregister(&wdt->miscdev); - iounmap(wdt->regs); - release_mem_region(wdt->mem.start, wdt->mem.end - wdt->mem.start + 1); - kfree(wdt); - - return 0; -} -static int mpc5200_wdt_suspend(struct of_device *op, pm_message_t state) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - mpc5200_wdt_stop(wdt); - return 0; -} -static int mpc5200_wdt_resume(struct of_device *op) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - if (wdt->count) - mpc5200_wdt_start(wdt); - return 0; -} -static int mpc5200_wdt_shutdown(struct of_device *op) -{ - struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); - mpc5200_wdt_stop(wdt); - return 0; -} - -static struct of_device_id mpc5200_wdt_match[] = { - { .compatible = "mpc5200-gpt", }, - {}, -}; -static struct of_platform_driver mpc5200_wdt_driver = { - .owner = THIS_MODULE, - .name = "mpc5200-gpt-wdt", - .match_table = mpc5200_wdt_match, - .probe = mpc5200_wdt_probe, - .remove = mpc5200_wdt_remove, - .suspend = mpc5200_wdt_suspend, - .resume = mpc5200_wdt_resume, - .shutdown = mpc5200_wdt_shutdown, -}; - - -static int __init mpc5200_wdt_init(void) -{ - return of_register_platform_driver(&mpc5200_wdt_driver); -} - -static void __exit mpc5200_wdt_exit(void) -{ - of_unregister_platform_driver(&mpc5200_wdt_driver); -} - -module_init(mpc5200_wdt_init); -module_exit(mpc5200_wdt_exit); - -MODULE_AUTHOR("Domen Puncer "); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/mpc83xx_wdt.c b/drivers/char/watchdog/mpc83xx_wdt.c deleted file mode 100644 index a0bf95f..0000000 --- a/drivers/char/watchdog/mpc83xx_wdt.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * mpc83xx_wdt.c - MPC83xx watchdog userspace interface - * - * Authors: Dave Updegraff - * Kumar Gala - * Attribution: from 83xx_wst: Florian Schirmer - * ..and from sc520_wdt - * - * Note: it appears that you can only actually ENABLE or DISABLE the thing - * once after POR. Once enabled, you cannot disable, and vice versa. - * - * 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 - -struct mpc83xx_wdt { - __be32 res0; - __be32 swcrr; /* System watchdog control register */ -#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ -#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ -#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ -#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ - __be32 swcnr; /* System watchdog count register */ - u8 res1[2]; - __be16 swsrr; /* System watchdog service register */ - u8 res2[0xF0]; -}; - -static struct mpc83xx_wdt __iomem *wd_base; - -static u16 timeout = 0xffff; -module_param(timeout, ushort, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in ticks. (0swsrr, 0x556c); - out_be16(&wd_base->swsrr, 0xaa39); - spin_unlock(&wdt_spinlock); -} - -static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (count) - mpc83xx_wdt_keepalive(); - return count; -} - -static int mpc83xx_wdt_open(struct inode *inode, struct file *file) -{ - u32 tmp = SWCRR_SWEN; - if (test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - - /* Once we start the watchdog we can't stop it */ - __module_get(THIS_MODULE); - - /* Good, fire up the show */ - if (prescale) - tmp |= SWCRR_SWPR; - if (reset) - tmp |= SWCRR_SWRI; - - tmp |= timeout << 16; - - out_be32(&wd_base->swcrr, tmp); - - return nonseekable_open(inode, file); -} - -static int mpc83xx_wdt_release(struct inode *inode, struct file *file) -{ - printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); - mpc83xx_wdt_keepalive(); - clear_bit(0, &wdt_is_open); - return 0; -} - -static int mpc83xx_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = "MPC83xx", - }; - - 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: - mpc83xx_wdt_keepalive(); - return 0; - case WDIOC_GETTIMEOUT: - return put_user(timeout_sec, p); - default: - return -ENOTTY; - } -} - -static const struct file_operations mpc83xx_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = mpc83xx_wdt_write, - .ioctl = mpc83xx_wdt_ioctl, - .open = mpc83xx_wdt_open, - .release = mpc83xx_wdt_release, -}; - -static struct miscdevice mpc83xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpc83xx_wdt_fops, -}; - -static int __devinit mpc83xx_wdt_probe(struct platform_device *dev) -{ - struct resource *r; - int ret; - unsigned int *freq = dev->dev.platform_data; - - /* get a pointer to the register memory */ - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - - if (!r) { - ret = -ENODEV; - goto err_out; - } - - wd_base = ioremap(r->start, sizeof (struct mpc83xx_wdt)); - - if (wd_base == NULL) { - ret = -ENOMEM; - goto err_out; - } - - ret = misc_register(&mpc83xx_wdt_miscdev); - if (ret) { - printk(KERN_ERR "cannot register miscdev on minor=%d " - "(err=%d)\n", - WATCHDOG_MINOR, ret); - goto err_unmap; - } - - /* Calculate the timeout in seconds */ - if (prescale) - timeout_sec = (timeout * 0x10000) / (*freq); - else - timeout_sec = timeout / (*freq); - - printk(KERN_INFO "WDT driver for MPC83xx initialized. " - "mode:%s timeout=%d (%d seconds)\n", - reset ? "reset":"interrupt", timeout, timeout_sec); - - spin_lock_init(&wdt_spinlock); - - return 0; - -err_unmap: - iounmap(wd_base); -err_out: - return ret; -} - -static int __devexit mpc83xx_wdt_remove(struct platform_device *dev) -{ - misc_deregister(&mpc83xx_wdt_miscdev); - iounmap(wd_base); - - return 0; -} - -static struct platform_driver mpc83xx_wdt_driver = { - .probe = mpc83xx_wdt_probe, - .remove = __devexit_p(mpc83xx_wdt_remove), - .driver = { - .name = "mpc83xx_wdt", - }, -}; - -static int __init mpc83xx_wdt_init(void) -{ - return platform_driver_register(&mpc83xx_wdt_driver); -} - -static void __exit mpc83xx_wdt_exit(void) -{ - platform_driver_unregister(&mpc83xx_wdt_driver); -} - -module_init(mpc83xx_wdt_init); -module_exit(mpc83xx_wdt_exit); - -MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); -MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c deleted file mode 100644 index 85b5734..0000000 --- a/drivers/char/watchdog/mpc8xx_wdt.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * mpc8xx_wdt.c - MPC8xx watchdog userspace interface - * - * Author: Florian Schirmer - * - * 2002 (c) Florian Schirmer This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned long wdt_opened; -static int wdt_status; - -static void mpc8xx_wdt_handler_disable(void) -{ - volatile uint __iomem *piscr; - piscr = (uint *)&((immap_t*)IMAP_ADDR)->im_sit.sit_piscr; - - if (!m8xx_has_internal_rtc) - m8xx_wdt_stop_timer(); - else - out_be32(piscr, in_be32(piscr) & ~(PISCR_PIE | PISCR_PTE)); - - printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); -} - -static void mpc8xx_wdt_handler_enable(void) -{ - volatile uint __iomem *piscr; - piscr = (uint *)&((immap_t*)IMAP_ADDR)->im_sit.sit_piscr; - - if (!m8xx_has_internal_rtc) - m8xx_wdt_install_timer(); - else - out_be32(piscr, in_be32(piscr) | PISCR_PIE | PISCR_PTE); - - printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); -} - -static int mpc8xx_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &wdt_opened)) - return -EBUSY; - - m8xx_wdt_reset(); - mpc8xx_wdt_handler_disable(); - - return nonseekable_open(inode, file); -} - -static int mpc8xx_wdt_release(struct inode *inode, struct file *file) -{ - m8xx_wdt_reset(); - -#if !defined(CONFIG_WATCHDOG_NOWAYOUT) - mpc8xx_wdt_handler_enable(); -#endif - - clear_bit(0, &wdt_opened); - - return 0; -} - -static ssize_t mpc8xx_wdt_write(struct file *file, const char *data, size_t len, - loff_t * ppos) -{ - if (len) - m8xx_wdt_reset(); - - return len; -} - -static int mpc8xx_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int timeout; - static struct watchdog_info info = { - .options = WDIOF_KEEPALIVEPING, - .firmware_version = 0, - .identity = "MPC8xx watchdog", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - if (put_user(wdt_status, (int *)arg)) - return -EFAULT; - wdt_status &= ~WDIOF_KEEPALIVEPING; - break; - - case WDIOC_GETTEMP: - return -EOPNOTSUPP; - - case WDIOC_SETOPTIONS: - return -EOPNOTSUPP; - - case WDIOC_KEEPALIVE: - m8xx_wdt_reset(); - wdt_status |= WDIOF_KEEPALIVEPING; - break; - - case WDIOC_SETTIMEOUT: - return -EOPNOTSUPP; - - case WDIOC_GETTIMEOUT: - timeout = m8xx_wdt_get_timeout(); - if (put_user(timeout, (int *)arg)) - return -EFAULT; - break; - - default: - return -ENOTTY; - } - - return 0; -} - -static const struct file_operations mpc8xx_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = mpc8xx_wdt_write, - .ioctl = mpc8xx_wdt_ioctl, - .open = mpc8xx_wdt_open, - .release = mpc8xx_wdt_release, -}; - -static struct miscdevice mpc8xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpc8xx_wdt_fops, -}; - -static int __init mpc8xx_wdt_init(void) -{ - return misc_register(&mpc8xx_wdt_miscdev); -} - -static void __exit mpc8xx_wdt_exit(void) -{ - misc_deregister(&mpc8xx_wdt_miscdev); - - m8xx_wdt_reset(); - mpc8xx_wdt_handler_enable(); -} - -module_init(mpc8xx_wdt_init); -module_exit(mpc8xx_wdt_exit); - -MODULE_AUTHOR("Florian Schirmer "); -MODULE_DESCRIPTION("MPC8xx watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/mpcore_wdt.c b/drivers/char/watchdog/mpcore_wdt.c deleted file mode 100644 index 0d2b277..0000000 --- a/drivers/char/watchdog/mpcore_wdt.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Watchdog driver for the mpcore watchdog timer - * - * (c) Copyright 2004 ARM Limited - * - * Based on the SoftDog driver: - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -struct mpcore_wdt { - unsigned long timer_alive; - struct device *dev; - void __iomem *base; - int irq; - unsigned int perturb; - char expect_close; -}; - -static struct platform_device *mpcore_wdt_dev; - -extern unsigned int mpcore_timer_rate; - -#define TIMER_MARGIN 60 -static int mpcore_margin = TIMER_MARGIN; -module_param(mpcore_margin, int, 0); -MODULE_PARM_DESC(mpcore_margin, "MPcore timer margin in seconds. (0base + TWD_WDOG_INTSTAT)) { - dev_printk(KERN_CRIT, wdt->dev, "Triggered - Reboot ignored.\n"); - - /* Clear the interrupt on the watchdog */ - writel(1, wdt->base + TWD_WDOG_INTSTAT); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -/* - * mpcore_wdt_keepalive - reload the timer - * - * Note that the spec says a DIFFERENT value must be written to the reload - * register each time. The "perturb" variable deals with this by adding 1 - * to the count every other time the function is called. - */ -static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt) -{ - unsigned int count; - - /* Assume prescale is set to 256 */ - count = (mpcore_timer_rate / 256) * mpcore_margin; - - /* Reload the counter */ - writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD); - - wdt->perturb = wdt->perturb ? 0 : 1; -} - -static void mpcore_wdt_stop(struct mpcore_wdt *wdt) -{ - writel(0x12345678, wdt->base + TWD_WDOG_DISABLE); - writel(0x87654321, wdt->base + TWD_WDOG_DISABLE); - writel(0x0, wdt->base + TWD_WDOG_CONTROL); -} - -static void mpcore_wdt_start(struct mpcore_wdt *wdt) -{ - dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n"); - - /* This loads the count register but does NOT start the count yet */ - mpcore_wdt_keepalive(wdt); - - if (mpcore_noboot) { - /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */ - writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL); - } else { - /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */ - writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL); - } -} - -static int mpcore_wdt_set_heartbeat(int t) -{ - if (t < 0x0001 || t > 0xFFFF) - return -EINVAL; - - mpcore_margin = t; - return 0; -} - -/* - * /dev/watchdog handling - */ -static int mpcore_wdt_open(struct inode *inode, struct file *file) -{ - struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev); - - if (test_and_set_bit(0, &wdt->timer_alive)) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - file->private_data = wdt; - - /* - * Activate timer - */ - mpcore_wdt_start(wdt); - - return nonseekable_open(inode, file); -} - -static int mpcore_wdt_release(struct inode *inode, struct file *file) -{ - struct mpcore_wdt *wdt = file->private_data; - - /* - * Shut off the timer. - * Lock it in if it's a module and we set nowayout - */ - if (wdt->expect_close == 42) { - mpcore_wdt_stop(wdt); - } else { - dev_printk(KERN_CRIT, wdt->dev, "unexpected close, not stopping watchdog!\n"); - mpcore_wdt_keepalive(wdt); - } - clear_bit(0, &wdt->timer_alive); - wdt->expect_close = 0; - return 0; -} - -static ssize_t mpcore_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - struct mpcore_wdt *wdt = file->private_data; - - /* - * Refresh the timer. - */ - if (len) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - wdt->expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - wdt->expect_close = 42; - } - } - mpcore_wdt_keepalive(wdt); - } - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .identity = "MPcore Watchdog", -}; - -static int mpcore_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct mpcore_wdt *wdt = file->private_data; - int ret; - union { - struct watchdog_info ident; - int i; - } uarg; - - if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg)) - return -ENOTTY; - - if (_IOC_DIR(cmd) & _IOC_WRITE) { - ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd)); - if (ret) - return -EFAULT; - } - - switch (cmd) { - case WDIOC_GETSUPPORT: - uarg.ident = ident; - ret = 0; - break; - - case WDIOC_SETOPTIONS: - ret = -EINVAL; - if (uarg.i & WDIOS_DISABLECARD) { - mpcore_wdt_stop(wdt); - ret = 0; - } - if (uarg.i & WDIOS_ENABLECARD) { - mpcore_wdt_start(wdt); - ret = 0; - } - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - uarg.i = 0; - ret = 0; - break; - - case WDIOC_KEEPALIVE: - mpcore_wdt_keepalive(wdt); - ret = 0; - break; - - case WDIOC_SETTIMEOUT: - ret = mpcore_wdt_set_heartbeat(uarg.i); - if (ret) - break; - - mpcore_wdt_keepalive(wdt); - /* Fall */ - case WDIOC_GETTIMEOUT: - uarg.i = mpcore_margin; - ret = 0; - break; - - default: - return -ENOTTY; - } - - if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { - ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd)); - if (ret) - ret = -EFAULT; - } - return ret; -} - -/* - * System shutdown handler. Turn off the watchdog if we're - * restarting or halting the system. - */ -static void mpcore_wdt_shutdown(struct platform_device *dev) -{ - struct mpcore_wdt *wdt = platform_get_drvdata(dev); - - if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT) - mpcore_wdt_stop(wdt); -} - -/* - * Kernel Interfaces - */ -static const struct file_operations mpcore_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = mpcore_wdt_write, - .ioctl = mpcore_wdt_ioctl, - .open = mpcore_wdt_open, - .release = mpcore_wdt_release, -}; - -static struct miscdevice mpcore_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mpcore_wdt_fops, -}; - -static int __devinit mpcore_wdt_probe(struct platform_device *dev) -{ - struct mpcore_wdt *wdt; - struct resource *res; - int ret; - - /* We only accept one device, and it must have an id of -1 */ - if (dev->id != -1) - return -ENODEV; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto err_out; - } - - wdt = kzalloc(sizeof(struct mpcore_wdt), GFP_KERNEL); - if (!wdt) { - ret = -ENOMEM; - goto err_out; - } - - wdt->dev = &dev->dev; - wdt->irq = platform_get_irq(dev, 0); - if (wdt->irq < 0) { - ret = -ENXIO; - goto err_free; - } - wdt->base = ioremap(res->start, res->end - res->start + 1); - if (!wdt->base) { - ret = -ENOMEM; - goto err_free; - } - - mpcore_wdt_miscdev.parent = &dev->dev; - ret = misc_register(&mpcore_wdt_miscdev); - if (ret) { - dev_printk(KERN_ERR, _dev, "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto err_misc; - } - - ret = request_irq(wdt->irq, mpcore_wdt_fire, IRQF_DISABLED, "mpcore_wdt", wdt); - if (ret) { - dev_printk(KERN_ERR, _dev, "cannot register IRQ%d for watchdog\n", wdt->irq); - goto err_irq; - } - - mpcore_wdt_stop(wdt); - platform_set_drvdata(&dev->dev, wdt); - mpcore_wdt_dev = dev; - - return 0; - - err_irq: - misc_deregister(&mpcore_wdt_miscdev); - err_misc: - iounmap(wdt->base); - err_free: - kfree(wdt); - err_out: - return ret; -} - -static int __devexit mpcore_wdt_remove(struct platform_device *dev) -{ - struct mpcore_wdt *wdt = platform_get_drvdata(dev); - - platform_set_drvdata(dev, NULL); - - misc_deregister(&mpcore_wdt_miscdev); - - mpcore_wdt_dev = NULL; - - free_irq(wdt->irq, wdt); - iounmap(wdt->base); - kfree(wdt); - return 0; -} - -static struct platform_driver mpcore_wdt_driver = { - .probe = mpcore_wdt_probe, - .remove = __devexit_p(mpcore_wdt_remove), - .shutdown = mpcore_wdt_shutdown, - .driver = { - .owner = THIS_MODULE, - .name = "mpcore_wdt", - }, -}; - -static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n"; - -static int __init mpcore_wdt_init(void) -{ - /* - * Check that the margin value is within it's range; - * if not reset to the default - */ - if (mpcore_wdt_set_heartbeat(mpcore_margin)) { - mpcore_wdt_set_heartbeat(TIMER_MARGIN); - printk(KERN_INFO "mpcore_margin value must be 0, All Rights Reserved. - * http://www.4g-systems.biz - * - * (C) Copyright 2007 OpenWrt.org, Florian Fainelli - * - * 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 Michael Stickel nor 4G Systems admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 2005 4G Systems - * - * Release 0.01. - * Author: Michael Stickel michael.stickel@4g-systems.biz - * - * Release 0.02. - * Author: Florian Fainelli florian@openwrt.org - * use the Linux watchdog/timer APIs - * - * The Watchdog is configured to reset the MTX-1 - * if it is not triggered for 100 seconds. - * It should not be triggered more often than 1.6 seconds. - * - * A timer triggers the watchdog every 5 seconds, until - * it is opened for the first time. After the first open - * it MUST be triggered every 2..95 seconds. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MTX1_WDT_INTERVAL (5 * HZ) - -static int ticks = 100 * HZ; - -static struct { - struct completion stop; - volatile int running; - struct timer_list timer; - volatile int queue; - int default_ticks; - unsigned long inuse; -} mtx1_wdt_device; - -static void mtx1_wdt_trigger(unsigned long unused) -{ - u32 tmp; - - if (mtx1_wdt_device.running) - ticks--; - /* - * toggle GPIO2_15 - */ - tmp = au_readl(GPIO2_DIR); - tmp = (tmp & ~(1<<15)) | ((~tmp) & (1<<15)); - au_writel (tmp, GPIO2_DIR); - - if (mtx1_wdt_device.queue && ticks) - mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); - else { - complete(&mtx1_wdt_device.stop); - } -} - -static void mtx1_wdt_reset(void) -{ - ticks = mtx1_wdt_device.default_ticks; -} - - -static void mtx1_wdt_start(void) -{ - if (!mtx1_wdt_device.queue) { - mtx1_wdt_device.queue = 1; - au_writel (au_readl(GPIO2_DIR) | (u32)(1<<15), GPIO2_DIR); - mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); - } - mtx1_wdt_device.running++; -} - -static int mtx1_wdt_stop(void) -{ - if (mtx1_wdt_device.queue) { - mtx1_wdt_device.queue = 0; - au_writel (au_readl(GPIO2_DIR) & ~((u32)(1<<15)), GPIO2_DIR); - } - - ticks = mtx1_wdt_device.default_ticks; - - return 0; -} - -/* Filesystem functions */ - -static int mtx1_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &mtx1_wdt_device.inuse)) - return -EBUSY; - - return nonseekable_open(inode, file); -} - - -static int mtx1_wdt_release(struct inode *inode, struct file *file) -{ - clear_bit(0, &mtx1_wdt_device.inuse); - return 0; -} - -static int mtx1_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 = "MTX-1 WDT", - }; - - switch(cmd) { - case WDIOC_KEEPALIVE: - mtx1_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: - mtx1_wdt_start(); - break; - case WDIOS_DISABLECARD: - return mtx1_wdt_stop(); - default: - return -EINVAL; - } - break; - default: - return -ENOTTY; - } - return 0; -} - - -static ssize_t mtx1_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - if (!count) - return -EIO; - - mtx1_wdt_reset(); - return count; -} - -static struct file_operations mtx1_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = mtx1_wdt_ioctl, - .open = mtx1_wdt_open, - .write = mtx1_wdt_write, - .release = mtx1_wdt_release -}; - - -static struct miscdevice mtx1_wdt_misc = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mtx1_wdt_fops -}; - - -static int __init mtx1_wdt_init(void) -{ - int ret; - - if ((ret = misc_register(&mtx1_wdt_misc)) < 0) { - printk(KERN_ERR " mtx-1_wdt : failed to register\n"); - return ret; - } - - init_completion(&mtx1_wdt_device.stop); - mtx1_wdt_device.queue = 0; - - clear_bit(0, &mtx1_wdt_device.inuse); - - setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L); - - mtx1_wdt_device.default_ticks = ticks; - - mtx1_wdt_start(); - - printk(KERN_INFO "MTX-1 Watchdog driver\n"); - - return 0; -} - -static void __exit mtx1_wdt_exit(void) -{ - if (mtx1_wdt_device.queue) { - mtx1_wdt_device.queue = 0; - wait_for_completion(&mtx1_wdt_device.stop); - } - misc_deregister(&mtx1_wdt_misc); -} - -module_init(mtx1_wdt_init); -module_exit(mtx1_wdt_exit); - -MODULE_AUTHOR("Michael Stickel, Florian Fainelli"); -MODULE_DESCRIPTION("Driver for the MTX-1 watchdog"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/watchdog/mv64x60_wdt.c b/drivers/char/watchdog/mv64x60_wdt.c deleted file mode 100644 index 0365c31..0000000 --- a/drivers/char/watchdog/mv64x60_wdt.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface - * - * Author: James Chapman - * - * Platform-specific setup code should configure the dog to generate - * interrupt or reset as required. This code only enables/disables - * and services the watchdog. - * - * Derived from mpc8xx_wdt.c, with the following copyright. - * - * 2002 (c) Florian Schirmer This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MV64x60_WDT_WDC_OFFSET 0 - -/* - * The watchdog configuration register contains a pair of 2-bit fields, - * 1. a reload field, bits 27-26, which triggers a reload of - * the countdown register, and - * 2. an enable field, bits 25-24, which toggles between - * enabling and disabling the watchdog timer. - * Bit 31 is a read-only field which indicates whether the - * watchdog timer is currently enabled. - * - * The low 24 bits contain the timer reload value. - */ -#define MV64x60_WDC_ENABLE_SHIFT 24 -#define MV64x60_WDC_SERVICE_SHIFT 26 -#define MV64x60_WDC_ENABLED_SHIFT 31 - -#define MV64x60_WDC_ENABLED_TRUE 1 -#define MV64x60_WDC_ENABLED_FALSE 0 - -/* Flags bits */ -#define MV64x60_WDOG_FLAG_OPENED 0 - -static unsigned long wdt_flags; -static int wdt_status; -static void __iomem *mv64x60_wdt_regs; -static int mv64x60_wdt_timeout; -static int mv64x60_wdt_count; -static unsigned int bus_clk; -static char expect_close; -static DEFINE_SPINLOCK(mv64x60_wdt_spinlock); - -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 mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift) -{ - u32 data; - u32 enabled; - int ret = 0; - - spin_lock(&mv64x60_wdt_spinlock); - data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); - enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1; - - /* only toggle the requested field if enabled state matches predicate */ - if ((enabled ^ enabled_predicate) == 0) { - /* We write a 1, then a 2 -- to the appropriate field */ - data = (1 << field_shift) | mv64x60_wdt_count; - writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); - - data = (2 << field_shift) | mv64x60_wdt_count; - writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); - ret = 1; - } - spin_unlock(&mv64x60_wdt_spinlock); - - return ret; -} - -static void mv64x60_wdt_service(void) -{ - mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE, - MV64x60_WDC_SERVICE_SHIFT); -} - -static void mv64x60_wdt_handler_enable(void) -{ - if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE, - MV64x60_WDC_ENABLE_SHIFT)) { - mv64x60_wdt_service(); - printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n"); - } -} - -static void mv64x60_wdt_handler_disable(void) -{ - if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE, - MV64x60_WDC_ENABLE_SHIFT)) - printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n"); -} - -static void mv64x60_wdt_set_timeout(unsigned int timeout) -{ - /* maximum bus cycle count is 0xFFFFFFFF */ - if (timeout > 0xFFFFFFFF / bus_clk) - timeout = 0xFFFFFFFF / bus_clk; - - mv64x60_wdt_count = timeout * bus_clk >> 8; - mv64x60_wdt_timeout = timeout; -} - -static int mv64x60_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags)) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - mv64x60_wdt_handler_enable(); - - return nonseekable_open(inode, file); -} - -static int mv64x60_wdt_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) - mv64x60_wdt_handler_disable(); - else { - printk(KERN_CRIT - "mv64x60_wdt: unexpected close, not stopping timer!\n"); - mv64x60_wdt_service(); - } - expect_close = 0; - - clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags); - - return 0; -} - -static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t * ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - if(get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - mv64x60_wdt_service(); - } - - return len; -} - -static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int timeout; - int options; - void __user *argp = (void __user *)arg; - static struct watchdog_info info = { - .options = WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE | - WDIOF_KEEPALIVEPING, - .firmware_version = 0, - .identity = "MV64x60 watchdog", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &info, sizeof(info))) - return -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - if (put_user(wdt_status, (int __user *)argp)) - return -EFAULT; - wdt_status &= ~WDIOF_KEEPALIVEPING; - break; - - case WDIOC_GETTEMP: - return -EOPNOTSUPP; - - case WDIOC_SETOPTIONS: - if (get_user(options, (int __user *)argp)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) - mv64x60_wdt_handler_disable(); - - if (options & WDIOS_ENABLECARD) - mv64x60_wdt_handler_enable(); - break; - - case WDIOC_KEEPALIVE: - mv64x60_wdt_service(); - wdt_status |= WDIOF_KEEPALIVEPING; - break; - - case WDIOC_SETTIMEOUT: - if (get_user(timeout, (int __user *)argp)) - return -EFAULT; - mv64x60_wdt_set_timeout(timeout); - /* Fall through */ - - case WDIOC_GETTIMEOUT: - if (put_user(mv64x60_wdt_timeout, (int __user *)argp)) - return -EFAULT; - break; - - default: - return -ENOTTY; - } - - return 0; -} - -static const struct file_operations mv64x60_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = mv64x60_wdt_write, - .ioctl = mv64x60_wdt_ioctl, - .open = mv64x60_wdt_open, - .release = mv64x60_wdt_release, -}; - -static struct miscdevice mv64x60_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &mv64x60_wdt_fops, -}; - -static int __devinit mv64x60_wdt_probe(struct platform_device *dev) -{ - struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data; - struct resource *r; - int timeout = 10; - - bus_clk = 133; /* in MHz */ - if (pdata) { - timeout = pdata->timeout; - bus_clk = pdata->bus_clk; - } - - /* Since bus_clk is truncated MHz, actual frequency could be - * up to 1MHz higher. Round up, since it's better to time out - * too late than too soon. - */ - bus_clk++; - bus_clk *= 1000000; /* convert to Hz */ - - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!r) - return -ENODEV; - - mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1); - if (mv64x60_wdt_regs == NULL) - return -ENOMEM; - - mv64x60_wdt_set_timeout(timeout); - - mv64x60_wdt_handler_disable(); /* in case timer was already running */ - - return misc_register(&mv64x60_wdt_miscdev); -} - -static int __devexit mv64x60_wdt_remove(struct platform_device *dev) -{ - misc_deregister(&mv64x60_wdt_miscdev); - - mv64x60_wdt_handler_disable(); - - iounmap(mv64x60_wdt_regs); - - return 0; -} - -static struct platform_driver mv64x60_wdt_driver = { - .probe = mv64x60_wdt_probe, - .remove = __devexit_p(mv64x60_wdt_remove), - .driver = { - .owner = THIS_MODULE, - .name = MV64x60_WDT_NAME, - }, -}; - -static int __init mv64x60_wdt_init(void) -{ - printk(KERN_INFO "MV64x60 watchdog driver\n"); - - return platform_driver_register(&mv64x60_wdt_driver); -} - -static void __exit mv64x60_wdt_exit(void) -{ - platform_driver_unregister(&mv64x60_wdt_driver); -} - -module_init(mv64x60_wdt_init); -module_exit(mv64x60_wdt_exit); - -MODULE_AUTHOR("James Chapman "); -MODULE_DESCRIPTION("MV64x60 watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c deleted file mode 100644 index 719b066..0000000 --- a/drivers/char/watchdog/omap_wdt.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * linux/drivers/char/watchdog/omap_wdt.c - * - * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog - * - * Author: MontaVista Software, Inc. - * or - * - * 2003 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is - * licensed "as is" without any warranty of any kind, whether express - * or implied. - * - * History: - * - * 20030527: George G. Davis - * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c - * (c) Copyright 2000 Oleg Drokin - * Based on SoftDog driver by Alan Cox - * - * Copyright (c) 2004 Texas Instruments. - * 1. Modified to support OMAP1610 32-KHz watchdog timer - * 2. Ported to 2.6 kernel - * - * Copyright (c) 2005 David Brownell - * Use the driver model and standard identifiers; handle bigger timeouts. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "omap_wdt.h" - -static unsigned timer_margin; -module_param(timer_margin, uint, 0); -MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); - -static int omap_wdt_users; -static struct clk *armwdt_ck = NULL; -static struct clk *mpu_wdt_ick = NULL; -static struct clk *mpu_wdt_fck = NULL; - -static unsigned int wdt_trgr_pattern = 0x1234; - -static void omap_wdt_ping(void) -{ - /* wait for posted write to complete */ - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) - cpu_relax(); - wdt_trgr_pattern = ~wdt_trgr_pattern; - omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); - /* wait for posted write to complete */ - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) - cpu_relax(); - /* reloaded WCRR from WLDR */ -} - -static void omap_wdt_enable(void) -{ - /* Sequence to enable the watchdog */ - omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) - cpu_relax(); - omap_writel(0x4444, OMAP_WATCHDOG_SPR); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) - cpu_relax(); -} - -static void omap_wdt_disable(void) -{ - /* sequence required to disable watchdog */ - omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) - cpu_relax(); - omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) - cpu_relax(); -} - -static void omap_wdt_adjust_timeout(unsigned new_timeout) -{ - if (new_timeout < TIMER_MARGIN_MIN) - new_timeout = TIMER_MARGIN_DEFAULT; - if (new_timeout > TIMER_MARGIN_MAX) - new_timeout = TIMER_MARGIN_MAX; - timer_margin = new_timeout; -} - -static void omap_wdt_set_timeout(void) -{ - u32 pre_margin = GET_WLDR_VAL(timer_margin); - - /* just count up at 32 KHz */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) - cpu_relax(); - omap_writel(pre_margin, OMAP_WATCHDOG_LDR); - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) - cpu_relax(); -} - -/* - * Allow only one task to hold it open - */ - -static int omap_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) - return -EBUSY; - - if (cpu_is_omap16xx()) - clk_enable(armwdt_ck); /* Enable the clock */ - - if (cpu_is_omap24xx()) { - clk_enable(mpu_wdt_ick); /* Enable the interface clock */ - clk_enable(mpu_wdt_fck); /* Enable the functional clock */ - } - - /* initialize prescaler */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) - cpu_relax(); - omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) - cpu_relax(); - - omap_wdt_set_timeout(); - omap_wdt_enable(); - return nonseekable_open(inode, file); -} - -static int omap_wdt_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer unless NOWAYOUT is defined. - */ -#ifndef CONFIG_WATCHDOG_NOWAYOUT - omap_wdt_disable(); - - if (cpu_is_omap16xx()) { - clk_disable(armwdt_ck); /* Disable the clock */ - clk_put(armwdt_ck); - armwdt_ck = NULL; - } - - if (cpu_is_omap24xx()) { - clk_disable(mpu_wdt_ick); /* Disable the clock */ - clk_disable(mpu_wdt_fck); /* Disable the clock */ - clk_put(mpu_wdt_ick); - clk_put(mpu_wdt_fck); - mpu_wdt_ick = NULL; - mpu_wdt_fck = NULL; - } -#else - printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); -#endif - omap_wdt_users = 0; - return 0; -} - -static ssize_t -omap_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* Refresh LOAD_TIME. */ - if (len) - omap_wdt_ping(); - return len; -} - -static int -omap_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int new_margin; - static struct watchdog_info ident = { - .identity = "OMAP Watchdog", - .options = WDIOF_SETTIMEOUT, - .firmware_version = 0, - }; - - switch (cmd) { - default: - return -ENOTTY; - case WDIOC_GETSUPPORT: - return copy_to_user((struct watchdog_info __user *)arg, &ident, - sizeof(ident)); - case WDIOC_GETSTATUS: - return put_user(0, (int __user *)arg); - case WDIOC_GETBOOTSTATUS: - if (cpu_is_omap16xx()) - return put_user(omap_readw(ARM_SYSST), - (int __user *)arg); - if (cpu_is_omap24xx()) - return put_user(omap_prcm_get_reset_sources(), - (int __user *)arg); - case WDIOC_KEEPALIVE: - omap_wdt_ping(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, (int __user *)arg)) - return -EFAULT; - omap_wdt_adjust_timeout(new_margin); - - omap_wdt_disable(); - omap_wdt_set_timeout(); - omap_wdt_enable(); - - omap_wdt_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(timer_margin, (int __user *)arg); - } -} - -static const struct file_operations omap_wdt_fops = { - .owner = THIS_MODULE, - .write = omap_wdt_write, - .ioctl = omap_wdt_ioctl, - .open = omap_wdt_open, - .release = omap_wdt_release, -}; - -static struct miscdevice omap_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &omap_wdt_fops -}; - -static int __init omap_wdt_probe(struct platform_device *pdev) -{ - struct resource *res, *mem; - int ret; - - /* reserve static register mappings */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - - mem = request_mem_region(res->start, res->end - res->start + 1, - pdev->name); - if (mem == NULL) - return -EBUSY; - - platform_set_drvdata(pdev, mem); - - omap_wdt_users = 0; - - if (cpu_is_omap16xx()) { - armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); - if (IS_ERR(armwdt_ck)) { - ret = PTR_ERR(armwdt_ck); - armwdt_ck = NULL; - goto fail; - } - } - - if (cpu_is_omap24xx()) { - mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); - if (IS_ERR(mpu_wdt_ick)) { - ret = PTR_ERR(mpu_wdt_ick); - mpu_wdt_ick = NULL; - goto fail; - } - mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); - if (IS_ERR(mpu_wdt_fck)) { - ret = PTR_ERR(mpu_wdt_fck); - mpu_wdt_fck = NULL; - goto fail; - } - } - - omap_wdt_disable(); - omap_wdt_adjust_timeout(timer_margin); - - omap_wdt_miscdev.parent = &pdev->dev; - ret = misc_register(&omap_wdt_miscdev); - if (ret) - goto fail; - - pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); - - /* autogate OCP interface clock */ - omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); - return 0; - -fail: - if (armwdt_ck) - clk_put(armwdt_ck); - if (mpu_wdt_ick) - clk_put(mpu_wdt_ick); - if (mpu_wdt_fck) - clk_put(mpu_wdt_fck); - release_resource(mem); - return ret; -} - -static void omap_wdt_shutdown(struct platform_device *pdev) -{ - omap_wdt_disable(); -} - -static int omap_wdt_remove(struct platform_device *pdev) -{ - struct resource *mem = platform_get_drvdata(pdev); - misc_deregister(&omap_wdt_miscdev); - release_resource(mem); - if (armwdt_ck) - clk_put(armwdt_ck); - if (mpu_wdt_ick) - clk_put(mpu_wdt_ick); - if (mpu_wdt_fck) - clk_put(mpu_wdt_fck); - return 0; -} - -#ifdef CONFIG_PM - -/* REVISIT ... not clear this is the best way to handle system suspend; and - * it's very inappropriate for selective device suspend (e.g. suspending this - * through sysfs rather than by stopping the watchdog daemon). Also, this - * may not play well enough with NOWAYOUT... - */ - -static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) -{ - if (omap_wdt_users) - omap_wdt_disable(); - return 0; -} - -static int omap_wdt_resume(struct platform_device *pdev) -{ - if (omap_wdt_users) { - omap_wdt_enable(); - omap_wdt_ping(); - } - return 0; -} - -#else -#define omap_wdt_suspend NULL -#define omap_wdt_resume NULL -#endif - -static struct platform_driver omap_wdt_driver = { - .probe = omap_wdt_probe, - .remove = omap_wdt_remove, - .shutdown = omap_wdt_shutdown, - .suspend = omap_wdt_suspend, - .resume = omap_wdt_resume, - .driver = { - .owner = THIS_MODULE, - .name = "omap_wdt", - }, -}; - -static int __init omap_wdt_init(void) -{ - return platform_driver_register(&omap_wdt_driver); -} - -static void __exit omap_wdt_exit(void) -{ - platform_driver_unregister(&omap_wdt_driver); -} - -module_init(omap_wdt_init); -module_exit(omap_wdt_exit); - -MODULE_AUTHOR("George G. Davis"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h deleted file mode 100644 index 52a532a5..0000000 --- a/drivers/char/watchdog/omap_wdt.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * linux/drivers/char/watchdog/omap_wdt.h - * - * BRIEF MODULE DESCRIPTION - * OMAP Watchdog timer register definitions - * - * Copyright (C) 2004 Texas Instruments. - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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. - */ - -#ifndef _OMAP_WATCHDOG_H -#define _OMAP_WATCHDOG_H - -#define OMAP1610_WATCHDOG_BASE 0xfffeb000 -#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ - -#ifdef CONFIG_ARCH_OMAP24XX -#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE -#else -#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE -#define RM_RSTST_WKUP 0 -#endif - -#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) -#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) -#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) -#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) -#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) -#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) -#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) -#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) -#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) - -/* Using the prescaler, the OMAP watchdog could go for many - * months before firing. These limits work without scaling, - * with the 60 second default assumed by most tools and docs. - */ -#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ -#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ -#define TIMER_MARGIN_MIN 1 - -#define PTV 0 /* prescale */ -#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1< - * and Marcus Junker, - * - * 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 Sven Anders, 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. - * - * Release 1.1 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* #define DEBUG 1 */ - -#define DEFAULT_TIMEOUT 1 /* 1 minute */ -#define MAX_TIMEOUT 255 - -#define VERSION "1.1" -#define MODNAME "pc87413 WDT" -#define PFX MODNAME ": " -#define DPFX MODNAME " - DEBUG: " - -#define WDT_INDEX_IO_PORT (io+0) /* I/O port base (index register) */ -#define WDT_DATA_IO_PORT (WDT_INDEX_IO_PORT+1) -#define SWC_LDN 0x04 -#define SIOCFG2 0x22 /* Serial IO register */ -#define WDCTL 0x10 /* Watchdog-Timer-Controll-Register */ -#define WDTO 0x11 /* Watchdog timeout register */ -#define WDCFG 0x12 /* Watchdog config register */ - -static int io = 0x2E; /* Address used on Portwell Boards */ - -static int timeout = DEFAULT_TIMEOUT; /* timeout value */ -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 ----------------------------------------*/ - -/* Select pins for Watchdog output */ - -static inline void pc87413_select_wdt_out (void) -{ - unsigned int cr_data = 0; - - /* Step 1: Select multiple pin,pin55,as WDT output */ - - outb_p(SIOCFG2, WDT_INDEX_IO_PORT); - - cr_data = inb (WDT_DATA_IO_PORT); - - cr_data |= 0x80; /* Set Bit7 to 1*/ - outb_p(SIOCFG2, WDT_INDEX_IO_PORT); - - outb_p(cr_data, WDT_DATA_IO_PORT); - -#ifdef DEBUG - printk(KERN_INFO DPFX "Select multiple pin,pin55,as WDT output:" - " Bit7 to 1: %d\n", cr_data); -#endif -} - -/* Enable SWC functions */ - -static inline void pc87413_enable_swc(void) -{ - unsigned int cr_data=0; - - /* Step 2: Enable SWC functions */ - - outb_p(0x07, WDT_INDEX_IO_PORT); /* Point SWC_LDN (LDN=4) */ - outb_p(SWC_LDN, WDT_DATA_IO_PORT); - - outb_p(0x30, WDT_INDEX_IO_PORT); /* Read Index 0x30 First */ - cr_data = inb(WDT_DATA_IO_PORT); - cr_data |= 0x01; /* Set Bit0 to 1 */ - outb_p(0x30, WDT_INDEX_IO_PORT); - outb_p(cr_data, WDT_DATA_IO_PORT); /* Index0x30_bit0P1 */ - -#ifdef DEBUG - printk(KERN_INFO DPFX "pc87413 - Enable SWC functions\n"); -#endif -} - -/* Read SWC I/O base address */ - -static inline unsigned int pc87413_get_swc_base(void) -{ - unsigned int swc_base_addr = 0; - unsigned char addr_l, addr_h = 0; - - /* Step 3: Read SWC I/O Base Address */ - - outb_p(0x60, WDT_INDEX_IO_PORT); /* Read Index 0x60 */ - addr_h = inb(WDT_DATA_IO_PORT); - - outb_p(0x61, WDT_INDEX_IO_PORT); /* Read Index 0x61 */ - - addr_l = inb(WDT_DATA_IO_PORT); - - swc_base_addr = (addr_h << 8) + addr_l; - -#ifdef DEBUG - printk(KERN_INFO DPFX "Read SWC I/O Base Address: low %d, high %d," - " res %d\n", addr_l, addr_h, swc_base_addr); -#endif - - return swc_base_addr; -} - -/* Select Bank 3 of SWC */ - -static inline void pc87413_swc_bank3(unsigned int swc_base_addr) -{ - /* Step 4: Select Bank3 of SWC */ - - outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); - -#ifdef DEBUG - printk(KERN_INFO DPFX "Select Bank3 of SWC\n"); -#endif -} - -/* Set watchdog timeout to x minutes */ - -static inline void pc87413_programm_wdto(unsigned int swc_base_addr, - char pc87413_time) -{ - /* Step 5: Programm WDTO, Twd. */ - - outb_p(pc87413_time, swc_base_addr + WDTO); - -#ifdef DEBUG - printk(KERN_INFO DPFX "Set WDTO to %d minutes\n", pc87413_time); -#endif -} - -/* Enable WDEN */ - -static inline void pc87413_enable_wden(unsigned int swc_base_addr) -{ - /* Step 6: Enable WDEN */ - - outb_p(inb (swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); - -#ifdef DEBUG - printk(KERN_INFO DPFX "Enable WDEN\n"); -#endif -} - -/* Enable SW_WD_TREN */ -static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) -{ - /* Enable SW_WD_TREN */ - - outb_p(inb (swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); - -#ifdef DEBUG - printk(KERN_INFO DPFX "Enable SW_WD_TREN\n"); -#endif -} - -/* Disable SW_WD_TREN */ - -static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) -{ - /* Disable SW_WD_TREN */ - - outb_p(inb (swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); - -#ifdef DEBUG - printk(KERN_INFO DPFX "pc87413 - Disable SW_WD_TREN\n"); -#endif -} - -/* Enable SW_WD_TRG */ - -static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) -{ - /* Enable SW_WD_TRG */ - - outb_p(inb (swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); - -#ifdef DEBUG - printk(KERN_INFO DPFX "pc87413 - Enable SW_WD_TRG\n"); -#endif -} - -/* Disable SW_WD_TRG */ - -static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) -{ - /* Disable SW_WD_TRG */ - - outb_p(inb (swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); - -#ifdef DEBUG - printk(KERN_INFO DPFX "Disable SW_WD_TRG\n"); -#endif -} - -/* -- Higher level functions ------------------------------------*/ - -/* Enable the watchdog */ - -static void pc87413_enable(void) -{ - unsigned int swc_base_addr; - - spin_lock(&io_lock); - - pc87413_select_wdt_out(); - pc87413_enable_swc(); - swc_base_addr = pc87413_get_swc_base(); - pc87413_swc_bank3(swc_base_addr); - pc87413_programm_wdto(swc_base_addr, timeout); - pc87413_enable_wden(swc_base_addr); - pc87413_enable_sw_wd_tren(swc_base_addr); - pc87413_enable_sw_wd_trg(swc_base_addr); - - spin_unlock(&io_lock); -} - -/* Disable the watchdog */ - -static void pc87413_disable(void) -{ - unsigned int swc_base_addr; - - spin_lock(&io_lock); - - pc87413_select_wdt_out(); - pc87413_enable_swc(); - swc_base_addr = pc87413_get_swc_base(); - pc87413_swc_bank3(swc_base_addr); - pc87413_disable_sw_wd_tren(swc_base_addr); - pc87413_disable_sw_wd_trg(swc_base_addr); - pc87413_programm_wdto(swc_base_addr, 0); - - spin_unlock(&io_lock); -} - -/* Refresh the watchdog */ - -static void pc87413_refresh(void) -{ - unsigned int swc_base_addr; - - spin_lock(&io_lock); - - pc87413_select_wdt_out(); - pc87413_enable_swc(); - swc_base_addr = pc87413_get_swc_base(); - pc87413_swc_bank3(swc_base_addr); - pc87413_disable_sw_wd_tren(swc_base_addr); - pc87413_disable_sw_wd_trg(swc_base_addr); - pc87413_programm_wdto(swc_base_addr, timeout); - pc87413_enable_wden(swc_base_addr); - pc87413_enable_sw_wd_tren(swc_base_addr); - pc87413_enable_sw_wd_trg(swc_base_addr); - - spin_unlock(&io_lock); -} - -/* -- File operations -------------------------------------------*/ - -/** - * pc87413_open: - * @inode: inode of device - * @file: file handle to device - * - */ - -static int pc87413_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 */ - pc87413_refresh(); - - printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to" - " %d minute(s).\n", timeout); - - return nonseekable_open(inode, file); -} - -/** - * pc87413_release: - * @inode: inode to board - * @file: file handle to board - * - * The watchdog has a configurable API. There is a religious dispute - * between people who want their watchdog to be able to shut down and - * those who want to be sure if the watchdog manager dies the machine - * reboots. In the former case we disable the counters, in the latter - * case you have to open it again very soon. - */ - -static int pc87413_release(struct inode *inode, struct file *file) -{ - /* Shut off the timer. */ - - if (expect_close == 42) { - pc87413_disable(); - printk(KERN_INFO MODNAME "Watchdog disabled," - " sleeping again...\n"); - } else { - printk(KERN_CRIT MODNAME "Unexpected close, not stopping" - " watchdog!\n"); - pc87413_refresh(); - } - - clear_bit(0, &timer_enabled); - expect_close = 0; - - return 0; -} - -/** - * pc87413_status: - * - * return, if the watchdog is enabled (timeout is set...) - */ - - -static int pc87413_status(void) -{ - return 0; /* currently not supported */ -} - -/** - * pc87413_write: - * @file: file handle to the watchdog - * @data: data buffer to write - * @len: length in bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t pc87413_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 */ - pc87413_refresh(); - } - return len; -} - -/** - * pc87413_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. We only actually usefully support - * querying capabilities and current status. - */ - -static int pc87413_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 = 1, - .identity = "PC87413(HF/F) 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(pc87413_status(), uarg.i); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, uarg.i); - - case WDIOC_KEEPALIVE: - pc87413_refresh(); -#ifdef DEBUG - printk(KERN_INFO DPFX "keepalive\n"); -#endif - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, uarg.i)) - return -EFAULT; - - // the API states this is given in secs - new_timeout /= 60; - - if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) - return -EINVAL; - - timeout = new_timeout; - pc87413_refresh(); - - // fall through and return the new timeout... - - case WDIOC_GETTIMEOUT: - - new_timeout = 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) { - pc87413_disable(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - pc87413_enable(); - retval = 0; - } - - return retval; - } - } -} - -/* -- Notifier funtions -----------------------------------------*/ - -/** - * notify_sys: - * @this: our notifier block - * @code: the event being reported - * @unused: unused - * - * Our notifier is called on system shutdowns. We want to turn the card - * off at reboot otherwise the machine will reboot again during memory - * test or worse yet during the following fsck. This would suck, in fact - * trust me - if it happens it does suck. - */ - -static int pc87413_notify_sys(struct notifier_block *this, - unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - { - /* Turn the card off */ - pc87413_disable(); - } - return NOTIFY_DONE; -} - -/* -- Module's structures ---------------------------------------*/ - -static const struct file_operations pc87413_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = pc87413_write, - .ioctl = pc87413_ioctl, - .open = pc87413_open, - .release = pc87413_release, -}; - -static struct notifier_block pc87413_notifier = -{ - .notifier_call = pc87413_notify_sys, -}; - -static struct miscdevice pc87413_miscdev= -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &pc87413_fops -}; - -/* -- Module init functions -------------------------------------*/ - -/** - * pc87413_init: module's "constructor" - * - * Set up the WDT watchdog board. All we have to do is grab the - * resources we require and bitch if anyone beat us to them. - * The open() function will actually kick the board off. - */ - -static int __init pc87413_init(void) -{ - int ret; - - spin_lock_init(&io_lock); - - printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", WDT_INDEX_IO_PORT); - - /* request_region(io, 2, "pc87413"); */ - - ret = register_reboot_notifier(&pc87413_notifier); - if (ret != 0) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - ret); - } - - ret = misc_register(&pc87413_miscdev); - - if (ret != 0) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - unregister_reboot_notifier(&pc87413_notifier); - return ret; - } - - printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); - - pc87413_enable(); - - return 0; -} - -/** - * pc87413_exit: module's "destructor" - * - * Unload the watchdog. You cannot do this with any file handles open. - * If your watchdog is set to continue ticking on close and you unload - * it, well it keeps ticking. We won't get the interrupt but the board - * will not touch PC memory so all is fine. You just have to load a new - * module in 60 seconds or reboot. - */ - -static void __exit pc87413_exit(void) -{ - /* Stop the timer before we leave */ - if (!nowayout) - { - pc87413_disable(); - printk(KERN_INFO MODNAME "Watchdog disabled.\n"); - } - - misc_deregister(&pc87413_miscdev); - unregister_reboot_notifier(&pc87413_notifier); - /* release_region(io,2); */ - - printk(MODNAME " watchdog component driver removed.\n"); -} - -module_init(pc87413_init); -module_exit(pc87413_exit); - -MODULE_AUTHOR("Sven Anders , Marcus Junker ,"); -MODULE_DESCRIPTION("PC87413 WDT driver"); -MODULE_LICENSE("GPL"); - -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -module_param(io, int, 0); -MODULE_PARM_DESC(io, MODNAME " I/O port (default: " __MODULE_STRING(io) ")."); - -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes (default=" __MODULE_STRING(timeout) ")."); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c deleted file mode 100644 index 7b41434..0000000 --- a/drivers/char/watchdog/pcwd.c +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * PC Watchdog Driver - * by Ken Hollis (khollis@bitgate.com) - * - * Permission granted from Simon Machell (smachell@berkprod.com) - * Written for the Linux Kernel, and GPLed by Ken Hollis - * - * 960107 Added request_region routines, modulized the whole thing. - * 960108 Fixed end-of-file pointer (Thanks to Dan Hollis), added - * WD_TIMEOUT define. - * 960216 Added eof marker on the file, and changed verbose messages. - * 960716 Made functional and cosmetic changes to the source for - * inclusion in Linux 2.0.x kernels, thanks to Alan Cox. - * 960717 Removed read/seek routines, replaced with ioctl. Also, added - * check_region command due to Alan's suggestion. - * 960821 Made changes to compile in newer 2.0.x kernels. Added - * "cold reboot sense" entry. - * 960825 Made a few changes to code, deleted some defines and made - * typedefs to replace them. Made heartbeat reset only available - * via ioctl, and removed the write routine. - * 960828 Added new items for PC Watchdog Rev.C card. - * 960829 Changed around all of the IOCTLs, added new features, - * added watchdog disable/re-enable routines. Added firmware - * version reporting. Added read routine for temperature. - * Removed some extra defines, added an autodetect Revision - * routine. - * 961006 Revised some documentation, fixed some cosmetic bugs. Made - * drivers to panic the system if it's overheating at bootup. - * 961118 Changed some verbiage on some of the output, tidied up - * code bits, and added compatibility to 2.1.x. - * 970912 Enabled board on open and disable on close. - * 971107 Took account of recent VFS changes (broke read). - * 971210 Disable board on initialisation in case board already ticking. - * 971222 Changed open/close for temperature handling - * Michael Meskes . - * 980112 Used minor numbers from include/linux/miscdevice.h - * 990403 Clear reset status after reading control status register in - * pcwd_showprevstate(). [Marc Boucher ] - * 990605 Made changes to code to support Firmware 1.22a, added - * fairly useless proc entry. - * 990610 removed said useless proc code for the merge - * 000403 Removed last traces of proc code. - * 011214 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * Added timeout module option to override default - */ - -/* - * A bells and whistles driver is available from http://www.pcwd.de/ - * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ - */ - -#include /* For module specific items */ -#include /* For new moduleparam's */ -#include /* For standard types (like size_t) */ -#include /* For the -ENODEV/... values */ -#include /* For printk/panic/... */ -#include /* For mdelay function */ -#include /* For timer related operations */ -#include /* For jiffies stuff */ -#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ -#include /* For the watchdog specific items */ -#include /* For kernel_power_off() */ -#include /* For __init/__exit/... */ -#include /* For file operations */ -#include /* For isa devices */ -#include /* For io-port access */ -#include /* For spin_lock/spin_unlock/... */ - -#include /* For copy_to_user/put_user/... */ -#include /* For inb/outb/... */ - -/* Module and version information */ -#define WATCHDOG_VERSION "1.20" -#define WATCHDOG_DATE "18 Feb 2007" -#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" -#define WATCHDOG_NAME "pcwd" -#define PFX WATCHDOG_NAME ": " -#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " (" WATCHDOG_DATE ")\n" -#define WD_VER WATCHDOG_VERSION " (" WATCHDOG_DATE ")" - -/* - * It should be noted that PCWD_REVISION_B was removed because A and B - * are essentially the same types of card, with the exception that B - * has temperature reporting. Since I didn't receive a Rev.B card, - * the Rev.B card is not supported. (It's a good thing too, as they - * are no longer in production.) - */ -#define PCWD_REVISION_A 1 -#define PCWD_REVISION_C 2 - -/* - * These are the auto-probe addresses available. - * - * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350. - * Revision A has an address range of 2 addresses, while Revision C has 4. - */ -#define PCWD_ISA_NR_CARDS 3 -static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; - -/* - * These are the defines that describe the control status bits for the - * PCI-PC Watchdog card. -*/ -/* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */ -#define WD_WDRST 0x01 /* Previously reset state */ -#define WD_T110 0x02 /* Temperature overheat sense */ -#define WD_HRTBT 0x04 /* Heartbeat sense */ -#define WD_RLY2 0x08 /* External relay triggered */ -#define WD_SRLY2 0x80 /* Software external relay triggered */ -/* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */ -#define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ -#define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ -#define WD_REVC_TTRP 0x04 /* Temperature Trip status */ -#define WD_REVC_RL2A 0x08 /* Relay 2 activated by on-board processor */ -#define WD_REVC_RL1A 0x10 /* Relay 1 active */ -#define WD_REVC_R2DS 0x40 /* Relay 2 disable */ -#define WD_REVC_RLY2 0x80 /* Relay 2 activated? */ -/* Port 2 : Control Status #2 */ -#define WD_WDIS 0x10 /* Watchdog Disabled */ -#define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */ -#define WD_SSEL 0x40 /* Watchdog Switch Select (1:SW1 <-> 0:SW2) */ -#define WD_WCMD 0x80 /* Watchdog Command Mode */ - -/* max. time we give an ISA watchdog card to process a command */ -/* 500ms for each 4 bit response (according to spec.) */ -#define ISA_COMMAND_TIMEOUT 1000 - -/* Watchdog's internal commands */ -#define CMD_ISA_IDLE 0x00 -#define CMD_ISA_VERSION_INTEGER 0x01 -#define CMD_ISA_VERSION_TENTH 0x02 -#define CMD_ISA_VERSION_HUNDRETH 0x03 -#define CMD_ISA_VERSION_MINOR 0x04 -#define CMD_ISA_SWITCH_SETTINGS 0x05 -#define CMD_ISA_RESET_PC 0x06 -#define CMD_ISA_ARM_0 0x07 -#define CMD_ISA_ARM_30 0x08 -#define CMD_ISA_ARM_60 0x09 -#define CMD_ISA_DELAY_TIME_2SECS 0x0A -#define CMD_ISA_DELAY_TIME_4SECS 0x0B -#define CMD_ISA_DELAY_TIME_8SECS 0x0C -#define CMD_ISA_RESET_RELAYS 0x0D - -/* Watchdog's Dip Switch heartbeat values */ -static const int heartbeat_tbl [] = { - 20, /* OFF-OFF-OFF = 20 Sec */ - 40, /* OFF-OFF-ON = 40 Sec */ - 60, /* OFF-ON-OFF = 1 Min */ - 300, /* OFF-ON-ON = 5 Min */ - 600, /* ON-OFF-OFF = 10 Min */ - 1800, /* ON-OFF-ON = 30 Min */ - 3600, /* ON-ON-OFF = 1 Hour */ - 7200, /* ON-ON-ON = 2 hour */ -}; - -/* - * We are using an kernel timer to do the pinging of the watchdog - * every ~500ms. We try to set the internal heartbeat of the - * watchdog to 2 ms. - */ - -#define WDT_INTERVAL (HZ/2+1) - -/* We can only use 1 card due to the /dev/watchdog restriction */ -static int cards_found; - -/* internal variables */ -static atomic_t open_allowed = ATOMIC_INIT(1); -static char expect_close; -static int temp_panic; -static struct { /* this is private data for each ISA-PC watchdog card */ - char fw_ver_str[6]; /* The cards firmware version */ - int revision; /* The card's revision */ - int supports_temp; /* Wether or not the card has a temperature device */ - int command_mode; /* Wether or not the card is in command mode */ - int boot_status; /* The card's boot status */ - int io_addr; /* The cards I/O address */ - spinlock_t io_lock; /* the lock for io operations */ - struct timer_list timer; /* The timer that pings the watchdog */ - unsigned long next_heartbeat; /* the next_heartbeat for the timer */ -} pcwd_private; - -/* module parameters */ -#define QUIET 0 /* Default */ -#define VERBOSE 1 /* Verbose */ -#define DEBUG 2 /* print fancy stuff too */ -static int debug = QUIET; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); - -#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ -static int heartbeat = WATCHDOG_HEARTBEAT; -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<=heartbeat<=7200 or 0=delay-time from dip-switches, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); - -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) ")"); - -/* - * Internal functions - */ - -static int send_isa_command(int cmd) -{ - int i; - int control_status; - int port0, last_port0; /* Double read for stabilising */ - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n", - cmd); - - /* The WCMD bit must be 1 and the command is only 4 bits in size */ - control_status = (cmd & 0x0F) | WD_WCMD; - outb_p(control_status, pcwd_private.io_addr + 2); - udelay(ISA_COMMAND_TIMEOUT); - - port0 = inb_p(pcwd_private.io_addr); - for (i = 0; i < 25; ++i) { - last_port0 = port0; - port0 = inb_p(pcwd_private.io_addr); - - if (port0 == last_port0) - break; /* Data is stable */ - - udelay (250); - } - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n", - cmd, port0, last_port0); - - return port0; -} - -static int set_command_mode(void) -{ - int i, found=0, count=0; - - /* Set the card into command mode */ - spin_lock(&pcwd_private.io_lock); - while ((!found) && (count < 3)) { - i = send_isa_command(CMD_ISA_IDLE); - - if (i == 0x00) - found = 1; - else if (i == 0xF3) { - /* Card does not like what we've done to it */ - outb_p(0x00, pcwd_private.io_addr + 2); - udelay(1200); /* Spec says wait 1ms */ - outb_p(0x00, pcwd_private.io_addr + 2); - udelay(ISA_COMMAND_TIMEOUT); - } - count++; - } - spin_unlock(&pcwd_private.io_lock); - pcwd_private.command_mode = found; - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "command_mode=%d\n", - pcwd_private.command_mode); - - return(found); -} - -static void unset_command_mode(void) -{ - /* Set the card into normal mode */ - spin_lock(&pcwd_private.io_lock); - outb_p(0x00, pcwd_private.io_addr + 2); - udelay(ISA_COMMAND_TIMEOUT); - spin_unlock(&pcwd_private.io_lock); - - pcwd_private.command_mode = 0; - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "command_mode=%d\n", - pcwd_private.command_mode); -} - -static inline void pcwd_check_temperature_support(void) -{ - if (inb(pcwd_private.io_addr) != 0xF0) - pcwd_private.supports_temp = 1; -} - -static inline void pcwd_get_firmware(void) -{ - int one, ten, hund, minor; - - strcpy(pcwd_private.fw_ver_str, "ERROR"); - - if (set_command_mode()) { - one = send_isa_command(CMD_ISA_VERSION_INTEGER); - ten = send_isa_command(CMD_ISA_VERSION_TENTH); - hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); - minor = send_isa_command(CMD_ISA_VERSION_MINOR); - sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c", one, ten, hund, minor); - } - unset_command_mode(); - - return; -} - -static inline int pcwd_get_option_switches(void) -{ - int option_switches=0; - - if (set_command_mode()) { - /* Get switch settings */ - option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS); - } - - unset_command_mode(); - return(option_switches); -} - -static void pcwd_show_card_info(void) -{ - int option_switches; - - /* Get some extra info from the hardware (in command/debug/diag mode) */ - if (pcwd_private.revision == PCWD_REVISION_A) - printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr); - else if (pcwd_private.revision == PCWD_REVISION_C) { - pcwd_get_firmware(); - printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", - pcwd_private.io_addr, pcwd_private.fw_ver_str); - option_switches = pcwd_get_option_switches(); - printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", - option_switches, - ((option_switches & 0x10) ? "ON" : "OFF"), - ((option_switches & 0x08) ? "ON" : "OFF")); - - /* Reprogram internal heartbeat to 2 seconds */ - if (set_command_mode()) { - send_isa_command(CMD_ISA_DELAY_TIME_2SECS); - unset_command_mode(); - } - } - - if (pcwd_private.supports_temp) - printk(KERN_INFO PFX "Temperature Option Detected\n"); - - if (pcwd_private.boot_status & WDIOF_CARDRESET) - printk(KERN_INFO PFX "Previous reboot was caused by the card\n"); - - if (pcwd_private.boot_status & WDIOF_OVERHEAT) { - printk(KERN_EMERG PFX "Card senses a CPU Overheat. Panicking!\n"); - printk(KERN_EMERG PFX "CPU Overheat\n"); - } - - if (pcwd_private.boot_status == 0) - printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); -} - -static void pcwd_timer_ping(unsigned long data) -{ - int wdrst_stat; - - /* If we got a heartbeat pulse within the WDT_INTERVAL - * we agree to ping the WDT */ - if(time_before(jiffies, pcwd_private.next_heartbeat)) { - /* Ping the watchdog */ - spin_lock(&pcwd_private.io_lock); - if (pcwd_private.revision == PCWD_REVISION_A) { - /* Rev A cards are reset by setting the WD_WDRST bit in register 1 */ - wdrst_stat = inb_p(pcwd_private.io_addr); - wdrst_stat &= 0x0F; - wdrst_stat |= WD_WDRST; - - outb_p(wdrst_stat, pcwd_private.io_addr + 1); - } else { - /* Re-trigger watchdog by writing to port 0 */ - outb_p(0x00, pcwd_private.io_addr); - } - - /* Re-set the timer interval */ - mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL); - - spin_unlock(&pcwd_private.io_lock); - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } -} - -static int pcwd_start(void) -{ - int stat_reg; - - pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); - - /* Start the timer */ - mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL); - - /* Enable the port */ - if (pcwd_private.revision == PCWD_REVISION_C) { - spin_lock(&pcwd_private.io_lock); - outb_p(0x00, pcwd_private.io_addr + 3); - udelay(ISA_COMMAND_TIMEOUT); - stat_reg = inb_p(pcwd_private.io_addr + 2); - spin_unlock(&pcwd_private.io_lock); - if (stat_reg & WD_WDIS) { - printk(KERN_INFO PFX "Could not start watchdog\n"); - return -EIO; - } - } - - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog started\n"); - - return 0; -} - -static int pcwd_stop(void) -{ - int stat_reg; - - /* Stop the timer */ - del_timer(&pcwd_private.timer); - - /* Disable the board */ - if (pcwd_private.revision == PCWD_REVISION_C) { - spin_lock(&pcwd_private.io_lock); - outb_p(0xA5, pcwd_private.io_addr + 3); - udelay(ISA_COMMAND_TIMEOUT); - outb_p(0xA5, pcwd_private.io_addr + 3); - udelay(ISA_COMMAND_TIMEOUT); - stat_reg = inb_p(pcwd_private.io_addr + 2); - spin_unlock(&pcwd_private.io_lock); - if ((stat_reg & WD_WDIS) == 0) { - printk(KERN_INFO PFX "Could not stop watchdog\n"); - return -EIO; - } - } - - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog stopped\n"); - - return 0; -} - -static int pcwd_keepalive(void) -{ - /* user land ping */ - pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); - - return 0; -} - -static int pcwd_set_heartbeat(int t) -{ - if ((t < 2) || (t > 7200)) /* arbitrary upper limit */ - return -EINVAL; - - heartbeat = t; - - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "New heartbeat: %d\n", - heartbeat); - - return 0; -} - -static int pcwd_get_status(int *status) -{ - int control_status; - - *status=0; - spin_lock(&pcwd_private.io_lock); - if (pcwd_private.revision == PCWD_REVISION_A) - /* Rev A cards return status information from - * the base register, which is used for the - * temperature in other cards. */ - control_status = inb(pcwd_private.io_addr); - else { - /* Rev C cards return card status in the base - * address + 1 register. And use different bits - * to indicate a card initiated reset, and an - * over-temperature condition. And the reboot - * status can be reset. */ - control_status = inb(pcwd_private.io_addr + 1); - } - spin_unlock(&pcwd_private.io_lock); - - if (pcwd_private.revision == PCWD_REVISION_A) { - if (control_status & WD_WDRST) - *status |= WDIOF_CARDRESET; - - if (control_status & WD_T110) { - *status |= WDIOF_OVERHEAT; - if (temp_panic) { - printk(KERN_INFO PFX "Temperature overheat trip!\n"); - kernel_power_off(); - /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ - } - } - } else { - if (control_status & WD_REVC_WTRP) - *status |= WDIOF_CARDRESET; - - if (control_status & WD_REVC_TTRP) { - *status |= WDIOF_OVERHEAT; - if (temp_panic) { - printk(KERN_INFO PFX "Temperature overheat trip!\n"); - kernel_power_off(); - /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ - } - } - } - - return 0; -} - -static int pcwd_clear_status(void) -{ - int control_status; - - if (pcwd_private.revision == PCWD_REVISION_C) { - spin_lock(&pcwd_private.io_lock); - - if (debug >= VERBOSE) - printk(KERN_INFO PFX "clearing watchdog trip status\n"); - - control_status = inb_p(pcwd_private.io_addr + 1); - - if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); - printk(KERN_DEBUG PFX "sending: 0x%02x\n", - (control_status & WD_REVC_R2DS)); - } - - /* clear reset status & Keep Relay 2 disable state as it is */ - outb_p((control_status & WD_REVC_R2DS), pcwd_private.io_addr + 1); - - spin_unlock(&pcwd_private.io_lock); - } - return 0; -} - -static int pcwd_get_temperature(int *temperature) -{ - /* check that port 0 gives temperature info and no command results */ - if (pcwd_private.command_mode) - return -1; - - *temperature = 0; - if (!pcwd_private.supports_temp) - return -ENODEV; - - /* - * Convert celsius to fahrenheit, since this was - * the decided 'standard' for this return value. - */ - spin_lock(&pcwd_private.io_lock); - *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32; - spin_unlock(&pcwd_private.io_lock); - - if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "temperature is: %d F\n", - *temperature); - } - - return 0; -} - -/* - * /dev/watchdog handling - */ - -static int pcwd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int rv; - int status; - int temperature; - int new_heartbeat; - int __user *argp = (int __user *)arg; - static struct watchdog_info ident = { - .options = WDIOF_OVERHEAT | - WDIOF_CARDRESET | - WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "PCWD", - }; - - switch(cmd) { - default: - return -ENOTTY; - - case WDIOC_GETSUPPORT: - if(copy_to_user(argp, &ident, sizeof(ident))) - return -EFAULT; - return 0; - - case WDIOC_GETSTATUS: - pcwd_get_status(&status); - return put_user(status, argp); - - case WDIOC_GETBOOTSTATUS: - return put_user(pcwd_private.boot_status, argp); - - case WDIOC_GETTEMP: - if (pcwd_get_temperature(&temperature)) - return -EFAULT; - - return put_user(temperature, argp); - - case WDIOC_SETOPTIONS: - if (pcwd_private.revision == PCWD_REVISION_C) - { - if(copy_from_user(&rv, argp, sizeof(int))) - return -EFAULT; - - if (rv & WDIOS_DISABLECARD) - { - return pcwd_stop(); - } - - if (rv & WDIOS_ENABLECARD) - { - return pcwd_start(); - } - - if (rv & WDIOS_TEMPPANIC) - { - temp_panic = 1; - } - } - return -EINVAL; - - case WDIOC_KEEPALIVE: - pcwd_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_heartbeat, argp)) - return -EFAULT; - - if (pcwd_set_heartbeat(new_heartbeat)) - return -EINVAL; - - pcwd_keepalive(); - /* Fall */ - - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, argp); - } - - return 0; -} - -static ssize_t pcwd_write(struct file *file, const char __user *buf, 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, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - pcwd_keepalive(); - } - return len; -} - -static int pcwd_open(struct inode *inode, struct file *file) -{ - if (!atomic_dec_and_test(&open_allowed) ) { - if (debug >= VERBOSE) - printk(KERN_ERR PFX "Attempt to open already opened device.\n"); - atomic_inc( &open_allowed ); - return -EBUSY; - } - - if (nowayout) - __module_get(THIS_MODULE); - - /* Activate */ - pcwd_start(); - pcwd_keepalive(); - return nonseekable_open(inode, file); -} - -static int pcwd_close(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - pcwd_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - pcwd_keepalive(); - } - expect_close = 0; - atomic_inc( &open_allowed ); - return 0; -} - -/* - * /dev/temperature handling - */ - -static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - int temperature; - - if (pcwd_get_temperature(&temperature)) - return -EFAULT; - - if (copy_to_user(buf, &temperature, 1)) - return -EFAULT; - - return 1; -} - -static int pcwd_temp_open(struct inode *inode, struct file *file) -{ - if (!pcwd_private.supports_temp) - return -ENODEV; - - return nonseekable_open(inode, file); -} - -static int pcwd_temp_close(struct inode *inode, struct file *file) -{ - return 0; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations pcwd_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = pcwd_write, - .ioctl = pcwd_ioctl, - .open = pcwd_open, - .release = pcwd_close, -}; - -static struct miscdevice pcwd_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &pcwd_fops, -}; - -static const struct file_operations pcwd_temp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = pcwd_temp_read, - .open = pcwd_temp_open, - .release = pcwd_temp_close, -}; - -static struct miscdevice temp_miscdev = { - .minor = TEMP_MINOR, - .name = "temperature", - .fops = &pcwd_temp_fops, -}; - -/* - * Init & exit routines - */ - -static inline int get_revision(void) -{ - int r = PCWD_REVISION_C; - - spin_lock(&pcwd_private.io_lock); - /* REV A cards use only 2 io ports; test - * presumes a floating bus reads as 0xff. */ - if ((inb(pcwd_private.io_addr + 2) == 0xFF) || - (inb(pcwd_private.io_addr + 3) == 0xFF)) - r=PCWD_REVISION_A; - spin_unlock(&pcwd_private.io_lock); - - return r; -} - -/* - * The ISA cards have a heartbeat bit in one of the registers, which - * register is card dependent. The heartbeat bit is monitored, and if - * found, is considered proof that a Berkshire card has been found. - * The initial rate is once per second at board start up, then twice - * per second for normal operation. - */ -static int __devinit pcwd_isa_match(struct device *dev, unsigned int id) -{ - int base_addr=pcwd_ioports[id]; - int port0, last_port0; /* Reg 0, in case it's REV A */ - int port1, last_port1; /* Register 1 for REV C cards */ - int i; - int retval; - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n", - id); - - if (!request_region (base_addr, 4, "PCWD")) { - printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr); - return 0; - } - - retval = 0; - - port0 = inb_p(base_addr); /* For REV A boards */ - port1 = inb_p(base_addr + 1); /* For REV C boards */ - if (port0 != 0xff || port1 != 0xff) { - /* Not an 'ff' from a floating bus, so must be a card! */ - for (i = 0; i < 4; ++i) { - - msleep(500); - - last_port0 = port0; - last_port1 = port1; - - port0 = inb_p(base_addr); - port1 = inb_p(base_addr + 1); - - /* Has either hearbeat bit changed? */ - if ((port0 ^ last_port0) & WD_HRTBT || - (port1 ^ last_port1) & WD_REVC_HRBT) { - retval = 1; - break; - } - } - } - release_region (base_addr, 4); - - return retval; -} - -static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id) -{ - int ret; - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n", - id); - - cards_found++; - if (cards_found == 1) - printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); - - if (cards_found > 1) { - printk(KERN_ERR PFX "This driver only supports 1 device\n"); - return -ENODEV; - } - - if (pcwd_ioports[id] == 0x0000) { - printk(KERN_ERR PFX "No I/O-Address for card detected\n"); - return -ENODEV; - } - pcwd_private.io_addr = pcwd_ioports[id]; - - spin_lock_init(&pcwd_private.io_lock); - - /* Check card's revision */ - pcwd_private.revision = get_revision(); - - if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - pcwd_private.io_addr); - ret=-EIO; - goto error_request_region; - } - - /* Initial variables */ - pcwd_private.supports_temp = 0; - temp_panic = 0; - pcwd_private.boot_status = 0x0000; - - /* get the boot_status */ - pcwd_get_status(&pcwd_private.boot_status); - - /* clear the "card caused reboot" flag */ - pcwd_clear_status(); - - setup_timer(&pcwd_private.timer, pcwd_timer_ping, 0); - - /* Disable the board */ - pcwd_stop(); - - /* Check whether or not the card supports the temperature device */ - pcwd_check_temperature_support(); - - /* Show info about the card itself */ - pcwd_show_card_info(); - - /* If heartbeat = 0 then we use the heartbeat from the dip-switches */ - if (heartbeat == 0) - heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)]; - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (pcwd_set_heartbeat(heartbeat)) { - pcwd_set_heartbeat(WATCHDOG_HEARTBEAT); - printk(KERN_INFO PFX "heartbeat value must be 2<=heartbeat<=7200, using %d\n", - WATCHDOG_HEARTBEAT); - } - - if (pcwd_private.supports_temp) { - ret = misc_register(&temp_miscdev); - if (ret) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - TEMP_MINOR, ret); - goto error_misc_register_temp; - } - } - - ret = misc_register(&pcwd_miscdev); - if (ret) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto error_misc_register_watchdog; - } - - printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", - heartbeat, nowayout); - - return 0; - -error_misc_register_watchdog: - if (pcwd_private.supports_temp) - misc_deregister(&temp_miscdev); -error_misc_register_temp: - release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); -error_request_region: - pcwd_private.io_addr = 0x0000; - cards_found--; - return ret; -} - -static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id) -{ - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n", - id); - - if (!pcwd_private.io_addr) - return 1; - - /* Disable the board */ - if (!nowayout) - pcwd_stop(); - - /* Deregister */ - misc_deregister(&pcwd_miscdev); - if (pcwd_private.supports_temp) - misc_deregister(&temp_miscdev); - release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); - pcwd_private.io_addr = 0x0000; - cards_found--; - - return 0; -} - -static void pcwd_isa_shutdown(struct device *dev, unsigned int id) -{ - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n", - id); - - pcwd_stop(); -} - -static struct isa_driver pcwd_isa_driver = { - .match = pcwd_isa_match, - .probe = pcwd_isa_probe, - .remove = __devexit_p(pcwd_isa_remove), - .shutdown = pcwd_isa_shutdown, - .driver = { - .owner = THIS_MODULE, - .name = WATCHDOG_NAME, - }, -}; - -static int __init pcwd_init_module(void) -{ - return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS); -} - -static void __exit pcwd_cleanup_module(void) -{ - isa_unregister_driver(&pcwd_isa_driver); - printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); -} - -module_init(pcwd_init_module); -module_exit(pcwd_cleanup_module); - -MODULE_AUTHOR("Ken Hollis , Wim Van Sebroeck "); -MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); -MODULE_VERSION(WATCHDOG_VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -MODULE_ALIAS_MISCDEV(TEMP_MINOR); diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c deleted file mode 100644 index 61a89e9..0000000 --- a/drivers/char/watchdog/pcwd_pci.c +++ /dev/null @@ -1,832 +0,0 @@ -/* - * Berkshire PCI-PC Watchdog Card Driver - * - * (c) Copyright 2003-2007 Wim Van Sebroeck . - * - * Based on source code of the following authors: - * Ken Hollis , - * Lindsay Harris , - * Alan Cox , - * Matt Domsch , - * Rob Radez - * - * 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. - */ - -/* - * A bells and whistles driver is available from: - * http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/ - * - * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ - */ - -/* - * Includes, defines, variables, module parameters, ... - */ - -#include /* For module specific items */ -#include /* For new moduleparam's */ -#include /* For standard types (like size_t) */ -#include /* For the -ENODEV/... values */ -#include /* For printk/panic/... */ -#include /* For mdelay function */ -#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ -#include /* For the watchdog specific items */ -#include /* For notifier support */ -#include /* For reboot_notifier stuff */ -#include /* For __init/__exit/... */ -#include /* For file operations */ -#include /* For pci functions */ -#include /* For io-port access */ -#include /* For spin_lock/spin_unlock/... */ - -#include /* For copy_to_user/put_user/... */ -#include /* For inb/outb/... */ - -/* Module and version information */ -#define WATCHDOG_VERSION "1.03" -#define WATCHDOG_DATE "21 Jan 2007" -#define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" -#define WATCHDOG_NAME "pcwd_pci" -#define PFX WATCHDOG_NAME ": " -#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " (" WATCHDOG_DATE ")\n" - -/* Stuff for the PCI ID's */ -#ifndef PCI_VENDOR_ID_QUICKLOGIC -#define PCI_VENDOR_ID_QUICKLOGIC 0x11e3 -#endif - -#ifndef PCI_DEVICE_ID_WATCHDOG_PCIPCWD -#define PCI_DEVICE_ID_WATCHDOG_PCIPCWD 0x5030 -#endif - -/* - * These are the defines that describe the control status bits for the - * PCI-PC Watchdog card. - */ -/* Port 1 : Control Status #1 */ -#define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ -#define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ -#define WD_PCI_TTRP 0x04 /* Temperature Trip status */ -#define WD_PCI_RL2A 0x08 /* Relay 2 Active */ -#define WD_PCI_RL1A 0x10 /* Relay 1 Active */ -#define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip/reset */ -#define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */ -/* Port 2 : Control Status #2 */ -#define WD_PCI_WDIS 0x10 /* Watchdog Disable */ -#define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */ -#define WD_PCI_WRSP 0x40 /* Watchdog wrote response */ -#define WD_PCI_PCMD 0x80 /* PC has sent command */ - -/* according to documentation max. time to process a command for the pci - * watchdog card is 100 ms, so we give it 150 ms to do it's job */ -#define PCI_COMMAND_TIMEOUT 150 - -/* Watchdog's internal commands */ -#define CMD_GET_STATUS 0x04 -#define CMD_GET_FIRMWARE_VERSION 0x08 -#define CMD_READ_WATCHDOG_TIMEOUT 0x18 -#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 -#define CMD_GET_CLEAR_RESET_COUNT 0x84 - -/* Watchdog's Dip Switch heartbeat values */ -static const int heartbeat_tbl [] = { - 5, /* OFF-OFF-OFF = 5 Sec */ - 10, /* OFF-OFF-ON = 10 Sec */ - 30, /* OFF-ON-OFF = 30 Sec */ - 60, /* OFF-ON-ON = 1 Min */ - 300, /* ON-OFF-OFF = 5 Min */ - 600, /* ON-OFF-ON = 10 Min */ - 1800, /* ON-ON-OFF = 30 Min */ - 3600, /* ON-ON-ON = 1 hour */ -}; - -/* We can only use 1 card due to the /dev/watchdog restriction */ -static int cards_found; - -/* internal variables */ -static int temp_panic; -static unsigned long is_active; -static char expect_release; -static struct { /* this is private data for each PCI-PC watchdog card */ - int supports_temp; /* Wether or not the card has a temperature device */ - int boot_status; /* The card's boot status */ - unsigned long io_addr; /* The cards I/O address */ - spinlock_t io_lock; /* the lock for io operations */ - struct pci_dev *pdev; /* the PCI-device */ -} pcipcwd_private; - -/* module parameters */ -#define QUIET 0 /* Default */ -#define VERBOSE 1 /* Verbose */ -#define DEBUG 2 /* print fancy stuff too */ -static int debug = QUIET; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); - -#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ -static int heartbeat = WATCHDOG_HEARTBEAT; -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0= DEBUG) - printk(KERN_DEBUG PFX "sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x\n", - cmd, *msb, *lsb); - - spin_lock(&pcipcwd_private.io_lock); - /* If a command requires data it should be written first. - * Data for commands with 8 bits of data should be written to port 4. - * Commands with 16 bits of data, should be written as LSB to port 4 - * and MSB to port 5. - * After the required data has been written then write the command to - * port 6. */ - outb_p(*lsb, pcipcwd_private.io_addr + 4); - outb_p(*msb, pcipcwd_private.io_addr + 5); - outb_p(cmd, pcipcwd_private.io_addr + 6); - - /* wait till the pci card processed the command, signaled by - * the WRSP bit in port 2 and give it a max. timeout of - * PCI_COMMAND_TIMEOUT to process */ - got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; - for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response); count++) { - mdelay(1); - got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; - } - - if (debug >= DEBUG) { - if (got_response) { - printk(KERN_DEBUG PFX "time to process command was: %d ms\n", - count); - } else { - printk(KERN_DEBUG PFX "card did not respond on command!\n"); - } - } - - if (got_response) { - /* read back response */ - *lsb = inb_p(pcipcwd_private.io_addr + 4); - *msb = inb_p(pcipcwd_private.io_addr + 5); - - /* clear WRSP bit */ - inb_p(pcipcwd_private.io_addr + 6); - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n", - cmd, *msb, *lsb); - } - - spin_unlock(&pcipcwd_private.io_lock); - - return got_response; -} - -static inline void pcipcwd_check_temperature_support(void) -{ - if (inb_p(pcipcwd_private.io_addr) != 0xF0) - pcipcwd_private.supports_temp = 1; -} - -static int pcipcwd_get_option_switches(void) -{ - int option_switches; - - option_switches = inb_p(pcipcwd_private.io_addr + 3); - return option_switches; -} - -static void pcipcwd_show_card_info(void) -{ - int got_fw_rev, fw_rev_major, fw_rev_minor; - char fw_ver_str[20]; /* The cards firmware version */ - int option_switches; - - got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); - if (got_fw_rev) { - sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); - } else { - sprintf(fw_ver_str, ""); - } - - /* Get switch settings */ - option_switches = pcipcwd_get_option_switches(); - - printk(KERN_INFO PFX "Found card at port 0x%04x (Firmware: %s) %s temp option\n", - (int) pcipcwd_private.io_addr, fw_ver_str, - (pcipcwd_private.supports_temp ? "with" : "without")); - - printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", - option_switches, - ((option_switches & 0x10) ? "ON" : "OFF"), - ((option_switches & 0x08) ? "ON" : "OFF")); - - if (pcipcwd_private.boot_status & WDIOF_CARDRESET) - printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card\n"); - - if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) - printk(KERN_INFO PFX "Card sensed a CPU Overheat\n"); - - if (pcipcwd_private.boot_status == 0) - printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); -} - -static int pcipcwd_start(void) -{ - int stat_reg; - - spin_lock(&pcipcwd_private.io_lock); - outb_p(0x00, pcipcwd_private.io_addr + 3); - udelay(1000); - - stat_reg = inb_p(pcipcwd_private.io_addr + 2); - spin_unlock(&pcipcwd_private.io_lock); - - if (stat_reg & WD_PCI_WDIS) { - printk(KERN_ERR PFX "Card timer not enabled\n"); - return -1; - } - - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog started\n"); - - return 0; -} - -static int pcipcwd_stop(void) -{ - int stat_reg; - - spin_lock(&pcipcwd_private.io_lock); - outb_p(0xA5, pcipcwd_private.io_addr + 3); - udelay(1000); - - outb_p(0xA5, pcipcwd_private.io_addr + 3); - udelay(1000); - - stat_reg = inb_p(pcipcwd_private.io_addr + 2); - spin_unlock(&pcipcwd_private.io_lock); - - if (!(stat_reg & WD_PCI_WDIS)) { - printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); - return -1; - } - - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Watchdog stopped\n"); - - return 0; -} - -static int pcipcwd_keepalive(void) -{ - /* Re-trigger watchdog by writing to port 0 */ - spin_lock(&pcipcwd_private.io_lock); - outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */ - spin_unlock(&pcipcwd_private.io_lock); - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); - - return 0; -} - -static int pcipcwd_set_heartbeat(int t) -{ - int t_msb = t / 256; - int t_lsb = t % 256; - - if ((t < 0x0001) || (t > 0xFFFF)) - return -EINVAL; - - /* Write new heartbeat to watchdog */ - send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb); - - heartbeat = t; - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "New heartbeat: %d\n", - heartbeat); - - return 0; -} - -static int pcipcwd_get_status(int *status) -{ - int control_status; - - *status=0; - control_status = inb_p(pcipcwd_private.io_addr + 1); - if (control_status & WD_PCI_WTRP) - *status |= WDIOF_CARDRESET; - if (control_status & WD_PCI_TTRP) { - *status |= WDIOF_OVERHEAT; - if (temp_panic) - panic(PFX "Temperature overheat trip!\n"); - } - - if (debug >= DEBUG) - printk(KERN_DEBUG PFX "Control Status #1: 0x%02x\n", - control_status); - - return 0; -} - -static int pcipcwd_clear_status(void) -{ - int control_status; - int msb; - int reset_counter; - - if (debug >= VERBOSE) - printk(KERN_INFO PFX "clearing watchdog trip status & LED\n"); - - control_status = inb_p(pcipcwd_private.io_addr + 1); - - if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); - printk(KERN_DEBUG PFX "sending: 0x%02x\n", - (control_status & WD_PCI_R2DS) | WD_PCI_WTRP); - } - - /* clear trip status & LED and keep mode of relay 2 */ - outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP, pcipcwd_private.io_addr + 1); - - /* clear reset counter */ - msb=0; - reset_counter=0xff; - send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter); - - if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "reset count was: 0x%02x\n", - reset_counter); - } - - return 0; -} - -static int pcipcwd_get_temperature(int *temperature) -{ - *temperature = 0; - if (!pcipcwd_private.supports_temp) - return -ENODEV; - - spin_lock(&pcipcwd_private.io_lock); - *temperature = inb_p(pcipcwd_private.io_addr); - spin_unlock(&pcipcwd_private.io_lock); - - /* - * Convert celsius to fahrenheit, since this was - * the decided 'standard' for this return value. - */ - *temperature = (*temperature * 9 / 5) + 32; - - if (debug >= DEBUG) { - printk(KERN_DEBUG PFX "temperature is: %d F\n", - *temperature); - } - - return 0; -} - -static int pcipcwd_get_timeleft(int *time_left) -{ - int msb; - int lsb; - - /* Read the time that's left before rebooting */ - /* Note: if the board is not yet armed then we will read 0xFFFF */ - send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); - - *time_left = (msb << 8) + lsb; - - if (debug >= VERBOSE) - printk(KERN_DEBUG PFX "Time left before next reboot: %d\n", - *time_left); - - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t pcipcwd_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 */ - pcipcwd_keepalive(); - } - return len; -} - -static int pcipcwd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_OVERHEAT | - WDIOF_CARDRESET | - WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = WATCHDOG_DRIVER_NAME, - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, - sizeof (ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - { - int status; - - pcipcwd_get_status(&status); - - return put_user(status, p); - } - - case WDIOC_GETBOOTSTATUS: - return put_user(pcipcwd_private.boot_status, p); - - case WDIOC_GETTEMP: - { - int temperature; - - if (pcipcwd_get_temperature(&temperature)) - return -EFAULT; - - return put_user(temperature, p); - } - - case WDIOC_KEEPALIVE: - pcipcwd_keepalive(); - return 0; - - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if (get_user (new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - if (pcipcwd_stop()) - return -EIO; - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - if (pcipcwd_start()) - return -EIO; - retval = 0; - } - - if (new_options & WDIOS_TEMPPANIC) { - temp_panic = 1; - retval = 0; - } - - return retval; - } - - case WDIOC_SETTIMEOUT: - { - int new_heartbeat; - - if (get_user(new_heartbeat, p)) - return -EFAULT; - - if (pcipcwd_set_heartbeat(new_heartbeat)) - return -EINVAL; - - pcipcwd_keepalive(); - /* Fall */ - } - - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - - case WDIOC_GETTIMELEFT: - { - int time_left; - - if (pcipcwd_get_timeleft(&time_left)) - return -EFAULT; - - return put_user(time_left, p); - } - - default: - return -ENOTTY; - } -} - -static int pcipcwd_open(struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &is_active)) { - if (debug >= VERBOSE) - printk(KERN_ERR PFX "Attempt to open already opened device.\n"); - return -EBUSY; - } - - /* Activate */ - pcipcwd_start(); - pcipcwd_keepalive(); - return nonseekable_open(inode, file); -} - -static int pcipcwd_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - */ - if (expect_release == 42) { - pcipcwd_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - pcipcwd_keepalive(); - } - expect_release = 0; - clear_bit(0, &is_active); - return 0; -} - -/* - * /dev/temperature handling - */ - -static ssize_t pcipcwd_temp_read(struct file *file, char __user *data, - size_t len, loff_t *ppos) -{ - int temperature; - - if (pcipcwd_get_temperature(&temperature)) - return -EFAULT; - - if (copy_to_user (data, &temperature, 1)) - return -EFAULT; - - return 1; -} - -static int pcipcwd_temp_open(struct inode *inode, struct file *file) -{ - if (!pcipcwd_private.supports_temp) - return -ENODEV; - - return nonseekable_open(inode, file); -} - -static int pcipcwd_temp_release(struct inode *inode, struct file *file) -{ - return 0; -} - -/* - * Notify system - */ - -static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) { - /* Turn the WDT off */ - pcipcwd_stop(); - } - - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations pcipcwd_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = pcipcwd_write, - .ioctl = pcipcwd_ioctl, - .open = pcipcwd_open, - .release = pcipcwd_release, -}; - -static struct miscdevice pcipcwd_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &pcipcwd_fops, -}; - -static const struct file_operations pcipcwd_temp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = pcipcwd_temp_read, - .open = pcipcwd_temp_open, - .release = pcipcwd_temp_release, -}; - -static struct miscdevice pcipcwd_temp_miscdev = { - .minor = TEMP_MINOR, - .name = "temperature", - .fops = &pcipcwd_temp_fops, -}; - -static struct notifier_block pcipcwd_notifier = { - .notifier_call = pcipcwd_notify_sys, -}; - -/* - * Init & exit routines - */ - -static int __devinit pcipcwd_card_init(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int ret = -EIO; - - cards_found++; - if (cards_found == 1) - printk(KERN_INFO PFX DRIVER_VERSION); - - if (cards_found > 1) { - printk(KERN_ERR PFX "This driver only supports 1 device\n"); - return -ENODEV; - } - - if (pci_enable_device(pdev)) { - printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); - return -ENODEV; - } - - if (pci_resource_start(pdev, 0) == 0x0000) { - printk(KERN_ERR PFX "No I/O-Address for card detected\n"); - ret = -ENODEV; - goto err_out_disable_device; - } - - pcipcwd_private.pdev = pdev; - pcipcwd_private.io_addr = pci_resource_start(pdev, 0); - - if (pci_request_regions(pdev, WATCHDOG_NAME)) { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - (int) pcipcwd_private.io_addr); - ret = -EIO; - goto err_out_disable_device; - } - - /* get the boot_status */ - pcipcwd_get_status(&pcipcwd_private.boot_status); - - /* clear the "card caused reboot" flag */ - pcipcwd_clear_status(); - - /* disable card */ - pcipcwd_stop(); - - /* Check whether or not the card supports the temperature device */ - pcipcwd_check_temperature_support(); - - /* Show info about the card itself */ - pcipcwd_show_card_info(); - - /* If heartbeat = 0 then we use the heartbeat from the dip-switches */ - if (heartbeat == 0) - heartbeat = heartbeat_tbl[(pcipcwd_get_option_switches() & 0x07)]; - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (pcipcwd_set_heartbeat(heartbeat)) { - pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT); - printk(KERN_INFO PFX "heartbeat value must be 0"); -MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -MODULE_ALIAS_MISCDEV(TEMP_MINOR); diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c deleted file mode 100644 index 0f3fd6c..0000000 --- a/drivers/char/watchdog/pcwd_usb.c +++ /dev/null @@ -1,824 +0,0 @@ -/* - * Berkshire USB-PC Watchdog Card Driver - * - * (c) Copyright 2004-2007 Wim Van Sebroeck . - * - * Based on source code of the following authors: - * Ken Hollis , - * Alan Cox , - * Matt Domsch , - * Rob Radez , - * Greg Kroah-Hartman - * - * 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. - * - * Thanks also to Simon Machell at Berkshire Products Inc. for - * providing the test hardware. More info is available at - * http://www.berkprod.com/ or http://www.pcwatchdog.com/ - */ - -#include /* For module specific items */ -#include /* For new moduleparam's */ -#include /* For standard types (like size_t) */ -#include /* For the -ENODEV/... values */ -#include /* For printk/panic/... */ -#include /* For mdelay function */ -#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ -#include /* For the watchdog specific items */ -#include /* For notifier support */ -#include /* For reboot_notifier stuff */ -#include /* For __init/__exit/... */ -#include /* For file operations */ -#include /* For USB functions */ -#include /* For kmalloc, ... */ -#include /* For mutex locking */ -#include /* For HID_REQ_SET_REPORT & HID_DT_REPORT */ - -#include /* For copy_to_user/put_user/... */ - - -#ifdef CONFIG_USB_DEBUG - static int debug = 1; -#else - static int debug; -#endif - -/* Use our own dbg macro */ -#undef dbg -#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG PFX format "\n" , ## arg); } while (0) - - -/* Module and Version Information */ -#define DRIVER_VERSION "1.02" -#define DRIVER_DATE "21 Jan 2007" -#define DRIVER_AUTHOR "Wim Van Sebroeck " -#define DRIVER_DESC "Berkshire USB-PC Watchdog driver" -#define DRIVER_LICENSE "GPL" -#define DRIVER_NAME "pcwd_usb" -#define PFX DRIVER_NAME ": " - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -MODULE_ALIAS_MISCDEV(TEMP_MINOR); - -/* Module Parameters */ -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug enabled or not"); - -#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ -static int heartbeat = WATCHDOG_HEARTBEAT; -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0context; - unsigned char *data = usb_pcwd->intr_buffer; - int retval; - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - /* -EPIPE: should clear the halt */ - default: /* error */ - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto resubmit; - } - - dbg("received following data cmd=0x%02x msb=0x%02x lsb=0x%02x", - data[0], data[1], data[2]); - - usb_pcwd->cmd_command = data[0]; - usb_pcwd->cmd_data_msb = data[1]; - usb_pcwd->cmd_data_lsb = data[2]; - - /* notify anyone waiting that the cmd has finished */ - atomic_set (&usb_pcwd->cmd_received, 1); - -resubmit: - retval = usb_submit_urb (urb, GFP_ATOMIC); - if (retval) - printk(KERN_ERR PFX "can't resubmit intr, usb_submit_urb failed with result %d\n", - retval); -} - -static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd, unsigned char cmd, - unsigned char *msb, unsigned char *lsb) -{ - int got_response, count; - unsigned char buf[6]; - - /* We will not send any commands if the USB PCWD device does not exist */ - if ((!usb_pcwd) || (!usb_pcwd->exists)) - return -1; - - /* The USB PC Watchdog uses a 6 byte report format. The board currently uses - * only 3 of the six bytes of the report. */ - buf[0] = cmd; /* Byte 0 = CMD */ - buf[1] = *msb; /* Byte 1 = Data MSB */ - buf[2] = *lsb; /* Byte 2 = Data LSB */ - buf[3] = buf[4] = buf[5] = 0; /* All other bytes not used */ - - dbg("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x", - buf[0], buf[1], buf[2]); - - atomic_set (&usb_pcwd->cmd_received, 0); - - if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0), - HID_REQ_SET_REPORT, HID_DT_REPORT, - 0x0200, usb_pcwd->interface_number, buf, sizeof(buf), - USB_COMMAND_TIMEOUT) != sizeof(buf)) { - dbg("usb_pcwd_send_command: error in usb_control_msg for cmd 0x%x 0x%x 0x%x\n", cmd, *msb, *lsb); - } - /* wait till the usb card processed the command, - * with a max. timeout of USB_COMMAND_TIMEOUT */ - got_response = 0; - for (count = 0; (count < USB_COMMAND_TIMEOUT) && (!got_response); count++) { - mdelay(1); - if (atomic_read (&usb_pcwd->cmd_received)) - got_response = 1; - } - - if ((got_response) && (cmd == usb_pcwd->cmd_command)) { - /* read back response */ - *msb = usb_pcwd->cmd_data_msb; - *lsb = usb_pcwd->cmd_data_lsb; - } - - return got_response; -} - -static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd) -{ - unsigned char msb = 0x00; - unsigned char lsb = 0x00; - int retval; - - /* Enable Watchdog */ - retval = usb_pcwd_send_command(usb_pcwd, CMD_ENABLE_WATCHDOG, &msb, &lsb); - - if ((retval == 0) || (lsb == 0)) { - printk(KERN_ERR PFX "Card did not acknowledge enable attempt\n"); - return -1; - } - - return 0; -} - -static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd) -{ - unsigned char msb = 0xA5; - unsigned char lsb = 0xC3; - int retval; - - /* Disable Watchdog */ - retval = usb_pcwd_send_command(usb_pcwd, CMD_DISABLE_WATCHDOG, &msb, &lsb); - - if ((retval == 0) || (lsb != 0)) { - printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); - return -1; - } - - return 0; -} - -static int usb_pcwd_keepalive(struct usb_pcwd_private *usb_pcwd) -{ - unsigned char dummy; - - /* Re-trigger Watchdog */ - usb_pcwd_send_command(usb_pcwd, CMD_TRIGGER, &dummy, &dummy); - - return 0; -} - -static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t) -{ - unsigned char msb = t / 256; - unsigned char lsb = t % 256; - - if ((t < 0x0001) || (t > 0xFFFF)) - return -EINVAL; - - /* Write new heartbeat to watchdog */ - usb_pcwd_send_command(usb_pcwd, CMD_WRITE_WATCHDOG_TIMEOUT, &msb, &lsb); - - heartbeat = t; - return 0; -} - -static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temperature) -{ - unsigned char msb, lsb; - - usb_pcwd_send_command(usb_pcwd, CMD_READ_TEMP, &msb, &lsb); - - /* - * Convert celsius to fahrenheit, since this was - * the decided 'standard' for this return value. - */ - *temperature = (lsb * 9 / 5) + 32; - - return 0; -} - -static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd, int *time_left) -{ - unsigned char msb, lsb; - - /* Read the time that's left before rebooting */ - /* Note: if the board is not yet armed then we will read 0xFFFF */ - usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); - - *time_left = (msb << 8) + lsb; - - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t usb_pcwd_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 */ - usb_pcwd_keepalive(usb_pcwd_device); - } - return len; -} - -static int usb_pcwd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | - WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = DRIVER_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_GETTEMP: - { - int temperature; - - if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature)) - return -EFAULT; - - return put_user(temperature, p); - } - - case WDIOC_KEEPALIVE: - usb_pcwd_keepalive(usb_pcwd_device); - return 0; - - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if (get_user (new_options, p)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - usb_pcwd_stop(usb_pcwd_device); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - usb_pcwd_start(usb_pcwd_device); - retval = 0; - } - - return retval; - } - - case WDIOC_SETTIMEOUT: - { - int new_heartbeat; - - if (get_user(new_heartbeat, p)) - return -EFAULT; - - if (usb_pcwd_set_heartbeat(usb_pcwd_device, new_heartbeat)) - return -EINVAL; - - usb_pcwd_keepalive(usb_pcwd_device); - /* Fall */ - } - - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - - case WDIOC_GETTIMELEFT: - { - int time_left; - - if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left)) - return -EFAULT; - - return put_user(time_left, p); - } - - default: - return -ENOTTY; - } -} - -static int usb_pcwd_open(struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &is_active)) - return -EBUSY; - - /* Activate */ - usb_pcwd_start(usb_pcwd_device); - usb_pcwd_keepalive(usb_pcwd_device); - return nonseekable_open(inode, file); -} - -static int usb_pcwd_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - */ - if (expect_release == 42) { - usb_pcwd_stop(usb_pcwd_device); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - usb_pcwd_keepalive(usb_pcwd_device); - } - expect_release = 0; - clear_bit(0, &is_active); - return 0; -} - -/* - * /dev/temperature handling - */ - -static ssize_t usb_pcwd_temperature_read(struct file *file, char __user *data, - size_t len, loff_t *ppos) -{ - int temperature; - - if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature)) - return -EFAULT; - - if (copy_to_user(data, &temperature, 1)) - return -EFAULT; - - return 1; -} - -static int usb_pcwd_temperature_open(struct inode *inode, struct file *file) -{ - return nonseekable_open(inode, file); -} - -static int usb_pcwd_temperature_release(struct inode *inode, struct file *file) -{ - return 0; -} - -/* - * Notify system - */ - -static int usb_pcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) { - /* Turn the WDT off */ - usb_pcwd_stop(usb_pcwd_device); - } - - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations usb_pcwd_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = usb_pcwd_write, - .ioctl = usb_pcwd_ioctl, - .open = usb_pcwd_open, - .release = usb_pcwd_release, -}; - -static struct miscdevice usb_pcwd_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &usb_pcwd_fops, -}; - -static const struct file_operations usb_pcwd_temperature_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = usb_pcwd_temperature_read, - .open = usb_pcwd_temperature_open, - .release = usb_pcwd_temperature_release, -}; - -static struct miscdevice usb_pcwd_temperature_miscdev = { - .minor = TEMP_MINOR, - .name = "temperature", - .fops = &usb_pcwd_temperature_fops, -}; - -static struct notifier_block usb_pcwd_notifier = { - .notifier_call = usb_pcwd_notify_sys, -}; - -/** - * usb_pcwd_delete - */ -static inline void usb_pcwd_delete (struct usb_pcwd_private *usb_pcwd) -{ - usb_free_urb(usb_pcwd->intr_urb); - if (usb_pcwd->intr_buffer != NULL) - usb_buffer_free(usb_pcwd->udev, usb_pcwd->intr_size, - usb_pcwd->intr_buffer, usb_pcwd->intr_dma); - kfree (usb_pcwd); -} - -/** - * usb_pcwd_probe - * - * Called by the usb core when a new device is connected that it thinks - * this driver might be interested in. - */ -static int usb_pcwd_probe(struct usb_interface *interface, const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct usb_pcwd_private *usb_pcwd = NULL; - int pipe, maxp; - int retval = -ENOMEM; - int got_fw_rev; - unsigned char fw_rev_major, fw_rev_minor; - char fw_ver_str[20]; - unsigned char option_switches, dummy; - - cards_found++; - if (cards_found > 1) { - printk(KERN_ERR PFX "This driver only supports 1 device\n"); - return -ENODEV; - } - - /* get the active interface descriptor */ - iface_desc = interface->cur_altsetting; - - /* check out that we have a HID device */ - if (!(iface_desc->desc.bInterfaceClass == USB_CLASS_HID)) { - printk(KERN_ERR PFX "The device isn't a Human Interface Device\n"); - return -ENODEV; - } - - /* check out the endpoint: it has to be Interrupt & IN */ - endpoint = &iface_desc->endpoint[0].desc; - - if (!((endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT))) { - /* we didn't find a Interrupt endpoint with direction IN */ - printk(KERN_ERR PFX "Couldn't find an INTR & IN endpoint\n"); - return -ENODEV; - } - - /* get a handle to the interrupt data pipe */ - pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); - maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); - - /* allocate memory for our device and initialize it */ - usb_pcwd = kzalloc (sizeof(struct usb_pcwd_private), GFP_KERNEL); - if (usb_pcwd == NULL) { - printk(KERN_ERR PFX "Out of memory\n"); - goto error; - } - - usb_pcwd_device = usb_pcwd; - - mutex_init(&usb_pcwd->mtx); - usb_pcwd->udev = udev; - usb_pcwd->interface = interface; - usb_pcwd->interface_number = iface_desc->desc.bInterfaceNumber; - usb_pcwd->intr_size = (le16_to_cpu(endpoint->wMaxPacketSize) > 8 ? le16_to_cpu(endpoint->wMaxPacketSize) : 8); - - /* set up the memory buffer's */ - if (!(usb_pcwd->intr_buffer = usb_buffer_alloc(udev, usb_pcwd->intr_size, GFP_ATOMIC, &usb_pcwd->intr_dma))) { - printk(KERN_ERR PFX "Out of memory\n"); - goto error; - } - - /* allocate the urb's */ - usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!usb_pcwd->intr_urb) { - printk(KERN_ERR PFX "Out of memory\n"); - goto error; - } - - /* initialise the intr urb's */ - usb_fill_int_urb(usb_pcwd->intr_urb, udev, pipe, - usb_pcwd->intr_buffer, usb_pcwd->intr_size, - usb_pcwd_intr_done, usb_pcwd, endpoint->bInterval); - usb_pcwd->intr_urb->transfer_dma = usb_pcwd->intr_dma; - usb_pcwd->intr_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - /* register our interrupt URB with the USB system */ - if (usb_submit_urb(usb_pcwd->intr_urb, GFP_KERNEL)) { - printk(KERN_ERR PFX "Problem registering interrupt URB\n"); - retval = -EIO; /* failure */ - goto error; - } - - /* The device exists and can be communicated with */ - usb_pcwd->exists = 1; - - /* disable card */ - usb_pcwd_stop(usb_pcwd); - - /* Get the Firmware Version */ - got_fw_rev = usb_pcwd_send_command(usb_pcwd, CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); - if (got_fw_rev) { - sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); - } else { - sprintf(fw_ver_str, ""); - } - - printk(KERN_INFO PFX "Found card (Firmware: %s) with temp option\n", - fw_ver_str); - - /* Get switch settings */ - usb_pcwd_send_command(usb_pcwd, CMD_GET_DIP_SWITCH_SETTINGS, &dummy, &option_switches); - - printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", - option_switches, - ((option_switches & 0x10) ? "ON" : "OFF"), - ((option_switches & 0x08) ? "ON" : "OFF")); - - /* If heartbeat = 0 then we use the heartbeat from the dip-switches */ - if (heartbeat == 0) - heartbeat = heartbeat_tbl[(option_switches & 0x07)]; - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (usb_pcwd_set_heartbeat(usb_pcwd, heartbeat)) { - usb_pcwd_set_heartbeat(usb_pcwd, WATCHDOG_HEARTBEAT); - printk(KERN_INFO PFX "heartbeat value must be 0udev. - */ -static void usb_pcwd_disconnect(struct usb_interface *interface) -{ - struct usb_pcwd_private *usb_pcwd; - - /* prevent races with open() */ - mutex_lock(&disconnect_mutex); - - usb_pcwd = usb_get_intfdata (interface); - usb_set_intfdata (interface, NULL); - - mutex_lock(&usb_pcwd->mtx); - - /* Stop the timer before we leave */ - if (!nowayout) - usb_pcwd_stop(usb_pcwd); - - /* We should now stop communicating with the USB PCWD device */ - usb_pcwd->exists = 0; - - /* Deregister */ - misc_deregister(&usb_pcwd_miscdev); - misc_deregister(&usb_pcwd_temperature_miscdev); - unregister_reboot_notifier(&usb_pcwd_notifier); - - mutex_unlock(&usb_pcwd->mtx); - - /* Delete the USB PCWD device */ - usb_pcwd_delete(usb_pcwd); - - cards_found--; - - mutex_unlock(&disconnect_mutex); - - printk(KERN_INFO PFX "USB PC Watchdog disconnected\n"); -} - - - -/** - * usb_pcwd_init - */ -static int __init usb_pcwd_init(void) -{ - int result; - - /* register this driver with the USB subsystem */ - result = usb_register(&usb_pcwd_driver); - if (result) { - printk(KERN_ERR PFX "usb_register failed. Error number %d\n", - result); - return result; - } - - printk(KERN_INFO PFX DRIVER_DESC " v" DRIVER_VERSION " (" DRIVER_DATE ")\n"); - return 0; -} - - -/** - * usb_pcwd_exit - */ -static void __exit usb_pcwd_exit(void) -{ - /* deregister this driver with the USB subsystem */ - usb_deregister(&usb_pcwd_driver); -} - - -module_init (usb_pcwd_init); -module_exit (usb_pcwd_exit); diff --git a/drivers/char/watchdog/pnx4008_wdt.c b/drivers/char/watchdog/pnx4008_wdt.c deleted file mode 100644 index 22f8873..0000000 --- a/drivers/char/watchdog/pnx4008_wdt.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * drivers/char/watchdog/pnx4008_wdt.c - * - * Watchdog driver for PNX4008 board - * - * Authors: Dmitry Chigirev , - * Vitaly Wool - * Based on sa1100 driver, - * Copyright (C) 2000 Oleg Drokin - * - * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define MODULE_NAME "PNX4008-WDT: " - -/* WatchDog Timer - Chapter 23 Page 207 */ - -#define DEFAULT_HEARTBEAT 19 -#define MAX_HEARTBEAT 60 - -/* Watchdog timer register set definition */ -#define WDTIM_INT(p) ((p) + 0x0) -#define WDTIM_CTRL(p) ((p) + 0x4) -#define WDTIM_COUNTER(p) ((p) + 0x8) -#define WDTIM_MCTRL(p) ((p) + 0xC) -#define WDTIM_MATCH0(p) ((p) + 0x10) -#define WDTIM_EMR(p) ((p) + 0x14) -#define WDTIM_PULSE(p) ((p) + 0x18) -#define WDTIM_RES(p) ((p) + 0x1C) - -/* WDTIM_INT bit definitions */ -#define MATCH_INT 1 - -/* WDTIM_CTRL bit definitions */ -#define COUNT_ENAB 1 -#define RESET_COUNT (1<<1) -#define DEBUG_EN (1<<2) - -/* WDTIM_MCTRL bit definitions */ -#define MR0_INT 1 -#undef RESET_COUNT0 -#define RESET_COUNT0 (1<<2) -#define STOP_COUNT0 (1<<2) -#define M_RES1 (1<<3) -#define M_RES2 (1<<4) -#define RESFRC1 (1<<5) -#define RESFRC2 (1<<6) - -/* WDTIM_EMR bit definitions */ -#define EXT_MATCH0 1 -#define MATCH_OUTPUT_HIGH (2<<4) /*a MATCH_CTRL setting */ - -/* WDTIM_RES bit definitions */ -#define WDOG_RESET 1 /* read only */ - -#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */ - -static int nowayout = WATCHDOG_NOWAYOUT; -static int heartbeat = DEFAULT_HEARTBEAT; - -static spinlock_t io_lock; -static unsigned long wdt_status; -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 -#define WDT_REGION_INITED 2 -#define WDT_DEVICE_INITED 3 - -static unsigned long boot_status; - -static struct resource *wdt_mem; -static void __iomem *wdt_base; -struct clk *wdt_clk; - -static void wdt_enable(void) -{ - spin_lock(&io_lock); - - if (wdt_clk) - clk_set_rate(wdt_clk, 1); - - /* stop counter, initiate counter reset */ - __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base)); - /*wait for reset to complete. 100% guarantee event */ - while (__raw_readl(WDTIM_COUNTER(wdt_base))) - cpu_relax(); - /* internal and external reset, stop after that */ - __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0, - WDTIM_MCTRL(wdt_base)); - /* configure match output */ - __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base)); - /* clear interrupt, just in case */ - __raw_writel(MATCH_INT, WDTIM_INT(wdt_base)); - /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */ - __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base)); - __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base)); - /*enable counter, stop when debugger active */ - __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base)); - - spin_unlock(&io_lock); -} - -static void wdt_disable(void) -{ - spin_lock(&io_lock); - - __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ - if (wdt_clk) - clk_set_rate(wdt_clk, 0); - - spin_unlock(&io_lock); -} - -static int pnx4008_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - wdt_enable(); - - return nonseekable_open(inode, file); -} - -static ssize_t -pnx4008_wdt_write(struct file *file, const char *data, size_t len, - loff_t * ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_enable(); - } - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "PNX4008 Watchdog", -}; - -static int -pnx4008_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - int time; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int *)arg); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); - if (ret) - break; - - if (time <= 0 || time > MAX_HEARTBEAT) { - ret = -EINVAL; - break; - } - - heartbeat = time; - wdt_enable(); - /* Fall through */ - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - } - return ret; -} - -static int pnx4008_wdt_release(struct inode *inode, struct file *file) -{ - if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n"); - - wdt_disable(); - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - -static const struct file_operations pnx4008_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = pnx4008_wdt_write, - .ioctl = pnx4008_wdt_ioctl, - .open = pnx4008_wdt_open, - .release = pnx4008_wdt_release, -}; - -static struct miscdevice pnx4008_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &pnx4008_wdt_fops, -}; - -static int pnx4008_wdt_probe(struct platform_device *pdev) -{ - int ret = 0, size; - struct resource *res; - - spin_lock_init(&io_lock); - - if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) - heartbeat = DEFAULT_HEARTBEAT; - - printk(KERN_INFO MODULE_NAME - "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - printk(KERN_INFO MODULE_NAME - "failed to get memory region resouce\n"); - return -ENOENT; - } - - size = res->end - res->start + 1; - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { - printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); - return -ENOENT; - } - wdt_base = (void __iomem *)IO_ADDRESS(res->start); - - wdt_clk = clk_get(&pdev->dev, "wdt_ck"); - if (IS_ERR(wdt_clk)) { - ret = PTR_ERR(wdt_clk); - release_resource(wdt_mem); - kfree(wdt_mem); - goto out; - } else - clk_set_rate(wdt_clk, 1); - - ret = misc_register(&pnx4008_wdt_miscdev); - if (ret < 0) { - printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); - clk_set_rate(wdt_clk, 0); - } else { - boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? - WDIOF_CARDRESET : 0; - wdt_disable(); /*disable for now */ - set_bit(WDT_DEVICE_INITED, &wdt_status); - } - -out: - return ret; -} - -static int pnx4008_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&pnx4008_wdt_miscdev); - if (wdt_clk) { - clk_set_rate(wdt_clk, 0); - clk_put(wdt_clk); - wdt_clk = NULL; - } - if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); - wdt_mem = NULL; - } - return 0; -} - -static struct platform_driver platform_wdt_driver = { - .driver = { - .name = "watchdog", - }, - .probe = pnx4008_wdt_probe, - .remove = pnx4008_wdt_remove, -}; - -static int __init pnx4008_wdt_init(void) -{ - return platform_driver_register(&platform_wdt_driver); -} - -static void __exit pnx4008_wdt_exit(void) -{ - return platform_driver_unregister(&platform_wdt_driver); -} - -module_init(pnx4008_wdt_init); -module_exit(pnx4008_wdt_exit); - -MODULE_AUTHOR("MontaVista Software, Inc. "); -MODULE_DESCRIPTION("PNX4008 Watchdog Driver"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, - "Watchdog heartbeat period in seconds from 1 to " - __MODULE_STRING(MAX_HEARTBEAT) ", default " - __MODULE_STRING(DEFAULT_HEARTBEAT)); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, - "Set to 1 to keep watchdog running after device release"); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/rm9k_wdt.c b/drivers/char/watchdog/rm9k_wdt.c deleted file mode 100644 index 5c921e4..0000000 --- a/drivers/char/watchdog/rm9k_wdt.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Watchdog implementation for GPI h/w found on PMC-Sierra RM9xxx - * chips. - * - * Copyright (C) 2004 by Basler Vision Technologies AG - * Author: Thomas Koeller - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -#define CLOCK 125000000 -#define MAX_TIMEOUT_SECONDS 32 -#define CPCCR 0x0080 -#define CPGIG1SR 0x0044 -#define CPGIG1ER 0x0054 - - -/* Function prototypes */ -static irqreturn_t wdt_gpi_irqhdl(int, void *); -static void wdt_gpi_start(void); -static void wdt_gpi_stop(void); -static void wdt_gpi_set_timeout(unsigned int); -static int wdt_gpi_open(struct inode *, struct file *); -static int wdt_gpi_release(struct inode *, struct file *); -static ssize_t wdt_gpi_write(struct file *, const char __user *, size_t, loff_t *); -static long wdt_gpi_ioctl(struct file *, unsigned int, unsigned long); -static int wdt_gpi_notify(struct notifier_block *, unsigned long, void *); -static const struct resource *wdt_gpi_get_resource(struct platform_device *, const char *, unsigned int); -static int __init wdt_gpi_probe(struct device *); -static int __exit wdt_gpi_remove(struct device *); - - -static const char wdt_gpi_name[] = "wdt_gpi"; -static atomic_t opencnt; -static int expect_close; -static int locked; - - -/* These are set from device resources */ -static void __iomem * wd_regs; -static unsigned int wd_irq, wd_ctr; - - -/* Module arguments */ -static int timeout = MAX_TIMEOUT_SECONDS; -module_param(timeout, int, 0444); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); - -static unsigned long resetaddr = 0xbffdc200; -module_param(resetaddr, ulong, 0444); -MODULE_PARM_DESC(resetaddr, "Address to write to to force a reset"); - -static unsigned long flagaddr = 0xbffdc104; -module_param(flagaddr, ulong, 0444); -MODULE_PARM_DESC(flagaddr, "Address to write to boot flags to"); - -static int powercycle; -module_param(powercycle, bool, 0444); -MODULE_PARM_DESC(powercycle, "Cycle power if watchdog expires"); - -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0444); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be disabled once started"); - - -/* Kernel interfaces */ -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = wdt_gpi_open, - .release = wdt_gpi_release, - .write = wdt_gpi_write, - .unlocked_ioctl = wdt_gpi_ioctl, -}; - -static struct miscdevice miscdev = { - .minor = WATCHDOG_MINOR, - .name = wdt_gpi_name, - .fops = &fops, -}; - -static struct notifier_block wdt_gpi_shutdown = { - .notifier_call = wdt_gpi_notify, -}; - - -/* Interrupt handler */ -static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt) -{ - if (!unlikely(__raw_readl(wd_regs + 0x0008) & 0x1)) - return IRQ_NONE; - __raw_writel(0x1, wd_regs + 0x0008); - - - printk(KERN_CRIT "%s: watchdog expired - resetting system\n", - wdt_gpi_name); - - *(volatile char *) flagaddr |= 0x01; - *(volatile char *) resetaddr = powercycle ? 0x01 : 0x2; - iob(); - while (1) - cpu_relax(); -} - - -/* Watchdog functions */ -static void wdt_gpi_start(void) -{ - u32 reg; - - lock_titan_regs(); - reg = titan_readl(CPGIG1ER); - titan_writel(reg | (0x100 << wd_ctr), CPGIG1ER); - iob(); - unlock_titan_regs(); -} - -static void wdt_gpi_stop(void) -{ - u32 reg; - - lock_titan_regs(); - reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4)); - titan_writel(reg, CPCCR); - reg = titan_readl(CPGIG1ER); - titan_writel(reg & ~(0x100 << wd_ctr), CPGIG1ER); - iob(); - unlock_titan_regs(); -} - -static void wdt_gpi_set_timeout(unsigned int to) -{ - u32 reg; - const u32 wdval = (to * CLOCK) & ~0x0000000f; - - lock_titan_regs(); - reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4)); - titan_writel(reg, CPCCR); - wmb(); - __raw_writel(wdval, wd_regs + 0x0000); - wmb(); - titan_writel(reg | (0x2 << (wd_ctr * 4)), CPCCR); - wmb(); - titan_writel(reg | (0x5 << (wd_ctr * 4)), CPCCR); - iob(); - unlock_titan_regs(); -} - - -/* /dev/watchdog operations */ -static int wdt_gpi_open(struct inode *inode, struct file *file) -{ - int res; - - if (unlikely(atomic_dec_if_positive(&opencnt) < 0)) - return -EBUSY; - - expect_close = 0; - if (locked) { - module_put(THIS_MODULE); - free_irq(wd_irq, &miscdev); - locked = 0; - } - - res = request_irq(wd_irq, wdt_gpi_irqhdl, IRQF_SHARED | IRQF_DISABLED, - wdt_gpi_name, &miscdev); - if (unlikely(res)) - return res; - - wdt_gpi_set_timeout(timeout); - wdt_gpi_start(); - - printk(KERN_INFO "%s: watchdog started, timeout = %u seconds\n", - wdt_gpi_name, timeout); - return nonseekable_open(inode, file); -} - -static int wdt_gpi_release(struct inode *inode, struct file *file) -{ - if (nowayout) { - printk(KERN_INFO "%s: no way out - watchdog left running\n", - wdt_gpi_name); - __module_get(THIS_MODULE); - locked = 1; - } else { - if (expect_close) { - wdt_gpi_stop(); - free_irq(wd_irq, &miscdev); - printk(KERN_INFO "%s: watchdog stopped\n", wdt_gpi_name); - } else { - printk(KERN_CRIT "%s: unexpected close() -" - " watchdog left running\n", - wdt_gpi_name); - wdt_gpi_set_timeout(timeout); - __module_get(THIS_MODULE); - locked = 1; - } - } - - atomic_inc(&opencnt); - return 0; -} - -static ssize_t -wdt_gpi_write(struct file *f, const char __user *d, size_t s, loff_t *o) -{ - char val; - - wdt_gpi_set_timeout(timeout); - expect_close = (s > 0) && !get_user(val, d) && (val == 'V'); - return s ? 1 : 0; -} - -static long -wdt_gpi_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - long res = -ENOTTY; - const long size = _IOC_SIZE(cmd); - int stat; - void __user *argp = (void __user *)arg; - static struct watchdog_info wdinfo = { - .identity = "RM9xxx/GPI watchdog", - .firmware_version = 0, - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING - }; - - if (unlikely(_IOC_TYPE(cmd) != WATCHDOG_IOCTL_BASE)) - return -ENOTTY; - - if ((_IOC_DIR(cmd) & _IOC_READ) - && !access_ok(VERIFY_WRITE, arg, size)) - return -EFAULT; - - if ((_IOC_DIR(cmd) & _IOC_WRITE) - && !access_ok(VERIFY_READ, arg, size)) - return -EFAULT; - - expect_close = 0; - - switch (cmd) { - case WDIOC_GETSUPPORT: - wdinfo.options = nowayout ? - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING : - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE; - res = __copy_to_user(argp, &wdinfo, size) ? -EFAULT : size; - break; - - case WDIOC_GETSTATUS: - break; - - case WDIOC_GETBOOTSTATUS: - stat = (*(volatile char *) flagaddr & 0x01) - ? WDIOF_CARDRESET : 0; - res = __copy_to_user(argp, &stat, size) ? - -EFAULT : size; - break; - - case WDIOC_SETOPTIONS: - break; - - case WDIOC_KEEPALIVE: - wdt_gpi_set_timeout(timeout); - res = size; - break; - - case WDIOC_SETTIMEOUT: - { - int val; - if (unlikely(__copy_from_user(&val, argp, size))) { - res = -EFAULT; - break; - } - - if (val > MAX_TIMEOUT_SECONDS) - val = MAX_TIMEOUT_SECONDS; - timeout = val; - wdt_gpi_set_timeout(val); - res = size; - printk(KERN_INFO "%s: timeout set to %u seconds\n", - wdt_gpi_name, timeout); - } - break; - - case WDIOC_GETTIMEOUT: - res = __copy_to_user(argp, &timeout, size) ? - -EFAULT : size; - break; - } - - return res; -} - - -/* Shutdown notifier */ -static int -wdt_gpi_notify(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - wdt_gpi_stop(); - - return NOTIFY_DONE; -} - - -/* Init & exit procedures */ -static const struct resource * -wdt_gpi_get_resource(struct platform_device *pdv, const char *name, - unsigned int type) -{ - char buf[80]; - if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf) - return NULL; - return platform_get_resource_byname(pdv, type, buf); -} - -/* No hotplugging on the platform bus - use __init */ -static int __init wdt_gpi_probe(struct device *dev) -{ - int res; - struct platform_device * const pdv = to_platform_device(dev); - const struct resource - * const rr = wdt_gpi_get_resource(pdv, WDT_RESOURCE_REGS, - IORESOURCE_MEM), - * const ri = wdt_gpi_get_resource(pdv, WDT_RESOURCE_IRQ, - IORESOURCE_IRQ), - * const rc = wdt_gpi_get_resource(pdv, WDT_RESOURCE_COUNTER, - 0); - - if (unlikely(!rr || !ri || !rc)) - return -ENXIO; - - wd_regs = ioremap_nocache(rr->start, rr->end + 1 - rr->start); - if (unlikely(!wd_regs)) - return -ENOMEM; - wd_irq = ri->start; - wd_ctr = rc->start; - res = misc_register(&miscdev); - if (res) - iounmap(wd_regs); - else - register_reboot_notifier(&wdt_gpi_shutdown); - return res; -} - -static int __exit wdt_gpi_remove(struct device *dev) -{ - int res; - - unregister_reboot_notifier(&wdt_gpi_shutdown); - res = misc_deregister(&miscdev); - iounmap(wd_regs); - wd_regs = NULL; - return res; -} - - -/* Device driver init & exit */ -static struct device_driver wdt_gpi_driver = { - .name = (char *) wdt_gpi_name, - .bus = &platform_bus_type, - .owner = THIS_MODULE, - .probe = wdt_gpi_probe, - .remove = __exit_p(wdt_gpi_remove), - .shutdown = NULL, - .suspend = NULL, - .resume = NULL, -}; - -static int __init wdt_gpi_init_module(void) -{ - atomic_set(&opencnt, 1); - if (timeout > MAX_TIMEOUT_SECONDS) - timeout = MAX_TIMEOUT_SECONDS; - return driver_register(&wdt_gpi_driver); -} - -static void __exit wdt_gpi_cleanup_module(void) -{ - driver_unregister(&wdt_gpi_driver); -} - -module_init(wdt_gpi_init_module); -module_exit(wdt_gpi_cleanup_module); - -MODULE_AUTHOR("Thomas Koeller "); -MODULE_DESCRIPTION("Basler eXcite watchdog driver for gpi devices"); -MODULE_VERSION("0.1"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c deleted file mode 100644 index 5d1c15f..0000000 --- a/drivers/char/watchdog/s3c2410_wdt.c +++ /dev/null @@ -1,563 +0,0 @@ -/* linux/drivers/char/watchdog/s3c2410_wdt.c - * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks - * - * S3C2410 Watchdog Timer Support - * - * Based on, softdog.c by Alan Cox, - * (c) Copyright 1996 Alan Cox - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Changelog: - * 05-Oct-2004 BJD Added semaphore init to stop crashes on open - * Fixed tmr_count / wdt_count confusion - * Added configurable debug - * - * 11-Jan-2005 BJD Fixed divide-by-2 in timeout code - * - * 25-Jan-2005 DA Added suspend/resume support - * Replaced reboot notifier with .shutdown method - * - * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#undef S3C_VA_WATCHDOG -#define S3C_VA_WATCHDOG (0) - -#include - -#define PFX "s3c2410-wdt: " - -#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) -#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) - -static int nowayout = WATCHDOG_NOWAYOUT; -static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME; -static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; -static int soft_noboot = 0; -static int debug = 0; - -module_param(tmr_margin, int, 0); -module_param(tmr_atboot, int, 0); -module_param(nowayout, int, 0); -module_param(soft_noboot, int, 0); -module_param(debug, int, 0); - -MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default=" __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); - -MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default=" __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT)); - -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default depends on ONLY_TESTING)"); - -MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)"); - - -typedef enum close_state { - CLOSE_STATE_NOT, - CLOSE_STATE_ALLOW=0x4021 -} close_state_t; - -static DECLARE_MUTEX(open_lock); - -static struct device *wdt_dev; /* platform device attached to */ -static struct resource *wdt_mem; -static struct resource *wdt_irq; -static struct clk *wdt_clock; -static void __iomem *wdt_base; -static unsigned int wdt_count; -static close_state_t allow_close; - -/* watchdog control routines */ - -#define DBG(msg...) do { \ - if (debug) \ - printk(KERN_INFO msg); \ - } while(0) - -/* functions */ - -static int s3c2410wdt_keepalive(void) -{ - writel(wdt_count, wdt_base + S3C2410_WTCNT); - return 0; -} - -static int s3c2410wdt_stop(void) -{ - unsigned long wtcon; - - wtcon = readl(wdt_base + S3C2410_WTCON); - wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); - writel(wtcon, wdt_base + S3C2410_WTCON); - - return 0; -} - -static int s3c2410wdt_start(void) -{ - unsigned long wtcon; - - s3c2410wdt_stop(); - - wtcon = readl(wdt_base + S3C2410_WTCON); - wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128; - - if (soft_noboot) { - wtcon |= S3C2410_WTCON_INTEN; - wtcon &= ~S3C2410_WTCON_RSTEN; - } else { - wtcon &= ~S3C2410_WTCON_INTEN; - wtcon |= S3C2410_WTCON_RSTEN; - } - - DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n", - __FUNCTION__, wdt_count, wtcon); - - writel(wdt_count, wdt_base + S3C2410_WTDAT); - writel(wdt_count, wdt_base + S3C2410_WTCNT); - writel(wtcon, wdt_base + S3C2410_WTCON); - - return 0; -} - -static int s3c2410wdt_set_heartbeat(int timeout) -{ - unsigned int freq = clk_get_rate(wdt_clock); - unsigned int count; - unsigned int divisor = 1; - unsigned long wtcon; - - if (timeout < 1) - return -EINVAL; - - freq /= 128; - count = timeout * freq; - - DBG("%s: count=%d, timeout=%d, freq=%d\n", - __FUNCTION__, count, timeout, freq); - - /* if the count is bigger than the watchdog register, - then work out what we need to do (and if) we can - actually make this value - */ - - if (count >= 0x10000) { - for (divisor = 1; divisor <= 0x100; divisor++) { - if ((count / divisor) < 0x10000) - break; - } - - if ((count / divisor) >= 0x10000) { - dev_err(wdt_dev, "timeout %d too big\n", timeout); - return -EINVAL; - } - } - - tmr_margin = timeout; - - DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", - __FUNCTION__, timeout, divisor, count, count/divisor); - - count /= divisor; - wdt_count = count; - - /* update the pre-scaler */ - wtcon = readl(wdt_base + S3C2410_WTCON); - wtcon &= ~S3C2410_WTCON_PRESCALE_MASK; - wtcon |= S3C2410_WTCON_PRESCALE(divisor-1); - - writel(count, wdt_base + S3C2410_WTDAT); - writel(wtcon, wdt_base + S3C2410_WTCON); - - return 0; -} - -/* - * /dev/watchdog handling - */ - -static int s3c2410wdt_open(struct inode *inode, struct file *file) -{ - if(down_trylock(&open_lock)) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - allow_close = CLOSE_STATE_NOT; - - /* start the timer */ - s3c2410wdt_start(); - return nonseekable_open(inode, file); -} - -static int s3c2410wdt_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - * Lock it in if it's a module and we set nowayout - */ - - if (allow_close == CLOSE_STATE_ALLOW) { - s3c2410wdt_stop(); - } else { - dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n"); - s3c2410wdt_keepalive(); - } - - allow_close = CLOSE_STATE_NOT; - up(&open_lock); - return 0; -} - -static ssize_t s3c2410wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* - * Refresh the timer. - */ - if(len) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - allow_close = CLOSE_STATE_NOT; - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - allow_close = CLOSE_STATE_ALLOW; - } - } - - s3c2410wdt_keepalive(); - } - return len; -} - -#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE - -static struct watchdog_info s3c2410_wdt_ident = { - .options = OPTIONS, - .firmware_version = 0, - .identity = "S3C2410 Watchdog", -}; - - -static int s3c2410wdt_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_margin; - - switch (cmd) { - default: - return -ENOTTY; - - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &s3c2410_wdt_ident, - sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_KEEPALIVE: - s3c2410wdt_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, p)) - return -EFAULT; - - if (s3c2410wdt_set_heartbeat(new_margin)) - return -EINVAL; - - s3c2410wdt_keepalive(); - return put_user(tmr_margin, p); - - case WDIOC_GETTIMEOUT: - return put_user(tmr_margin, p); - } -} - -/* kernel interface */ - -static const struct file_operations s3c2410wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = s3c2410wdt_write, - .ioctl = s3c2410wdt_ioctl, - .open = s3c2410wdt_open, - .release = s3c2410wdt_release, -}; - -static struct miscdevice s3c2410wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &s3c2410wdt_fops, -}; - -/* interrupt handler code */ - -static irqreturn_t s3c2410wdt_irq(int irqno, void *param) -{ - dev_info(wdt_dev, "watchdog timer expired (irq)\n"); - - s3c2410wdt_keepalive(); - return IRQ_HANDLED; -} -/* device interface */ - -static int s3c2410wdt_probe(struct platform_device *pdev) -{ - struct resource *res; - struct device *dev; - unsigned int wtcon; - int started = 0; - int ret; - int size; - - DBG("%s: probe=%p\n", __FUNCTION__, pdev); - - dev = &pdev->dev; - wdt_dev = &pdev->dev; - - /* get the memory region for the watchdog timer */ - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(dev, "no memory resource specified\n"); - return -ENOENT; - } - - size = (res->end-res->start)+1; - wdt_mem = request_mem_region(res->start, size, pdev->name); - if (wdt_mem == NULL) { - dev_err(dev, "failed to get memory region\n"); - ret = -ENOENT; - goto err_req; - } - - wdt_base = ioremap(res->start, size); - if (wdt_base == 0) { - dev_err(dev, "failed to ioremap() region\n"); - ret = -EINVAL; - goto err_req; - } - - DBG("probe: mapped wdt_base=%p\n", wdt_base); - - wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (wdt_irq == NULL) { - dev_err(dev, "no irq resource specified\n"); - ret = -ENOENT; - goto err_map; - } - - ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev); - if (ret != 0) { - dev_err(dev, "failed to install irq (%d)\n", ret); - goto err_map; - } - - wdt_clock = clk_get(&pdev->dev, "watchdog"); - if (IS_ERR(wdt_clock)) { - dev_err(dev, "failed to find watchdog clock source\n"); - ret = PTR_ERR(wdt_clock); - goto err_irq; - } - - clk_enable(wdt_clock); - - /* see if we can actually set the requested timer margin, and if - * not, try the default value */ - - if (s3c2410wdt_set_heartbeat(tmr_margin)) { - started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); - - if (started == 0) { - dev_info(dev,"tmr_margin value out of range, default %d used\n", - CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); - } else { - dev_info(dev, "default timer value is out of range, cannot start\n"); - } - } - - ret = misc_register(&s3c2410wdt_miscdev); - if (ret) { - dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", - WATCHDOG_MINOR, ret); - goto err_clk; - } - - if (tmr_atboot && started == 0) { - dev_info(dev, "starting watchdog timer\n"); - s3c2410wdt_start(); - } else if (!tmr_atboot) { - /* if we're not enabling the watchdog, then ensure it is - * disabled if it has been left running from the bootloader - * or other source */ - - s3c2410wdt_stop(); - } - - /* print out a statement of readiness */ - - wtcon = readl(wdt_base + S3C2410_WTCON); - - dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n", - (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in", - (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis", - (wtcon & S3C2410_WTCON_INTEN) ? "" : "en"); - - return 0; - - err_clk: - clk_disable(wdt_clock); - clk_put(wdt_clock); - - err_irq: - free_irq(wdt_irq->start, pdev); - - err_map: - iounmap(wdt_base); - - err_req: - release_resource(wdt_mem); - kfree(wdt_mem); - - return ret; -} - -static int s3c2410wdt_remove(struct platform_device *dev) -{ - release_resource(wdt_mem); - kfree(wdt_mem); - wdt_mem = NULL; - - free_irq(wdt_irq->start, dev); - wdt_irq = NULL; - - clk_disable(wdt_clock); - clk_put(wdt_clock); - wdt_clock = NULL; - - iounmap(wdt_base); - misc_deregister(&s3c2410wdt_miscdev); - return 0; -} - -static void s3c2410wdt_shutdown(struct platform_device *dev) -{ - s3c2410wdt_stop(); -} - -#ifdef CONFIG_PM - -static unsigned long wtcon_save; -static unsigned long wtdat_save; - -static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state) -{ - /* Save watchdog state, and turn it off. */ - wtcon_save = readl(wdt_base + S3C2410_WTCON); - wtdat_save = readl(wdt_base + S3C2410_WTDAT); - - /* Note that WTCNT doesn't need to be saved. */ - s3c2410wdt_stop(); - - return 0; -} - -static int s3c2410wdt_resume(struct platform_device *dev) -{ - /* Restore watchdog state. */ - - writel(wtdat_save, wdt_base + S3C2410_WTDAT); - writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */ - writel(wtcon_save, wdt_base + S3C2410_WTCON); - - printk(KERN_INFO PFX "watchdog %sabled\n", - (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); - - return 0; -} - -#else -#define s3c2410wdt_suspend NULL -#define s3c2410wdt_resume NULL -#endif /* CONFIG_PM */ - - -static struct platform_driver s3c2410wdt_driver = { - .probe = s3c2410wdt_probe, - .remove = s3c2410wdt_remove, - .shutdown = s3c2410wdt_shutdown, - .suspend = s3c2410wdt_suspend, - .resume = s3c2410wdt_resume, - .driver = { - .owner = THIS_MODULE, - .name = "s3c2410-wdt", - }, -}; - - -static char banner[] __initdata = KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n"; - -static int __init watchdog_init(void) -{ - printk(banner); - return platform_driver_register(&s3c2410wdt_driver); -} - -static void __exit watchdog_exit(void) -{ - platform_driver_unregister(&s3c2410wdt_driver); -} - -module_init(watchdog_init); -module_exit(watchdog_exit); - -MODULE_AUTHOR("Ben Dooks , " - "Dimitry Andric "); -MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/sa1100_wdt.c b/drivers/char/watchdog/sa1100_wdt.c deleted file mode 100644 index 3475f47..0000000 --- a/drivers/char/watchdog/sa1100_wdt.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Watchdog driver for the SA11x0/PXA2xx - * - * (c) Copyright 2000 Oleg Drokin - * Based on SoftDog driver by Alan Cox - * - * 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 Oleg Drokin nor iXcelerator.com admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 2000 Oleg Drokin - * - * 27/11/2000 Initial release - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_ARCH_PXA -#include -#endif - -#include -#include -#include - -#define OSCR_FREQ CLOCK_TICK_RATE - -static unsigned long sa1100wdt_users; -static int pre_margin; -static int boot_status; - -/* - * Allow only one person to hold it open - */ -static int sa1100dog_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(1,&sa1100wdt_users)) - return -EBUSY; - - /* Activate SA1100 Watchdog timer */ - OSMR3 = OSCR + pre_margin; - OSSR = OSSR_M3; - OWER = OWER_WME; - OIER |= OIER_E3; - return nonseekable_open(inode, file); -} - -/* - * The watchdog cannot be disabled. - * - * Previous comments suggested that turning off the interrupt by - * clearing OIER[E3] would prevent the watchdog timing out but this - * does not appear to be true (at least on the PXA255). - */ -static int sa1100dog_release(struct inode *inode, struct file *file) -{ - printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n"); - - clear_bit(1, &sa1100wdt_users); - - return 0; -} - -static ssize_t sa1100dog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) -{ - if (len) - /* Refresh OSMR3 timer. */ - OSMR3 = OSCR + pre_margin; - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "SA1100/PXA255 Watchdog", -}; - -static int sa1100dog_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret = -ENOTTY; - int time; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user(argp, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, p); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, p); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, p); - if (ret) - break; - - if (time <= 0 || time > 255) { - ret = -EINVAL; - break; - } - - pre_margin = OSCR_FREQ * time; - OSMR3 = OSCR + pre_margin; - /*fall through*/ - - case WDIOC_GETTIMEOUT: - ret = put_user(pre_margin / OSCR_FREQ, p); - break; - - case WDIOC_KEEPALIVE: - OSMR3 = OSCR + pre_margin; - ret = 0; - break; - } - return ret; -} - -static const struct file_operations sa1100dog_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = sa1100dog_write, - .ioctl = sa1100dog_ioctl, - .open = sa1100dog_open, - .release = sa1100dog_release, -}; - -static struct miscdevice sa1100dog_miscdev = -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sa1100dog_fops, -}; - -static int margin __initdata = 60; /* (secs) Default is 1 minute */ - -static int __init sa1100dog_init(void) -{ - int ret; - - /* - * Read the reset status, and save it for later. If - * we suspend, RCSR will be cleared, and the watchdog - * reset reason will be lost. - */ - boot_status = (RCSR & RCSR_WDR) ? WDIOF_CARDRESET : 0; - pre_margin = OSCR_FREQ * margin; - - ret = misc_register(&sa1100dog_miscdev); - if (ret == 0) - printk("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", - margin); - return ret; -} - -static void __exit sa1100dog_exit(void) -{ - misc_deregister(&sa1100dog_miscdev); -} - -module_init(sa1100dog_init); -module_exit(sa1100dog_exit); - -MODULE_AUTHOR("Oleg Drokin "); -MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog"); - -module_param(margin, int, 0); -MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/sbc60xxwdt.c b/drivers/char/watchdog/sbc60xxwdt.c deleted file mode 100644 index e4f3cb6..0000000 --- a/drivers/char/watchdog/sbc60xxwdt.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * 60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x - * - * Based on acquirewdt.c by Alan Cox. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * The author does 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 2000 Jakob Oestergaard - * - * 12/4 - 2000 [Initial revision] - * 25/4 - 2000 Added /dev/watchdog support - * 09/5 - 2001 [smj@oro.net] fixed fop_write to "return 1" on success - * 12/4 - 2002 [rob@osinvestor.com] eliminate fop_read - * fix possible wdt_is_open race - * add CONFIG_WATCHDOG_NOWAYOUT support - * remove lock_kernel/unlock_kernel pairs - * added KERN_* to printk's - * got rid of extraneous comments - * changed watchdog_info to correctly reflect what the driver offers - * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, WDIOC_SETTIMEOUT, - * WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls - * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces - * use module_param - * made timeout (the emulated heartbeat) a module_param - * made the keepalive ping an internal subroutine - * made wdt_stop and wdt_start module params - * added extra printk's for startup problems - * added MODULE_AUTHOR and MODULE_DESCRIPTION info - * - * - * This WDT driver is different from the other Linux WDT - * drivers in the following ways: - * *) The driver will ping the watchdog by itself, because this - * particular WDT has a very short timeout (one second) and it - * would be insane to count on any userspace daemon always - * getting scheduled within that time frame. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define OUR_NAME "sbc60xxwdt" -#define PFX OUR_NAME ": " - -/* - * You must set these - The driver cannot probe for the settings - */ - -static int wdt_stop = 0x45; -module_param(wdt_stop, int, 0); -MODULE_PARM_DESC(wdt_stop, "SBC60xx WDT 'stop' io port (default 0x45)"); - -static int wdt_start = 0x443; -module_param(wdt_start, int, 0); -MODULE_PARM_DESC(wdt_start, "SBC60xx WDT 'start' io port (default 0x443)"); - -/* - * The 60xx board can use watchdog timeout values from one second - * to several minutes. The default is one second, so if we reset - * the watchdog every ~250ms we should be safe. - */ - -#define WDT_INTERVAL (HZ/4+1) - -/* - * We must not require too good response from the userspace daemon. - * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 30 seconds. - * If the daemon pulses us every 25 seconds, we can still afford - * a 5 second scheduling delay on the (high priority) daemon. That - * should be sufficient for a box under any load. - */ - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, 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 void wdt_timer_ping(unsigned long); -static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); -static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static char wdt_expect_close; - -/* - * Whack the dog - */ - -static void wdt_timer_ping(unsigned long data) -{ - /* If we got a heartbeat pulse within the WDT_US_INTERVAL - * we agree to ping the WDT - */ - if(time_before(jiffies, next_heartbeat)) - { - /* Ping the WDT by reading from wdt_start */ - inb_p(wdt_start); - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } -} - -/* - * Utility routines - */ - -static void wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - /* Start the timer */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); -} - -static void wdt_turnoff(void) -{ - /* Stop the timer */ - del_timer(&timer); - inb_p(wdt_stop); - printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); -} - -static void wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); -} - -/* - * /dev/watchdog handling - */ - -static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if(count) - { - if (!nowayout) - { - size_t ofs; - - /* note: just in case someone wrote the magic character - * five months ago... */ - wdt_expect_close = 0; - - /* scan to see whether or not we got the magic character */ - for(ofs = 0; ofs != count; ofs++) - { - char c; - if(get_user(c, buf+ofs)) - return -EFAULT; - if(c == 'V') - wdt_expect_close = 42; - } - } - - /* Well, anyhow someone wrote to us, we should return that favour */ - wdt_keepalive(); - } - return count; -} - -static int fop_open(struct inode * inode, struct file * file) -{ - /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - /* Good, fire up the show */ - wdt_startup(); - return nonseekable_open(inode, file); -} - -static int fop_close(struct inode * inode, struct file * file) -{ - if(wdt_expect_close == 42) - wdt_turnoff(); - else { - del_timer(&timer); - printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n"); - } - clear_bit(0, &wdt_is_open); - wdt_expect_close = 0; - return 0; -} - -static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident= - { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "SBC60xx", - }; - - switch(cmd) - { - default: - return -ENOTTY; - 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: - wdt_keepalive(); - return 0; - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if(get_user(new_options, p)) - return -EFAULT; - - if(new_options & WDIOS_DISABLECARD) { - wdt_turnoff(); - retval = 0; - } - - if(new_options & WDIOS_ENABLECARD) { - wdt_startup(); - retval = 0; - } - - return retval; - } - case WDIOC_SETTIMEOUT: - { - int new_timeout; - - if(get_user(new_timeout, p)) - return -EFAULT; - - if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ - return -EINVAL; - - timeout = new_timeout; - wdt_keepalive(); - /* Fall through */ - } - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - } -} - -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = fop_write, - .open = fop_open, - .release = fop_close, - .ioctl = fop_ioctl, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -/* - * Notifier for system down - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code==SYS_DOWN || code==SYS_HALT) - wdt_turnoff(); - return NOTIFY_DONE; -} - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdt_notifier= -{ - .notifier_call = wdt_notify_sys, -}; - -static void __exit sbc60xxwdt_unload(void) -{ - wdt_turnoff(); - - /* Deregister */ - misc_deregister(&wdt_miscdev); - - unregister_reboot_notifier(&wdt_notifier); - if ((wdt_stop != 0x45) && (wdt_stop != wdt_start)) - release_region(wdt_stop,1); - release_region(wdt_start,1); -} - -static int __init sbc60xxwdt_init(void) -{ - int rc = -EBUSY; - - if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ - { - timeout = WATCHDOG_TIMEOUT; - printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", - timeout); - } - - if (!request_region(wdt_start, 1, "SBC 60XX WDT")) - { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_start); - rc = -EIO; - goto err_out; - } - - /* We cannot reserve 0x45 - the kernel already has! */ - if ((wdt_stop != 0x45) && (wdt_stop != wdt_start)) - { - if (!request_region(wdt_stop, 1, "SBC 60XX WDT")) - { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_stop); - rc = -EIO; - goto err_out_region1; - } - } - - rc = misc_register(&wdt_miscdev); - if (rc) - { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - wdt_miscdev.minor, rc); - goto err_out_region2; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) - { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - rc); - goto err_out_miscdev; - } - - printk(KERN_INFO PFX "WDT driver for 60XX single board computer initialised. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - - return 0; - -err_out_miscdev: - misc_deregister(&wdt_miscdev); -err_out_region2: - if ((wdt_stop != 0x45) && (wdt_stop != wdt_start)) - release_region(wdt_stop,1); -err_out_region1: - release_region(wdt_start,1); -err_out: - return rc; -} - -module_init(sbc60xxwdt_init); -module_exit(sbc60xxwdt_unload); - -MODULE_AUTHOR("Jakob Oestergaard "); -MODULE_DESCRIPTION("60xx Single Board Computer Watchdog Timer driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/sbc8360.c b/drivers/char/watchdog/sbc8360.c deleted file mode 100644 index 285d852..0000000 --- a/drivers/char/watchdog/sbc8360.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * SBC8360 Watchdog driver - * - * (c) Copyright 2005 Webcon, Inc. - * - * Based on ib700wdt.c, which is based on advantechwdt.c which is based - * on acquirewdt.c which is based on wdt.c. - * - * (c) Copyright 2001 Charles Howes - * - * Based on advantechwdt.c which is based on acquirewdt.c which - * is based on wdt.c. - * - * (c) Copyright 2000-2001 Marek Michalkiewicz - * - * Based on acquirewdt.c which is based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * Added timeout module option to override default - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static unsigned long sbc8360_is_open; -static spinlock_t sbc8360_lock; -static char expect_close; - -#define PFX "sbc8360: " - -/* - * - * Watchdog Timer Configuration - * - * The function of the watchdog timer is to reset the system automatically - * and is defined at I/O port 0120H and 0121H. To enable the watchdog timer - * and allow the system to reset, write appropriate values from the table - * below to I/O port 0120H and 0121H. To disable the timer, write a zero - * value to I/O port 0121H for the system to stop the watchdog function. - * - * The following describes how the timer should be programmed (according to - * the vendor documentation) - * - * Enabling Watchdog: - * MOV AX,000AH (enable, phase I) - * MOV DX,0120H - * OUT DX,AX - * MOV AX,000BH (enable, phase II) - * MOV DX,0120H - * OUT DX,AX - * MOV AX,000nH (set multiplier n, from 1-4) - * MOV DX,0120H - * OUT DX,AX - * MOV AX,000mH (set base timer m, from 0-F) - * MOV DX,0121H - * OUT DX,AX - * - * Reset timer: - * MOV AX,000mH (same as set base timer, above) - * MOV DX,0121H - * OUT DX,AX - * - * Disabling Watchdog: - * MOV AX,0000H (a zero value) - * MOV DX,0120H - * OUT DX,AX - * - * Watchdog timeout configuration values: - * N - * M | 1 2 3 4 - * --|---------------------------------- - * 0 | 0.5s 5s 50s 100s - * 1 | 1s 10s 100s 200s - * 2 | 1.5s 15s 150s 300s - * 3 | 2s 20s 200s 400s - * 4 | 2.5s 25s 250s 500s - * 5 | 3s 30s 300s 600s - * 6 | 3.5s 35s 350s 700s - * 7 | 4s 40s 400s 800s - * 8 | 4.5s 45s 450s 900s - * 9 | 5s 50s 500s 1000s - * A | 5.5s 55s 550s 1100s - * B | 6s 60s 600s 1200s - * C | 6.5s 65s 650s 1300s - * D | 7s 70s 700s 1400s - * E | 7.5s 75s 750s 1500s - * F | 8s 80s 800s 1600s - * - * Another way to say the same things is: - * For N=1, Timeout = (M+1) * 0.5s - * For N=2, Timeout = (M+1) * 5s - * For N=3, Timeout = (M+1) * 50s - * For N=4, Timeout = (M+1) * 100s - * - */ - -static int wd_times[64][2] = { - {0, 1}, /* 0 = 0.5s */ - {1, 1}, /* 1 = 1s */ - {2, 1}, /* 2 = 1.5s */ - {3, 1}, /* 3 = 2s */ - {4, 1}, /* 4 = 2.5s */ - {5, 1}, /* 5 = 3s */ - {6, 1}, /* 6 = 3.5s */ - {7, 1}, /* 7 = 4s */ - {8, 1}, /* 8 = 4.5s */ - {9, 1}, /* 9 = 5s */ - {0xA, 1}, /* 10 = 5.5s */ - {0xB, 1}, /* 11 = 6s */ - {0xC, 1}, /* 12 = 6.5s */ - {0xD, 1}, /* 13 = 7s */ - {0xE, 1}, /* 14 = 7.5s */ - {0xF, 1}, /* 15 = 8s */ - {0, 2}, /* 16 = 5s */ - {1, 2}, /* 17 = 10s */ - {2, 2}, /* 18 = 15s */ - {3, 2}, /* 19 = 20s */ - {4, 2}, /* 20 = 25s */ - {5, 2}, /* 21 = 30s */ - {6, 2}, /* 22 = 35s */ - {7, 2}, /* 23 = 40s */ - {8, 2}, /* 24 = 45s */ - {9, 2}, /* 25 = 50s */ - {0xA, 2}, /* 26 = 55s */ - {0xB, 2}, /* 27 = 60s */ - {0xC, 2}, /* 28 = 65s */ - {0xD, 2}, /* 29 = 70s */ - {0xE, 2}, /* 30 = 75s */ - {0xF, 2}, /* 31 = 80s */ - {0, 3}, /* 32 = 50s */ - {1, 3}, /* 33 = 100s */ - {2, 3}, /* 34 = 150s */ - {3, 3}, /* 35 = 200s */ - {4, 3}, /* 36 = 250s */ - {5, 3}, /* 37 = 300s */ - {6, 3}, /* 38 = 350s */ - {7, 3}, /* 39 = 400s */ - {8, 3}, /* 40 = 450s */ - {9, 3}, /* 41 = 500s */ - {0xA, 3}, /* 42 = 550s */ - {0xB, 3}, /* 43 = 600s */ - {0xC, 3}, /* 44 = 650s */ - {0xD, 3}, /* 45 = 700s */ - {0xE, 3}, /* 46 = 750s */ - {0xF, 3}, /* 47 = 800s */ - {0, 4}, /* 48 = 100s */ - {1, 4}, /* 49 = 200s */ - {2, 4}, /* 50 = 300s */ - {3, 4}, /* 51 = 400s */ - {4, 4}, /* 52 = 500s */ - {5, 4}, /* 53 = 600s */ - {6, 4}, /* 54 = 700s */ - {7, 4}, /* 55 = 800s */ - {8, 4}, /* 56 = 900s */ - {9, 4}, /* 57 = 1000s */ - {0xA, 4}, /* 58 = 1100s */ - {0xB, 4}, /* 59 = 1200s */ - {0xC, 4}, /* 60 = 1300s */ - {0xD, 4}, /* 61 = 1400s */ - {0xE, 4}, /* 62 = 1500s */ - {0xF, 4} /* 63 = 1600s */ -}; - -#define SBC8360_ENABLE 0x120 -#define SBC8360_BASETIME 0x121 - -static int timeout = 27; -static int wd_margin = 0xB; -static int wd_multiplier = 2; -static int nowayout = WATCHDOG_NOWAYOUT; - -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))"); -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, - "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -/* - * Kernel methods. - */ - -/* Activate and pre-configure watchdog */ -static void sbc8360_activate(void) -{ - /* Enable the watchdog */ - outb(0x0A, SBC8360_ENABLE); - msleep_interruptible(100); - outb(0x0B, SBC8360_ENABLE); - msleep_interruptible(100); - /* Set timeout multiplier */ - outb(wd_multiplier, SBC8360_ENABLE); - msleep_interruptible(100); - /* Nothing happens until first sbc8360_ping() */ -} - -/* Kernel pings watchdog */ -static void sbc8360_ping(void) -{ - /* Write the base timer register */ - outb(wd_margin, SBC8360_BASETIME); -} - -/* Userspace pings kernel driver, or requests clean close */ -static ssize_t sbc8360_write(struct file *file, const char __user * buf, - size_t count, loff_t * ppos) -{ - if (count) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - sbc8360_ping(); - } - return count; -} - -static int sbc8360_open(struct inode *inode, struct file *file) -{ - spin_lock(&sbc8360_lock); - if (test_and_set_bit(0, &sbc8360_is_open)) { - spin_unlock(&sbc8360_lock); - return -EBUSY; - } - if (nowayout) - __module_get(THIS_MODULE); - - /* Activate and ping once to start the countdown */ - spin_unlock(&sbc8360_lock); - sbc8360_activate(); - sbc8360_ping(); - return nonseekable_open(inode, file); -} - -static int sbc8360_close(struct inode *inode, struct file *file) -{ - spin_lock(&sbc8360_lock); - if (expect_close == 42) - outb(0, SBC8360_ENABLE); - else - printk(KERN_CRIT PFX - "SBC8360 device closed unexpectedly. SBC8360 will not stop!\n"); - - clear_bit(0, &sbc8360_is_open); - expect_close = 0; - spin_unlock(&sbc8360_lock); - return 0; -} - -/* - * Notifier for system down - */ - -static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) { - /* Disable the SBC8360 Watchdog */ - outb(0, SBC8360_ENABLE); - } - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations sbc8360_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = sbc8360_write, - .open = sbc8360_open, - .release = sbc8360_close, -}; - -static struct miscdevice sbc8360_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sbc8360_fops, -}; - -/* - * The SBC8360 needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block sbc8360_notifier = { - .notifier_call = sbc8360_notify_sys, -}; - -static int __init sbc8360_init(void) -{ - int res; - unsigned long int mseconds = 60000; - - if (timeout < 0 || timeout > 63) { - printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); - res = -EINVAL; - goto out; - } - - if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) { - printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n", - SBC8360_ENABLE); - res = -EIO; - goto out; - } - if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) { - printk(KERN_ERR PFX - "BASETIME method I/O %X is not available.\n", - SBC8360_BASETIME); - res = -EIO; - goto out_nobasetimereg; - } - - res = register_reboot_notifier(&sbc8360_notifier); - if (res) { - printk(KERN_ERR PFX "Failed to register reboot notifier.\n"); - goto out_noreboot; - } - - spin_lock_init(&sbc8360_lock); - res = misc_register(&sbc8360_miscdev); - if (res) { - printk(KERN_ERR PFX "failed to register misc device\n"); - goto out_nomisc; - } - - wd_margin = wd_times[timeout][0]; - wd_multiplier = wd_times[timeout][1]; - - if (wd_multiplier == 1) - mseconds = (wd_margin + 1) * 500; - else if (wd_multiplier == 2) - mseconds = (wd_margin + 1) * 5000; - else if (wd_multiplier == 3) - mseconds = (wd_margin + 1) * 50000; - else if (wd_multiplier == 4) - mseconds = (wd_margin + 1) * 100000; - - /* My kingdom for the ability to print "0.5 seconds" in the kernel! */ - printk(KERN_INFO PFX "Timeout set at %ld ms.\n", mseconds); - - return 0; - - out_nomisc: - unregister_reboot_notifier(&sbc8360_notifier); - out_noreboot: - release_region(SBC8360_BASETIME, 1); - out_nobasetimereg: - release_region(SBC8360_ENABLE, 1); - out: - return res; -} - -static void __exit sbc8360_exit(void) -{ - misc_deregister(&sbc8360_miscdev); - unregister_reboot_notifier(&sbc8360_notifier); - release_region(SBC8360_ENABLE, 1); - release_region(SBC8360_BASETIME, 1); -} - -module_init(sbc8360_init); -module_exit(sbc8360_exit); - -MODULE_AUTHOR("Ian E. Morgan "); -MODULE_DESCRIPTION("SBC8360 watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("1.01"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -/* end of sbc8360.c */ diff --git a/drivers/char/watchdog/sbc_epx_c3.c b/drivers/char/watchdog/sbc_epx_c3.c deleted file mode 100644 index 82cbd88..0000000 --- a/drivers/char/watchdog/sbc_epx_c3.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * SBC EPX C3 0.1 A Hardware Watchdog Device for the Winsystems EPX-C3 - * single board computer - * - * (c) Copyright 2006 Calin A. Culianu , All Rights - * Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * based on softdog.c by Alan Cox - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PFX "epx_c3: " -static int epx_c3_alive; - -#define WATCHDOG_TIMEOUT 1 /* 1 sec default 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) ")"); - -#define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */ -#define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */ - -static void epx_c3_start(void) -{ - outb(1, EPXC3_WATCHDOG_CTL_REG); -} - -static void epx_c3_stop(void) -{ - - outb(0, EPXC3_WATCHDOG_CTL_REG); - - printk(KERN_INFO PFX "Stopped watchdog timer.\n"); -} - -static void epx_c3_pet(void) -{ - outb(1, EPXC3_WATCHDOG_PET_REG); -} - -/* - * Allow only one person to hold it open - */ -static int epx_c3_open(struct inode *inode, struct file *file) -{ - if (epx_c3_alive) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - /* Activate timer */ - epx_c3_start(); - epx_c3_pet(); - - epx_c3_alive = 1; - printk(KERN_INFO "Started watchdog timer.\n"); - - return nonseekable_open(inode, file); -} - -static int epx_c3_release(struct inode *inode, struct file *file) -{ - /* Shut off the timer. - * Lock it in if it's a module and we defined ...NOWAYOUT */ - if (!nowayout) - epx_c3_stop(); /* Turn the WDT off */ - - epx_c3_alive = 0; - - return 0; -} - -static ssize_t epx_c3_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* Refresh the timer. */ - if (len) - epx_c3_pet(); - return len; -} - -static int epx_c3_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int options, retval = -EINVAL; - int __user *argp = (void __user *)arg; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = "Winsystems EPX-C3 H/W Watchdog", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof(ident))) - return -EFAULT; - return 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, argp); - case WDIOC_KEEPALIVE: - epx_c3_pet(); - return 0; - case WDIOC_GETTIMEOUT: - return put_user(WATCHDOG_TIMEOUT, argp); - case WDIOC_SETOPTIONS: - if (get_user(options, argp)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - epx_c3_stop(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - epx_c3_start(); - retval = 0; - } - - return retval; - default: - return -ENOTTY; - } -} - -static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - epx_c3_stop(); /* Turn the WDT off */ - - return NOTIFY_DONE; -} - -static const struct file_operations epx_c3_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = epx_c3_write, - .ioctl = epx_c3_ioctl, - .open = epx_c3_open, - .release = epx_c3_release, -}; - -static struct miscdevice epx_c3_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &epx_c3_fops, -}; - -static struct notifier_block epx_c3_notifier = { - .notifier_call = epx_c3_notify_sys, -}; - -static const char banner[] __initdata = - KERN_INFO PFX "Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n"; - -static int __init watchdog_init(void) -{ - int ret; - - if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog")) - return -EBUSY; - - ret = register_reboot_notifier(&epx_c3_notifier); - if (ret) { - printk(KERN_ERR PFX "cannot register reboot notifier " - "(err=%d)\n", ret); - goto out; - } - - ret = misc_register(&epx_c3_miscdev); - if (ret) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d " - "(err=%d)\n", WATCHDOG_MINOR, ret); - unregister_reboot_notifier(&epx_c3_notifier); - goto out; - } - - printk(banner); - - return 0; - -out: - release_region(EPXC3_WATCHDOG_CTL_REG, 2); - return ret; -} - -static void __exit watchdog_exit(void) -{ - misc_deregister(&epx_c3_miscdev); - unregister_reboot_notifier(&epx_c3_notifier); - release_region(EPXC3_WATCHDOG_CTL_REG, 2); -} - -module_init(watchdog_init); -module_exit(watchdog_exit); - -MODULE_AUTHOR("Calin A. Culianu "); -MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. Note that there is no way to probe for this device -- so only use it if you are *sure* you are runnning on this specific SBC system from Winsystems! It writes to IO ports 0x1ee and 0x1ef!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/sc1200wdt.c b/drivers/char/watchdog/sc1200wdt.c deleted file mode 100644 index 9670d47..0000000 --- a/drivers/char/watchdog/sc1200wdt.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver - * (c) Copyright 2002 Zwane Mwaikambo , - * All Rights Reserved. - * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * The author(s) of this software shall not be held liable for damages - * of any nature resulting due to the use of this software. This - * software is provided AS-IS with no warranties. - * - * Changelog: - * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware. - * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox. - * 20020222 Zwane Mwaikambo Added probing. - * 20020225 Zwane Mwaikambo Added ISAPNP support. - * 20020412 Rob Radez Broke out start/stop functions - * Return proper status instead of temperature warning - * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls - * Fix CONFIG_WATCHDOG_NOWAYOUT - * 20020530 Joel Becker Add Matt Domsch's nowayout module option - * 20030116 Adam Belay Updated to the latest pnp code - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define SC1200_MODULE_VER "build 20020303" -#define SC1200_MODULE_NAME "sc1200wdt" -#define PFX SC1200_MODULE_NAME ": " - -#define MAX_TIMEOUT 255 /* 255 minutes */ -#define PMIR (io) /* Power Management Index Register */ -#define PMDR (io+1) /* Power Management Data Register */ - -/* Data Register indexes */ -#define FER1 0x00 /* Function enable register 1 */ -#define FER2 0x01 /* Function enable register 2 */ -#define PMC1 0x02 /* Power Management Ctrl 1 */ -#define PMC2 0x03 /* Power Management Ctrl 2 */ -#define PMC3 0x04 /* Power Management Ctrl 3 */ -#define WDTO 0x05 /* Watchdog timeout register */ -#define WDCF 0x06 /* Watchdog config register */ -#define WDST 0x07 /* Watchdog status register */ - -/* WDCF bitfields - which devices assert WDO */ -#define KBC_IRQ 0x01 /* Keyboard Controller */ -#define MSE_IRQ 0x02 /* Mouse */ -#define UART1_IRQ 0x03 /* Serial0 */ -#define UART2_IRQ 0x04 /* Serial1 */ -/* 5 -7 are reserved */ - -static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER; -static int timeout = 1; -static int io = -1; -static int io_len = 2; /* for non plug and play */ -static struct semaphore open_sem; -static char expect_close; -static spinlock_t sc1200wdt_lock; /* io port access serialisation */ - -#if defined CONFIG_PNP -static int isapnp = 1; -static struct pnp_dev *wdt_dev; - -module_param(isapnp, int, 0); -MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); -#endif - -module_param(io, int, 0); -MODULE_PARM_DESC(io, "io port"); -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1"); - -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) ")"); - - - -/* Read from Data Register */ -static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data) -{ - spin_lock(&sc1200wdt_lock); - outb_p(index, PMIR); - *data = inb(PMDR); - spin_unlock(&sc1200wdt_lock); -} - - -/* Write to Data Register */ -static inline void sc1200wdt_write_data(unsigned char index, unsigned char data) -{ - spin_lock(&sc1200wdt_lock); - outb_p(index, PMIR); - outb(data, PMDR); - spin_unlock(&sc1200wdt_lock); -} - - -static void sc1200wdt_start(void) -{ - unsigned char reg; - - sc1200wdt_read_data(WDCF, ®); - /* assert WDO when any of the following interrupts are triggered too */ - reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); - sc1200wdt_write_data(WDCF, reg); - /* set the timeout and get the ball rolling */ - sc1200wdt_write_data(WDTO, timeout); -} - - -static void sc1200wdt_stop(void) -{ - sc1200wdt_write_data(WDTO, 0); -} - - -/* This returns the status of the WDO signal, inactive high. */ -static inline int sc1200wdt_status(void) -{ - unsigned char ret; - - sc1200wdt_read_data(WDST, &ret); - /* If the bit is inactive, the watchdog is enabled, so return - * KEEPALIVEPING which is a bit of a kludge because there's nothing - * else for enabled/disabled status - */ - return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */ -} - - -static int sc1200wdt_open(struct inode *inode, struct file *file) -{ - /* allow one at a time */ - if (down_trylock(&open_sem)) - return -EBUSY; - - if (timeout > MAX_TIMEOUT) - timeout = MAX_TIMEOUT; - - sc1200wdt_start(); - printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); - - return nonseekable_open(inode, file); -} - - -static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - int new_timeout; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = "PC87307/PC97307", - }; - - switch (cmd) { - default: - return -ENOTTY; - - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof ident)) - return -EFAULT; - return 0; - - case WDIOC_GETSTATUS: - return put_user(sc1200wdt_status(), p); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_KEEPALIVE: - sc1200wdt_write_data(WDTO, timeout); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, p)) - return -EFAULT; - - /* the API states this is given in secs */ - new_timeout /= 60; - if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) - return -EINVAL; - - timeout = new_timeout; - sc1200wdt_write_data(WDTO, timeout); - /* fall through and return the new timeout */ - - case WDIOC_GETTIMEOUT: - return put_user(timeout * 60, p); - - case WDIOC_SETOPTIONS: - { - int options, retval = -EINVAL; - - if (get_user(options, p)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - sc1200wdt_stop(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - sc1200wdt_start(); - retval = 0; - } - - return retval; - } - } -} - - -static int sc1200wdt_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - sc1200wdt_stop(); - printk(KERN_INFO PFX "Watchdog disabled\n"); - } else { - sc1200wdt_write_data(WDTO, timeout); - printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout); - } - up(&open_sem); - expect_close = 0; - - return 0; -} - - -static ssize_t sc1200wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data+i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - - sc1200wdt_write_data(WDTO, timeout); - return len; - } - - return 0; -} - - -static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - sc1200wdt_stop(); - - return NOTIFY_DONE; -} - - -static struct notifier_block sc1200wdt_notifier = -{ - .notifier_call = sc1200wdt_notify_sys, -}; - -static const struct file_operations sc1200wdt_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = sc1200wdt_write, - .ioctl = sc1200wdt_ioctl, - .open = sc1200wdt_open, - .release = sc1200wdt_release, -}; - -static struct miscdevice sc1200wdt_miscdev = -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sc1200wdt_fops, -}; - - -static int __init sc1200wdt_probe(void) -{ - /* The probe works by reading the PMC3 register's default value of 0x0e - * there is one caveat, if the device disables the parallel port or any - * of the UARTs we won't be able to detect it. - * Nb. This could be done with accuracy by reading the SID registers, but - * we don't have access to those io regions. - */ - - unsigned char reg; - - sc1200wdt_read_data(PMC3, ®); - reg &= 0x0f; /* we don't want the UART busy bits */ - return (reg == 0x0e) ? 0 : -ENODEV; -} - - -#if defined CONFIG_PNP - -static struct pnp_device_id scl200wdt_pnp_devices[] = { - /* National Semiconductor PC87307/PC97307 watchdog component */ - {.id = "NSC0800", .driver_data = 0}, - {.id = ""}, -}; - -static int scl200wdt_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) -{ - /* this driver only supports one card at a time */ - if (wdt_dev || !isapnp) - return -EBUSY; - - wdt_dev = dev; - io = pnp_port_start(wdt_dev, 0); - io_len = pnp_port_len(wdt_dev, 0); - - if (!request_region(io, io_len, SC1200_MODULE_NAME)) { - printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); - return -EBUSY; - } - - printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d\n", io, io_len); - return 0; -} - -static void scl200wdt_pnp_remove(struct pnp_dev * dev) -{ - if (wdt_dev){ - release_region(io, io_len); - wdt_dev = NULL; - } -} - -static struct pnp_driver scl200wdt_pnp_driver = { - .name = "scl200wdt", - .id_table = scl200wdt_pnp_devices, - .probe = scl200wdt_pnp_probe, - .remove = scl200wdt_pnp_remove, -}; - -#endif /* CONFIG_PNP */ - - -static int __init sc1200wdt_init(void) -{ - int ret; - - printk("%s\n", banner); - - spin_lock_init(&sc1200wdt_lock); - sema_init(&open_sem, 1); - -#if defined CONFIG_PNP - if (isapnp) { - ret = pnp_register_driver(&scl200wdt_pnp_driver); - if (ret) - goto out_clean; - } -#endif - - if (io == -1) { - printk(KERN_ERR PFX "io parameter must be specified\n"); - ret = -EINVAL; - goto out_pnp; - } - -#if defined CONFIG_PNP - /* now that the user has specified an IO port and we haven't detected - * any devices, disable pnp support */ - isapnp = 0; - pnp_unregister_driver(&scl200wdt_pnp_driver); -#endif - - if (!request_region(io, io_len, SC1200_MODULE_NAME)) { - printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); - ret = -EBUSY; - goto out_pnp; - } - - ret = sc1200wdt_probe(); - if (ret) - goto out_io; - - ret = register_reboot_notifier(&sc1200wdt_notifier); - if (ret) { - printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret); - goto out_io; - } - - ret = misc_register(&sc1200wdt_miscdev); - if (ret) { - printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); - goto out_rbt; - } - - /* ret = 0 */ - -out_clean: - return ret; - -out_rbt: - unregister_reboot_notifier(&sc1200wdt_notifier); - -out_io: - release_region(io, io_len); - -out_pnp: -#if defined CONFIG_PNP - if (isapnp) - pnp_unregister_driver(&scl200wdt_pnp_driver); -#endif - goto out_clean; -} - - -static void __exit sc1200wdt_exit(void) -{ - misc_deregister(&sc1200wdt_miscdev); - unregister_reboot_notifier(&sc1200wdt_notifier); - -#if defined CONFIG_PNP - if(isapnp) - pnp_unregister_driver(&scl200wdt_pnp_driver); - else -#endif - release_region(io, io_len); -} - -module_init(sc1200wdt_init); -module_exit(sc1200wdt_exit); - -MODULE_AUTHOR("Zwane Mwaikambo "); -MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/sc520_wdt.c b/drivers/char/watchdog/sc520_wdt.c deleted file mode 100644 index e8594c6..0000000 --- a/drivers/char/watchdog/sc520_wdt.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * AMD Elan SC520 processor Watchdog Timer driver - * - * Based on acquirewdt.c by Alan Cox, - * and sbc60xxwdt.c by Jakob Oestergaard - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * The authors do NOT admit liability nor provide warranty for - * any of this software. This material is provided "AS-IS" in - * the hope that it may be useful for others. - * - * (c) Copyright 2001 Scott Jennings - * 9/27 - 2001 [Initial release] - * - * Additional fixes Alan Cox - * - Fixed formatting - * - Removed debug printks - * - Fixed SMP built kernel deadlock - * - Switched to private locks not lock_kernel - * - Used ioremap/writew/readw - * - Added NOWAYOUT support - * 4/12 - 2002 Changes by Rob Radez - * - Change comments - * - Eliminate fop_llseek - * - Change CONFIG_WATCHDOG_NOWAYOUT semantics - * - Add KERN_* tags to printks - * - fix possible wdt_is_open race - * - Report proper capabilities in watchdog_info - * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT, - * GETTIMEOUT, SETOPTIONS} ioctls - * 09/8 - 2003 Changes by Wim Van Sebroeck - * - cleanup of trailing spaces - * - added extra printk's for startup problems - * - use module_param - * - made timeout (the emulated heartbeat) a module_param - * - made the keepalive ping an internal subroutine - * 3/27 - 2004 Changes by Sean Young - * - set MMCR_BASE to 0xfffef000 - * - CBAR does not need to be read - * - removed debugging printks - * - * This WDT driver is different from most other Linux WDT - * drivers in that the driver will ping the watchdog by itself, - * because this particular WDT has a very short timeout (1.6 - * seconds) and it would be insane to count on any userspace - * daemon always getting scheduled within that time frame. - * - * This driver uses memory mapped IO, and spinlock. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define OUR_NAME "sc520_wdt" -#define PFX OUR_NAME ": " - -/* - * The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7) - * - * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s - * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s - * - * We will program the SC520 watchdog for a timeout of 2.01s. - * If we reset the watchdog every ~250ms we should be safe. - */ - -#define WDT_INTERVAL (HZ/4+1) - -/* - * We must not require too good response from the userspace daemon. - * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 30 seconds. - */ - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, 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) ")"); - -/* - * AMD Elan SC520 - Watchdog Timer Registers - */ -#define MMCR_BASE 0xfffef000 /* The default base address */ -#define OFFS_WDTMRCTL 0xCB0 /* Watchdog Timer Control Register */ - -/* WDT Control Register bit definitions */ -#define WDT_EXP_SEL_01 0x0001 /* [01] Time-out = 496 us (with 33 Mhz clk). */ -#define WDT_EXP_SEL_02 0x0002 /* [02] Time-out = 508 ms (with 33 Mhz clk). */ -#define WDT_EXP_SEL_03 0x0004 /* [03] Time-out = 1.02 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_04 0x0008 /* [04] Time-out = 2.03 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_05 0x0010 /* [05] Time-out = 4.07 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_06 0x0020 /* [06] Time-out = 8.13 s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_07 0x0040 /* [07] Time-out = 16.27s (with 33 Mhz clk). */ -#define WDT_EXP_SEL_08 0x0080 /* [08] Time-out = 32.54s (with 33 Mhz clk). */ -#define WDT_IRQ_FLG 0x1000 /* [12] Interrupt Request Flag */ -#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */ -#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */ - -static __u16 __iomem *wdtmrctl; - -static void wdt_timer_ping(unsigned long); -static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); -static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static char wdt_expect_close; -static spinlock_t wdt_spinlock; - -/* - * Whack the dog - */ - -static void wdt_timer_ping(unsigned long data) -{ - /* If we got a heartbeat pulse within the WDT_US_INTERVAL - * we agree to ping the WDT - */ - if(time_before(jiffies, next_heartbeat)) - { - /* Ping the WDT */ - spin_lock(&wdt_spinlock); - writew(0xAAAA, wdtmrctl); - writew(0x5555, wdtmrctl); - spin_unlock(&wdt_spinlock); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } -} - -/* - * Utility routines - */ - -static void wdt_config(int writeval) -{ - __u16 dummy; - unsigned long flags; - - /* buy some time (ping) */ - spin_lock_irqsave(&wdt_spinlock, flags); - dummy=readw(wdtmrctl); /* ensure write synchronization */ - writew(0xAAAA, wdtmrctl); - writew(0x5555, wdtmrctl); - /* unlock WDT = make WDT configuration register writable one time */ - writew(0x3333, wdtmrctl); - writew(0xCCCC, wdtmrctl); - /* write WDT configuration register */ - writew(writeval, wdtmrctl); - spin_unlock_irqrestore(&wdt_spinlock, flags); -} - -static int wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - /* Start the timer */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - - /* Start the watchdog */ - wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04); - - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); - return 0; -} - -static int wdt_turnoff(void) -{ - /* Stop the timer */ - del_timer(&timer); - - /* Stop the watchdog */ - wdt_config(0); - - printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); - return 0; -} - -static int wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); - return 0; -} - -static int wdt_set_heartbeat(int t) -{ - if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ - return -EINVAL; - - timeout = t; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if(count) { - if (!nowayout) { - size_t ofs; - - /* note: just in case someone wrote the magic character - * five months ago... */ - wdt_expect_close = 0; - - /* now scan */ - for(ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if(c == 'V') - wdt_expect_close = 42; - } - } - - /* Well, anyhow someone wrote to us, we should return that favour */ - wdt_keepalive(); - } - return count; -} - -static int fop_open(struct inode * inode, struct file * file) -{ - /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - if (nowayout) - __module_get(THIS_MODULE); - - /* Good, fire up the show */ - wdt_startup(); - return nonseekable_open(inode, file); -} - -static int fop_close(struct inode * inode, struct file * file) -{ - if(wdt_expect_close == 42) { - wdt_turnoff(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - wdt_keepalive(); - } - clear_bit(0, &wdt_is_open); - wdt_expect_close = 0; - return 0; -} - -static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "SC520", - }; - - switch(cmd) - { - default: - return -ENOTTY; - 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: - wdt_keepalive(); - return 0; - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if(get_user(new_options, p)) - return -EFAULT; - - if(new_options & WDIOS_DISABLECARD) { - wdt_turnoff(); - retval = 0; - } - - if(new_options & WDIOS_ENABLECARD) { - wdt_startup(); - retval = 0; - } - - return retval; - } - case WDIOC_SETTIMEOUT: - { - int new_timeout; - - if(get_user(new_timeout, p)) - return -EFAULT; - - if(wdt_set_heartbeat(new_timeout)) - return -EINVAL; - - wdt_keepalive(); - /* Fall through */ - } - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - } -} - -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = fop_write, - .open = fop_open, - .release = fop_close, - .ioctl = fop_ioctl, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -/* - * Notifier for system down - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code==SYS_DOWN || code==SYS_HALT) - wdt_turnoff(); - return NOTIFY_DONE; -} - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdt_notifier = { - .notifier_call = wdt_notify_sys, -}; - -static void __exit sc520_wdt_unload(void) -{ - if (!nowayout) - wdt_turnoff(); - - /* Deregister */ - misc_deregister(&wdt_miscdev); - unregister_reboot_notifier(&wdt_notifier); - iounmap(wdtmrctl); -} - -static int __init sc520_wdt_init(void) -{ - int rc = -EBUSY; - - spin_lock_init(&wdt_spinlock); - - /* Check that the timeout value is within it's range ; if not reset to the default */ - if (wdt_set_heartbeat(timeout)) { - wdt_set_heartbeat(WATCHDOG_TIMEOUT); - printk(KERN_INFO PFX "timeout value must be 1<=timeout<=3600, using %d\n", - WATCHDOG_TIMEOUT); - } - - wdtmrctl = ioremap((unsigned long)(MMCR_BASE + OFFS_WDTMRCTL), 2); - if (!wdtmrctl) { - printk(KERN_ERR PFX "Unable to remap memory\n"); - rc = -ENOMEM; - goto err_out_region2; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - rc); - goto err_out_ioremap; - } - - rc = misc_register(&wdt_miscdev); - if (rc) { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, rc); - goto err_out_notifier; - } - - printk(KERN_INFO PFX "WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n", - timeout,nowayout); - - return 0; - -err_out_notifier: - unregister_reboot_notifier(&wdt_notifier); -err_out_ioremap: - iounmap(wdtmrctl); -err_out_region2: - return rc; -} - -module_init(sc520_wdt_init); -module_exit(sc520_wdt_unload); - -MODULE_AUTHOR("Scott and Bill Jennings"); -MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/scx200_wdt.c b/drivers/char/watchdog/scx200_wdt.c deleted file mode 100644 index d4fd0fa..0000000 --- a/drivers/char/watchdog/scx200_wdt.c +++ /dev/null @@ -1,269 +0,0 @@ -/* drivers/char/watchdog/scx200_wdt.c - - National Semiconductor SCx200 Watchdog support - - Copyright (c) 2001,2002 Christer Weinigel - - Some code taken from: - National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver - (c) Copyright 2002 Zwane Mwaikambo - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The author(s) of this software shall not be held liable for damages - of any nature resulting due to the use of this software. This - software is provided AS-IS with no warranties. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define NAME "scx200_wdt" - -MODULE_AUTHOR("Christer Weinigel "); -MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -static int margin = 60; /* in seconds */ -module_param(margin, int, 0); -MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); - -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); - -static u16 wdto_restart; -static struct semaphore open_semaphore; -static char expect_close; - -/* Bits of the WDCNFG register */ -#define W_ENABLE 0x00fa /* Enable watchdog */ -#define W_DISABLE 0x0000 /* Disable watchdog */ - -/* The scaling factor for the timer, this depends on the value of W_ENABLE */ -#define W_SCALE (32768/1024) - -static void scx200_wdt_ping(void) -{ - outw(wdto_restart, scx200_cb_base + SCx200_WDT_WDTO); -} - -static void scx200_wdt_update_margin(void) -{ - printk(KERN_INFO NAME ": timer margin %d seconds\n", margin); - wdto_restart = margin * W_SCALE; -} - -static void scx200_wdt_enable(void) -{ - printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n", - wdto_restart); - - outw(0, scx200_cb_base + SCx200_WDT_WDTO); - outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS); - outw(W_ENABLE, scx200_cb_base + SCx200_WDT_WDCNFG); - - scx200_wdt_ping(); -} - -static void scx200_wdt_disable(void) -{ - printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); - - outw(0, scx200_cb_base + SCx200_WDT_WDTO); - outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS); - outw(W_DISABLE, scx200_cb_base + SCx200_WDT_WDCNFG); -} - -static int scx200_wdt_open(struct inode *inode, struct file *file) -{ - /* only allow one at a time */ - if (down_trylock(&open_semaphore)) - return -EBUSY; - scx200_wdt_enable(); - - return nonseekable_open(inode, file); -} - -static int scx200_wdt_release(struct inode *inode, struct file *file) -{ - if (expect_close != 42) { - printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n"); - } else if (!nowayout) { - scx200_wdt_disable(); - } - expect_close = 0; - up(&open_semaphore); - - return 0; -} - -static int scx200_wdt_notify_sys(struct notifier_block *this, - unsigned long code, void *unused) -{ - if (code == SYS_HALT || code == SYS_POWER_OFF) - if (!nowayout) - scx200_wdt_disable(); - - return NOTIFY_DONE; -} - -static struct notifier_block scx200_wdt_notifier = -{ - .notifier_call = scx200_wdt_notify_sys, -}; - -static ssize_t scx200_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* check for a magic close character */ - if (len) - { - size_t i; - - scx200_wdt_ping(); - - expect_close = 0; - for (i = 0; i < len; ++i) { - char c; - if (get_user(c, data+i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - - return len; - } - - return 0; -} - -static int scx200_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .identity = "NatSemi SCx200 Watchdog", - .firmware_version = 1, - .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING), - }; - int new_margin; - - switch (cmd) { - default: - return -ENOTTY; - case WDIOC_GETSUPPORT: - if(copy_to_user(argp, &ident, sizeof(ident))) - return -EFAULT; - return 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - if (put_user(0, p)) - return -EFAULT; - return 0; - case WDIOC_KEEPALIVE: - scx200_wdt_ping(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, p)) - return -EFAULT; - if (new_margin < 1) - return -EINVAL; - margin = new_margin; - scx200_wdt_update_margin(); - scx200_wdt_ping(); - case WDIOC_GETTIMEOUT: - if (put_user(margin, p)) - return -EFAULT; - return 0; - } -} - -static const struct file_operations scx200_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = scx200_wdt_write, - .ioctl = scx200_wdt_ioctl, - .open = scx200_wdt_open, - .release = scx200_wdt_release, -}; - -static struct miscdevice scx200_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &scx200_wdt_fops, -}; - -static int __init scx200_wdt_init(void) -{ - int r; - - printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n"); - - /* check that we have found the configuration block */ - if (!scx200_cb_present()) - return -ENODEV; - - if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET, - SCx200_WDT_SIZE, - "NatSemi SCx200 Watchdog")) { - printk(KERN_WARNING NAME ": watchdog I/O region busy\n"); - return -EBUSY; - } - - scx200_wdt_update_margin(); - scx200_wdt_disable(); - - sema_init(&open_semaphore, 1); - - r = misc_register(&scx200_wdt_miscdev); - if (r) { - release_region(scx200_cb_base + SCx200_WDT_OFFSET, - SCx200_WDT_SIZE); - return r; - } - - r = register_reboot_notifier(&scx200_wdt_notifier); - if (r) { - printk(KERN_ERR NAME ": unable to register reboot notifier"); - misc_deregister(&scx200_wdt_miscdev); - release_region(scx200_cb_base + SCx200_WDT_OFFSET, - SCx200_WDT_SIZE); - return r; - } - - return 0; -} - -static void __exit scx200_wdt_cleanup(void) -{ - unregister_reboot_notifier(&scx200_wdt_notifier); - misc_deregister(&scx200_wdt_miscdev); - release_region(scx200_cb_base + SCx200_WDT_OFFSET, - SCx200_WDT_SIZE); -} - -module_init(scx200_wdt_init); -module_exit(scx200_wdt_cleanup); - -/* - Local variables: - compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules" - c-basic-offset: 8 - End: -*/ diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c deleted file mode 100644 index cecbedd..0000000 --- a/drivers/char/watchdog/shwdt.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * drivers/char/watchdog/shwdt.c - * - * Watchdog driver for integrated watchdog in the SuperH processors. - * - * Copyright (C) 2001, 2002, 2003 Paul Mundt - * - * 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. - * - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * - * 19-Apr-2002 Rob Radez - * Added expect close support, made emulated timeout runtime changeable - * general cleanups, add some ioctls - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PFX "shwdt: " - -/* - * Default clock division ratio is 5.25 msecs. For an additional table of - * values, consult the asm-sh/watchdog.h. Overload this at module load - * time. - * - * In order for this to work reliably we need to have HZ set to 1000 or - * something quite higher than 100 (or we need a proper high-res timer - * implementation that will deal with this properly), otherwise the 10ms - * resolution of a jiffy is enough to trigger the overflow. For things like - * the SH-4 and SH-5, this isn't necessarily that big of a problem, though - * for the SH-2 and SH-3, this isn't recommended unless the WDT is absolutely - * necssary. - * - * As a result of this timing problem, the only modes that are particularly - * feasible are the 4096 and the 2048 divisors, which yeild 5.25 and 2.62ms - * overflow periods respectively. - * - * Also, since we can't really expect userspace to be responsive enough - * before the overflow happens, we maintain two seperate timers .. One in - * the kernel for clearing out WOVF every 2ms or so (again, this depends on - * HZ == 1000), and another for monitoring userspace writes to the WDT device. - * - * As such, we currently use a configurable heartbeat interval which defaults - * to 30s. In this case, the userspace daemon is only responsible for periodic - * writes to the device before the next heartbeat is scheduled. If the daemon - * misses its deadline, the kernel timer will allow the WDT to overflow. - */ -static int clock_division_ratio = WTCSR_CKS_4096; - -#define next_ping_period(cks) msecs_to_jiffies(cks - 4) - -static void sh_wdt_ping(unsigned long data); - -static unsigned long shwdt_is_open; -static struct watchdog_info sh_wdt_info; -static char shwdt_expect_close; -static DEFINE_TIMER(timer, sh_wdt_ping, 0, 0); -static unsigned long next_heartbeat; - -#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ -static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ - -static int nowayout = WATCHDOG_NOWAYOUT; - -/** - * sh_wdt_start - Start the Watchdog - * - * Starts the watchdog. - */ -static void sh_wdt_start(void) -{ - __u8 csr; - - next_heartbeat = jiffies + (heartbeat * HZ); - mod_timer(&timer, next_ping_period(clock_division_ratio)); - - csr = sh_wdt_read_csr(); - csr |= WTCSR_WT | clock_division_ratio; - sh_wdt_write_csr(csr); - - sh_wdt_write_cnt(0); - - /* - * These processors have a bit of an inconsistent initialization - * process.. starting with SH-3, RSTS was moved to WTCSR, and the - * RSTCSR register was removed. - * - * On the SH-2 however, in addition with bits being in different - * locations, we must deal with RSTCSR outright.. - */ - csr = sh_wdt_read_csr(); - csr |= WTCSR_TME; - csr &= ~WTCSR_RSTS; - sh_wdt_write_csr(csr); - -#ifdef CONFIG_CPU_SH2 - /* - * Whoever came up with the RSTCSR semantics must've been smoking - * some of the good stuff, since in addition to the WTCSR/WTCNT write - * brain-damage, it's managed to fuck things up one step further.. - * - * If we need to clear the WOVF bit, the upper byte has to be 0xa5.. - * but if we want to touch RSTE or RSTS, the upper byte has to be - * 0x5a.. - */ - csr = sh_wdt_read_rstcsr(); - csr &= ~RSTCSR_RSTS; - sh_wdt_write_rstcsr(csr); -#endif -} - -/** - * sh_wdt_stop - Stop the Watchdog - * Stops the watchdog. - */ -static void sh_wdt_stop(void) -{ - __u8 csr; - - del_timer(&timer); - - csr = sh_wdt_read_csr(); - csr &= ~WTCSR_TME; - sh_wdt_write_csr(csr); -} - -/** - * sh_wdt_keepalive - Keep the Userspace Watchdog Alive - * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. - */ -static inline void sh_wdt_keepalive(void) -{ - next_heartbeat = jiffies + (heartbeat * HZ); -} - -/** - * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat - * Set the Userspace Watchdog heartbeat - */ -static int sh_wdt_set_heartbeat(int t) -{ - if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ - return -EINVAL; - - heartbeat = t; - return 0; -} - -/** - * sh_wdt_ping - Ping the Watchdog - * @data: Unused - * - * Clears overflow bit, resets timer counter. - */ -static void sh_wdt_ping(unsigned long data) -{ - if (time_before(jiffies, next_heartbeat)) { - __u8 csr; - - csr = sh_wdt_read_csr(); - csr &= ~WTCSR_IOVF; - sh_wdt_write_csr(csr); - - sh_wdt_write_cnt(0); - - mod_timer(&timer, next_ping_period(clock_division_ratio)); - } else - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " - "the watchdog\n"); -} - -/** - * sh_wdt_open - Open the Device - * @inode: inode of device - * @file: file handle of device - * - * Watchdog device is opened and started. - */ -static int sh_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &shwdt_is_open)) - return -EBUSY; - if (nowayout) - __module_get(THIS_MODULE); - - sh_wdt_start(); - - return nonseekable_open(inode, file); -} - -/** - * sh_wdt_close - Close the Device - * @inode: inode of device - * @file: file handle of device - * - * Watchdog device is closed and stopped. - */ -static int sh_wdt_close(struct inode *inode, struct file *file) -{ - if (shwdt_expect_close == 42) { - sh_wdt_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not " - "stopping watchdog!\n"); - sh_wdt_keepalive(); - } - - clear_bit(0, &shwdt_is_open); - shwdt_expect_close = 0; - - return 0; -} - -/** - * sh_wdt_write - Write to Device - * @file: file handle of device - * @buf: buffer to write - * @count: length of buffer - * @ppos: offset - * - * Pings the watchdog on write. - */ -static ssize_t sh_wdt_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -{ - if (count) { - if (!nowayout) { - size_t i; - - shwdt_expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - shwdt_expect_close = 42; - } - } - sh_wdt_keepalive(); - } - - return count; -} - -/** - * sh_wdt_mmap - map WDT/CPG registers into userspace - * @file: file structure for the device - * @vma: VMA to map the registers into - * - * A simple mmap() implementation for the corner cases where the counter - * needs to be mapped in userspace directly. Due to the relatively small - * size of the area, neighbouring registers not necessarily tied to the - * CPG will also be accessible through the register page, so this remains - * configurable for users that really know what they're doing. - * - * Additionaly, the register page maps in the CPG register base relative - * to the nearest page-aligned boundary, which requires that userspace do - * the appropriate CPU subtype math for calculating the page offset for - * the counter value. - */ -static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) -{ - int ret = -ENOSYS; - -#ifdef CONFIG_SH_WDT_MMAP - unsigned long addr; - - /* Only support the simple cases where we map in a register page. */ - if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) - return -EINVAL; - - /* - * Pick WTCNT as the start, it's usually the first register after the - * FRQCR, and neither one are generally page-aligned out of the box. - */ - addr = WTCNT & ~(PAGE_SIZE - 1); - - vma->vm_flags |= VM_IO; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, - PAGE_SIZE, vma->vm_page_prot)) { - printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", - __FUNCTION__); - return -EAGAIN; - } - - ret = 0; -#endif - - return ret; -} - -/** - * sh_wdt_ioctl - Query Device - * @inode: inode of device - * @file: file handle of device - * @cmd: watchdog command - * @arg: argument - * - * Query basic information from the device or ping it, as outlined by the - * watchdog API. - */ -static int sh_wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int new_heartbeat; - int options, retval = -EINVAL; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user((struct watchdog_info *)arg, - &sh_wdt_info, - sizeof(sh_wdt_info)) ? -EFAULT : 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, (int *)arg); - case WDIOC_KEEPALIVE: - sh_wdt_keepalive(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_heartbeat, (int *)arg)) - return -EFAULT; - - if (sh_wdt_set_heartbeat(new_heartbeat)) - return -EINVAL; - - sh_wdt_keepalive(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, (int *)arg); - case WDIOC_SETOPTIONS: - if (get_user(options, (int *)arg)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - sh_wdt_stop(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - sh_wdt_start(); - retval = 0; - } - - return retval; - default: - return -ENOTTY; - } - - return 0; -} - -/** - * sh_wdt_notify_sys - Notifier Handler - * @this: notifier block - * @code: notifier event - * @unused: unused - * - * Handles specific events, such as turning off the watchdog during a - * shutdown event. - */ -static int sh_wdt_notify_sys(struct notifier_block *this, - unsigned long code, void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - sh_wdt_stop(); - - return NOTIFY_DONE; -} - -static const struct file_operations sh_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = sh_wdt_write, - .ioctl = sh_wdt_ioctl, - .open = sh_wdt_open, - .release = sh_wdt_close, - .mmap = sh_wdt_mmap, -}; - -static struct watchdog_info sh_wdt_info = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "SH WDT", -}; - -static struct notifier_block sh_wdt_notifier = { - .notifier_call = sh_wdt_notify_sys, -}; - -static struct miscdevice sh_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &sh_wdt_fops, -}; - -/** - * sh_wdt_init - Initialize module - * Registers the device and notifier handler. Actual device - * initialization is handled by sh_wdt_open(). - */ -static int __init sh_wdt_init(void) -{ - int rc; - - if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { - clock_division_ratio = WTCSR_CKS_4096; - printk(KERN_INFO PFX "clock_division_ratio value must " - "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); - } - - rc = sh_wdt_set_heartbeat(heartbeat); - if (unlikely(rc)) { - heartbeat = WATCHDOG_HEARTBEAT; - printk(KERN_INFO PFX "heartbeat value must " - "be 1<=x<=3600, using %d\n", heartbeat); - } - - rc = register_reboot_notifier(&sh_wdt_notifier); - if (unlikely(rc)) { - printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", - rc); - return rc; - } - - rc = misc_register(&sh_wdt_miscdev); - if (unlikely(rc)) { - printk(KERN_ERR PFX "Can't register miscdev on " - "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); - unregister_reboot_notifier(&sh_wdt_notifier); - return rc; - } - - printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", - heartbeat, nowayout); - - return 0; -} - -/** - * sh_wdt_exit - Deinitialize module - * Unregisters the device and notifier handler. Actual device - * deinitialization is handled by sh_wdt_close(). - */ -static void __exit sh_wdt_exit(void) -{ - misc_deregister(&sh_wdt_miscdev); - unregister_reboot_notifier(&sh_wdt_notifier); -} - -MODULE_AUTHOR("Paul Mundt "); -MODULE_DESCRIPTION("SuperH watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -module_param(clock_division_ratio, int, 0); -MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -module_init(sh_wdt_init); -module_exit(sh_wdt_exit); diff --git a/drivers/char/watchdog/smsc37b787_wdt.c b/drivers/char/watchdog/smsc37b787_wdt.c deleted file mode 100644 index d3cb0a7..0000000 --- a/drivers/char/watchdog/smsc37b787_wdt.c +++ /dev/null @@ -1,627 +0,0 @@ -/* - * SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x - * - * Based on acquirewdt.c by Alan Cox - * 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* 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 const 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 "); -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=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/char/watchdog/softdog.c b/drivers/char/watchdog/softdog.c deleted file mode 100644 index 9c36949..0000000 --- a/drivers/char/watchdog/softdog.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * SoftDog 0.07: A Software Watchdog Device - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * Software only watchdog driver. Unlike its big brother the WDT501P - * driver this won't always recover a failed machine. - * - * 03/96: Angelo Haritsis : - * Modularised. - * Added soft_margin; use upon insmod to change the timer delay. - * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate - * minors. - * - * 19980911 Alan Cox - * Made SMP safe for 2.3.x - * - * 20011127 Joel Becker (jlbec@evilplan.org> - * Added soft_noboot; Allows testing the softdog trigger without - * requiring a recompile. - * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT. - * - * 20020530 Joel Becker - * Added Matt Domsch's nowayout module option. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define PFX "SoftDog: " - -#define TIMER_MARGIN 60 /* Default is 60 seconds */ -static int soft_margin = TIMER_MARGIN; /* in seconds */ -module_param(soft_margin, int, 0); -MODULE_PARM_DESC(soft_margin, "Watchdog soft_margin in seconds. (0 0xFFFF)) - return -EINVAL; - - soft_margin = t; - return 0; -} - -/* - * /dev/watchdog handling - */ - -static int softdog_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &driver_open)) - return -EBUSY; - if (!test_and_clear_bit(0, &orphan_timer)) - __module_get(THIS_MODULE); - /* - * Activate timer - */ - softdog_keepalive(); - return nonseekable_open(inode, file); -} - -static int softdog_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - * Lock it in if it's a module and we set nowayout - */ - if (expect_close == 42) { - softdog_stop(); - module_put(THIS_MODULE); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - set_bit(0, &orphan_timer); - softdog_keepalive(); - } - clear_bit(0, &driver_open); - expect_close = 0; - return 0; -} - -static ssize_t softdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) -{ - /* - * Refresh the timer. - */ - if(len) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - softdog_keepalive(); - } - return len; -} - -static int softdog_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_margin; - static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = "Software Watchdog", - }; - switch (cmd) { - default: - return -ENOTTY; - 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: - softdog_keepalive(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, p)) - return -EFAULT; - if (softdog_set_heartbeat(new_margin)) - return -EINVAL; - softdog_keepalive(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(soft_margin, p); - } -} - -/* - * Notifier for system down - */ - -static int softdog_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code==SYS_DOWN || code==SYS_HALT) { - /* Turn the WDT off */ - softdog_stop(); - } - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations softdog_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = softdog_write, - .ioctl = softdog_ioctl, - .open = softdog_open, - .release = softdog_release, -}; - -static struct miscdevice softdog_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &softdog_fops, -}; - -static struct notifier_block softdog_notifier = { - .notifier_call = softdog_notify_sys, -}; - -static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 initialized. soft_noboot=%d soft_margin=%d sec (nowayout= %d)\n"; - -static int __init watchdog_init(void) -{ - int ret; - - /* Check that the soft_margin value is within it's range ; if not reset to the default */ - if (softdog_set_heartbeat(soft_margin)) { - softdog_set_heartbeat(TIMER_MARGIN); - printk(KERN_INFO PFX "soft_margin value must be 0 - * added support for W83627THF. - * - * (c) Copyright 2003,2007 Pádraig Brady - * - * Based on advantechwdt.c which is based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 2000-2001 Marek Michalkiewicz - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define WATCHDOG_NAME "w83627hf/thf/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, "w83627hf/thf WDT io port (default 0x2E)"); - -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=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -/* - * Kernel methods. - */ - -#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ -#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register (same as EFER) */ -#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ - -static void -w83627hf_select_wd_register(void) -{ - unsigned char c; - outb_p(0x87, WDT_EFER); /* Enter extended function mode */ - outb_p(0x87, WDT_EFER); /* Again according to manual */ - - outb(0x20, WDT_EFER); /* check chip version */ - c = inb(WDT_EFDR); - if (c == 0x82) { /* W83627THF */ - outb_p(0x2b, WDT_EFER); /* select GPIO3 */ - c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ - outb_p(0x2b, WDT_EFER); - outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */ - } - - outb_p(0x07, WDT_EFER); /* point to logical device number reg */ - outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ - outb_p(0x30, WDT_EFER); /* select CR30 */ - outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */ -} - -static void -w83627hf_unselect_wd_register(void) -{ - outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ -} - -/* tyan motherboards seem to set F5 to 0x4C ? - * So explicitly init to appropriate value. */ -static void -w83627hf_init(void) -{ - unsigned char t; - - w83627hf_select_wd_register(); - - outb_p(0xF6, WDT_EFER); /* Select CRF6 */ - t=inb_p(WDT_EFDR); /* read CRF6 */ - if (t != 0) { - printk (KERN_INFO PFX "Watchdog already running. Resetting timeout to %d sec\n", timeout); - outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */ - } - - outb_p(0xF5, WDT_EFER); /* Select CRF5 */ - t=inb_p(WDT_EFDR); /* read CRF5 */ - t&=~0x0C; /* set second mode & disable keyboard turning off watchdog */ - outb_p(t, WDT_EFDR); /* Write back to CRF5 */ - - outb_p(0xF7, WDT_EFER); /* Select CRF7 */ - t=inb_p(WDT_EFDR); /* read CRF7 */ - t&=~0xC0; /* disable keyboard & mouse turning off watchdog */ - outb_p(t, WDT_EFDR); /* Write back to CRF7 */ - - w83627hf_unselect_wd_register(); -} - -static void -wdt_ctrl(int timeout) -{ - spin_lock(&io_lock); - - w83627hf_select_wd_register(); - - outb_p(0xF6, WDT_EFER); /* Select CRF6 */ - outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */ - - w83627hf_unselect_wd_register(); - - spin_unlock(&io_lock); -} - -static int -wdt_ping(void) -{ - wdt_ctrl(timeout); - return 0; -} - -static int -wdt_disable(void) -{ - wdt_ctrl(0); - 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 = "W83627HF 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_ping(); - 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_ping(); - 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 const 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 __init -wdt_init(void) -{ - int ret; - - spin_lock_init(&io_lock); - - printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG Super I/O chip initialising.\n"); - - 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); - } - - if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_io); - ret = -EIO; - goto out; - } - - w83627hf_init(); - - 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, 1); - goto out; -} - -static void __exit -wdt_exit(void) -{ - misc_deregister(&wdt_miscdev); - unregister_reboot_notifier(&wdt_notifier); - release_region(wdt_io,1); -} - -module_init(wdt_init); -module_exit(wdt_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Pádraig Brady "); -MODULE_DESCRIPTION("w83627hf/thf WDT driver"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/w83697hf_wdt.c b/drivers/char/watchdog/w83697hf_wdt.c deleted file mode 100644 index d9e821d..0000000 --- a/drivers/char/watchdog/w83697hf_wdt.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * w83697hf/hg WDT driver - * - * (c) Copyright 2006 Samuel Tardieu - * (c) Copyright 2006 Marcus Junker - * - * Based on w83627hf_wdt.c which is based on advantechwdt.c - * which is based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 2003 Pádraig Brady - * - * (c) Copyright 2000-2001 Marek Michalkiewicz - * - * (c) Copyright 1996 Alan Cox , 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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=" __MODULE_STRING(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 const 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 , Samuel Tardieu "); -MODULE_DESCRIPTION("w83697hf/hg WDT driver"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/w83877f_wdt.c b/drivers/char/watchdog/w83877f_wdt.c deleted file mode 100644 index 3c88fe1..0000000 --- a/drivers/char/watchdog/w83877f_wdt.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * W83877F Computer Watchdog Timer driver - * - * Based on acquirewdt.c by Alan Cox, - * and sbc60xxwdt.c by Jakob Oestergaard - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * The authors do NOT admit liability nor provide warranty for - * any of this software. This material is provided "AS-IS" in - * the hope that it may be useful for others. - * - * (c) Copyright 2001 Scott Jennings - * - * 4/19 - 2001 [Initial revision] - * 9/27 - 2001 Added spinlocking - * 4/12 - 2002 [rob@osinvestor.com] Eliminate extra comments - * Eliminate fop_read - * Eliminate extra spin_unlock - * Added KERN_* tags to printks - * add CONFIG_WATCHDOG_NOWAYOUT support - * fix possible wdt_is_open race - * changed watchdog_info to correctly reflect what the driver offers - * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, WDIOC_SETTIMEOUT, - * WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls - * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces - * added extra printk's for startup problems - * use module_param - * made timeout (the emulated heartbeat) a module_param - * made the keepalive ping an internal subroutine - * - * This WDT driver is different from most other Linux WDT - * drivers in that the driver will ping the watchdog by itself, - * because this particular WDT has a very short timeout (1.6 - * seconds) and it would be insane to count on any userspace - * daemon always getting scheduled within that time frame. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define OUR_NAME "w83877f_wdt" -#define PFX OUR_NAME ": " - -#define ENABLE_W83877F_PORT 0x3F0 -#define ENABLE_W83877F 0x87 -#define DISABLE_W83877F 0xAA -#define WDT_PING 0x443 -#define WDT_REGISTER 0x14 -#define WDT_ENABLE 0x9C -#define WDT_DISABLE 0x8C - -/* - * The W83877F seems to be fixed at 1.6s timeout (at least on the - * EMACS PC-104 board I'm using). If we reset the watchdog every - * ~250ms we should be safe. */ - -#define WDT_INTERVAL (HZ/4+1) - -/* - * We must not require too good response from the userspace daemon. - * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 30 seconds. - */ - -#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ -static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, 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 void wdt_timer_ping(unsigned long); -static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); -static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static char wdt_expect_close; -static spinlock_t wdt_spinlock; - -/* - * Whack the dog - */ - -static void wdt_timer_ping(unsigned long data) -{ - /* If we got a heartbeat pulse within the WDT_US_INTERVAL - * we agree to ping the WDT - */ - if(time_before(jiffies, next_heartbeat)) - { - /* Ping the WDT */ - spin_lock(&wdt_spinlock); - - /* Ping the WDT by reading from WDT_PING */ - inb_p(WDT_PING); - - /* Re-set the timer interval */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - - spin_unlock(&wdt_spinlock); - - } else { - printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); - } -} - -/* - * Utility routines - */ - -static void wdt_change(int writeval) -{ - unsigned long flags; - spin_lock_irqsave(&wdt_spinlock, flags); - - /* buy some time */ - inb_p(WDT_PING); - - /* make W83877F available */ - outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT); - outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT); - - /* enable watchdog */ - outb_p(WDT_REGISTER, ENABLE_W83877F_PORT); - outb_p(writeval, ENABLE_W83877F_PORT+1); - - /* lock the W8387FF away */ - outb_p(DISABLE_W83877F, ENABLE_W83877F_PORT); - - spin_unlock_irqrestore(&wdt_spinlock, flags); -} - -static void wdt_startup(void) -{ - next_heartbeat = jiffies + (timeout * HZ); - - /* Start the timer */ - mod_timer(&timer, jiffies + WDT_INTERVAL); - - wdt_change(WDT_ENABLE); - - printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); -} - -static void wdt_turnoff(void) -{ - /* Stop the timer */ - del_timer(&timer); - - wdt_change(WDT_DISABLE); - - printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); -} - -static void wdt_keepalive(void) -{ - /* user land ping */ - next_heartbeat = jiffies + (timeout * HZ); -} - -/* - * /dev/watchdog handling - */ - -static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if(count) - { - if (!nowayout) - { - size_t ofs; - - /* note: just in case someone wrote the magic character - * five months ago... */ - wdt_expect_close = 0; - - /* scan to see whether or not we got the magic character */ - for(ofs = 0; ofs != count; ofs++) - { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V') - wdt_expect_close = 42; - } - } - - /* someone wrote to us, we should restart timer */ - wdt_keepalive(); - } - return count; -} - -static int fop_open(struct inode * inode, struct file * file) -{ - /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - - /* Good, fire up the show */ - wdt_startup(); - return nonseekable_open(inode, file); -} - -static int fop_close(struct inode * inode, struct file * file) -{ - if(wdt_expect_close == 42) - wdt_turnoff(); - else { - del_timer(&timer); - printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n"); - } - clear_bit(0, &wdt_is_open); - wdt_expect_close = 0; - return 0; -} - -static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident= - { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "W83877F", - }; - - switch(cmd) - { - default: - return -ENOTTY; - 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: - wdt_keepalive(); - return 0; - case WDIOC_SETOPTIONS: - { - int new_options, retval = -EINVAL; - - if(get_user(new_options, p)) - return -EFAULT; - - if(new_options & WDIOS_DISABLECARD) { - wdt_turnoff(); - retval = 0; - } - - if(new_options & WDIOS_ENABLECARD) { - wdt_startup(); - retval = 0; - } - - return retval; - } - case WDIOC_SETTIMEOUT: - { - int new_timeout; - - if(get_user(new_timeout, p)) - return -EFAULT; - - if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ - return -EINVAL; - - timeout = new_timeout; - wdt_keepalive(); - /* Fall through */ - } - case WDIOC_GETTIMEOUT: - return put_user(timeout, p); - } -} - -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = fop_write, - .open = fop_open, - .release = fop_close, - .ioctl = fop_ioctl, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -/* - * Notifier for system down - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code==SYS_DOWN || code==SYS_HALT) - wdt_turnoff(); - return NOTIFY_DONE; -} - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdt_notifier= -{ - .notifier_call = wdt_notify_sys, -}; - -static void __exit w83877f_wdt_unload(void) -{ - wdt_turnoff(); - - /* Deregister */ - misc_deregister(&wdt_miscdev); - - unregister_reboot_notifier(&wdt_notifier); - release_region(WDT_PING,1); - release_region(ENABLE_W83877F_PORT,2); -} - -static int __init w83877f_wdt_init(void) -{ - int rc = -EBUSY; - - spin_lock_init(&wdt_spinlock); - - if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ - { - timeout = WATCHDOG_TIMEOUT; - printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", - timeout); - } - - if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) - { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - ENABLE_W83877F_PORT); - rc = -EIO; - goto err_out; - } - - if (!request_region(WDT_PING, 1, "W8387FF WDT")) - { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - WDT_PING); - rc = -EIO; - goto err_out_region1; - } - - rc = misc_register(&wdt_miscdev); - if (rc) - { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - wdt_miscdev.minor, rc); - goto err_out_region2; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) - { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - rc); - goto err_out_miscdev; - } - - printk(KERN_INFO PFX "WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - - return 0; - -err_out_miscdev: - misc_deregister(&wdt_miscdev); -err_out_region2: - release_region(WDT_PING,1); -err_out_region1: - release_region(ENABLE_W83877F_PORT,2); -err_out: - return rc; -} - -module_init(w83877f_wdt_init); -module_exit(w83877f_wdt_unload); - -MODULE_AUTHOR("Scott and Bill Jennings"); -MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/w83977f_wdt.c b/drivers/char/watchdog/w83977f_wdt.c deleted file mode 100644 index 1579684..0000000 --- a/drivers/char/watchdog/w83977f_wdt.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip - * - * (c) Copyright 2005 Jose Goncalves - * - * Based on w83877f_wdt.c by Scott Jennings, - * and wdt977.c by Woody Suwalski - * - * ----------------------- - * - * 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 - -#define WATCHDOG_VERSION "1.00" -#define WATCHDOG_NAME "W83977F WDT" -#define PFX WATCHDOG_NAME ": " -#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" - -#define IO_INDEX_PORT 0x3F0 -#define IO_DATA_PORT (IO_INDEX_PORT+1) - -#define UNLOCK_DATA 0x87 -#define LOCK_DATA 0xAA -#define DEVICE_REGISTER 0x07 - -#define DEFAULT_TIMEOUT 45 /* default timeout in seconds */ - -static int timeout = DEFAULT_TIMEOUT; -static int timeoutW; /* timeout in watchdog counter units */ -static unsigned long timer_alive; -static int testmode; -static char expect_close; -static spinlock_t spinlock; - -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (15..7635), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); -module_param(testmode, int, 0); -MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); - -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) ")"); - -/* - * Start the watchdog - */ - -static int wdt_start(void) -{ - unsigned long flags; - - spin_lock_irqsave(&spinlock, flags); - - /* Unlock the SuperIO chip */ - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - - /* - * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. - * F2 has the timeout in watchdog counter units. - * F3 is set to enable watchdog LED blink at timeout. - * F4 is used to just clear the TIMEOUT'ed state (bit 0). - */ - outb_p(DEVICE_REGISTER,IO_INDEX_PORT); - outb_p(0x08,IO_DATA_PORT); - outb_p(0xF2,IO_INDEX_PORT); - outb_p(timeoutW,IO_DATA_PORT); - outb_p(0xF3,IO_INDEX_PORT); - outb_p(0x08,IO_DATA_PORT); - outb_p(0xF4,IO_INDEX_PORT); - outb_p(0x00,IO_DATA_PORT); - - /* Set device Aux2 active */ - outb_p(0x30,IO_INDEX_PORT); - outb_p(0x01,IO_DATA_PORT); - - /* - * Select device Aux1 (dev=7) to set GP16 as the watchdog output - * (in reg E6) and GP13 as the watchdog LED output (in reg E3). - * Map GP16 at pin 119. - * In test mode watch the bit 0 on F4 to indicate "triggered" or - * check watchdog LED on SBC. - */ - outb_p(DEVICE_REGISTER,IO_INDEX_PORT); - outb_p(0x07,IO_DATA_PORT); - if (!testmode) - { - unsigned pin_map; - - outb_p(0xE6,IO_INDEX_PORT); - outb_p(0x0A,IO_DATA_PORT); - outb_p(0x2C,IO_INDEX_PORT); - pin_map = inb_p(IO_DATA_PORT); - pin_map |= 0x10; - pin_map &= ~(0x20); - outb_p(0x2C,IO_INDEX_PORT); - outb_p(pin_map,IO_DATA_PORT); - } - outb_p(0xE3,IO_INDEX_PORT); - outb_p(0x08,IO_DATA_PORT); - - /* Set device Aux1 active */ - outb_p(0x30,IO_INDEX_PORT); - outb_p(0x01,IO_DATA_PORT); - - /* Lock the SuperIO chip */ - outb_p(LOCK_DATA,IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - - printk(KERN_INFO PFX "activated.\n"); - - return 0; -} - -/* - * Stop the watchdog - */ - -static int wdt_stop(void) -{ - unsigned long flags; - - spin_lock_irqsave(&spinlock, flags); - - /* Unlock the SuperIO chip */ - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - - /* - * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. - * F2 is reset to its default value (watchdog timer disabled). - * F3 is reset to its default state. - * F4 clears the TIMEOUT'ed state (bit 0) - back to default. - */ - outb_p(DEVICE_REGISTER,IO_INDEX_PORT); - outb_p(0x08,IO_DATA_PORT); - outb_p(0xF2,IO_INDEX_PORT); - outb_p(0xFF,IO_DATA_PORT); - outb_p(0xF3,IO_INDEX_PORT); - outb_p(0x00,IO_DATA_PORT); - outb_p(0xF4,IO_INDEX_PORT); - outb_p(0x00,IO_DATA_PORT); - outb_p(0xF2,IO_INDEX_PORT); - outb_p(0x00,IO_DATA_PORT); - - /* - * Select device Aux1 (dev=7) to set GP16 (in reg E6) and - * Gp13 (in reg E3) as inputs. - */ - outb_p(DEVICE_REGISTER,IO_INDEX_PORT); - outb_p(0x07,IO_DATA_PORT); - if (!testmode) - { - outb_p(0xE6,IO_INDEX_PORT); - outb_p(0x01,IO_DATA_PORT); - } - outb_p(0xE3,IO_INDEX_PORT); - outb_p(0x01,IO_DATA_PORT); - - /* Lock the SuperIO chip */ - outb_p(LOCK_DATA,IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - - printk(KERN_INFO PFX "shutdown.\n"); - - return 0; -} - -/* - * Send a keepalive ping to the watchdog - * This is done by simply re-writing the timeout to reg. 0xF2 - */ - -static int wdt_keepalive(void) -{ - unsigned long flags; - - spin_lock_irqsave(&spinlock, flags); - - /* Unlock the SuperIO chip */ - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - - /* Select device Aux2 (device=8) to kick watchdog reg F2 */ - outb_p(DEVICE_REGISTER,IO_INDEX_PORT); - outb_p(0x08,IO_DATA_PORT); - outb_p(0xF2,IO_INDEX_PORT); - outb_p(timeoutW,IO_DATA_PORT); - - /* Lock the SuperIO chip */ - outb_p(LOCK_DATA,IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - - return 0; -} - -/* - * Set the watchdog timeout value - */ - -static int wdt_set_timeout(int t) -{ - int tmrval; - - /* - * Convert seconds to watchdog counter time units, rounding up. - * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup - * value. This information is supplied in the PCM-5335 manual and was - * checked by me on a real board. This is a bit strange because W83977f - * datasheet says counter unit is in minutes! - */ - if (t < 15) - return -EINVAL; - - tmrval = ((t + 15) + 29) / 30; - - if (tmrval > 255) - return -EINVAL; - - /* - * timeout is the timeout in seconds, - * timeoutW is the timeout in watchdog counter units. - */ - timeoutW = tmrval; - timeout = (timeoutW * 30) - 15; - return 0; -} - -/* - * Get the watchdog status - */ - -static int wdt_get_status(int *status) -{ - int new_status; - unsigned long flags; - - spin_lock_irqsave(&spinlock, flags); - - /* Unlock the SuperIO chip */ - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - outb_p(UNLOCK_DATA,IO_INDEX_PORT); - - /* Select device Aux2 (device=8) to read watchdog reg F4 */ - outb_p(DEVICE_REGISTER,IO_INDEX_PORT); - outb_p(0x08,IO_DATA_PORT); - outb_p(0xF4,IO_INDEX_PORT); - new_status = inb_p(IO_DATA_PORT); - - /* Lock the SuperIO chip */ - outb_p(LOCK_DATA,IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - - *status = 0; - if (new_status & 1) - *status |= WDIOF_CARDRESET; - - return 0; -} - - -/* - * /dev/watchdog handling - */ - -static int wdt_open(struct inode *inode, struct file *file) -{ - /* If the watchdog is alive we don't need to start it again */ - if( test_and_set_bit(0, &timer_alive) ) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - wdt_start(); - return nonseekable_open(inode, file); -} - -static int wdt_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - * Lock it in if it's a module and we set nowayout - */ - if (expect_close == 42) - { - wdt_stop(); - clear_bit(0, &timer_alive); - } else { - wdt_keepalive(); - printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); - } - expect_close = 0; - return 0; -} - -/* - * wdt_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t wdt_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if(count) - { - if (!nowayout) - { - size_t ofs; - - /* note: just in case someone wrote the magic character long ago */ - expect_close = 0; - - /* scan to see whether or not we got the magic character */ - for(ofs = 0; ofs != count; ofs++) - { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == 'V') { - expect_close = 42; - } - } - } - - /* someone wrote to us, we should restart timer */ - wdt_keepalive(); - } - return count; -} - -/* - * wdt_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - */ - -static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = WATCHDOG_NAME, -}; - -static int wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int status; - int new_options, retval = -EINVAL; - int new_timeout; - union { - struct watchdog_info __user *ident; - int __user *i; - } uarg; - - 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: - wdt_get_status(&status); - return put_user(status, uarg.i); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, uarg.i); - - case WDIOC_KEEPALIVE: - wdt_keepalive(); - return 0; - - case WDIOC_SETOPTIONS: - if (get_user (new_options, uarg.i)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - wdt_stop(); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - wdt_start(); - retval = 0; - } - - return retval; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, uarg.i)) - return -EFAULT; - - if (wdt_set_timeout(new_timeout)) - return -EINVAL; - - wdt_keepalive(); - /* Fall */ - - case WDIOC_GETTIMEOUT: - return put_user(timeout, uarg.i); - - } -} - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) - wdt_stop(); - return NOTIFY_DONE; -} - -static const struct file_operations wdt_fops= -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wdt_write, - .ioctl = wdt_ioctl, - .open = wdt_open, - .release = wdt_release, -}; - -static struct miscdevice wdt_miscdev= -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -static struct notifier_block wdt_notifier = { - .notifier_call = wdt_notify_sys, -}; - -static int __init w83977f_wdt_init(void) -{ - int rc; - - printk(KERN_INFO PFX DRIVER_VERSION); - - spin_lock_init(&spinlock); - - /* - * Check that the timeout value is within it's range ; - * if not reset to the default - */ - if (wdt_set_timeout(timeout)) { - wdt_set_timeout(DEFAULT_TIMEOUT); - printk(KERN_INFO PFX "timeout value must be 15<=timeout<=7635, using %d\n", - DEFAULT_TIMEOUT); - } - - if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) - { - printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", - IO_INDEX_PORT); - rc = -EIO; - goto err_out; - } - - rc = misc_register(&wdt_miscdev); - if (rc) - { - printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - wdt_miscdev.minor, rc); - goto err_out_region; - } - - rc = register_reboot_notifier(&wdt_notifier); - if (rc) - { - printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - rc); - goto err_out_miscdev; - } - - printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", - timeout, nowayout, testmode); - - return 0; - -err_out_miscdev: - misc_deregister(&wdt_miscdev); -err_out_region: - release_region(IO_INDEX_PORT,2); -err_out: - return rc; -} - -static void __exit w83977f_wdt_exit(void) -{ - wdt_stop(); - misc_deregister(&wdt_miscdev); - unregister_reboot_notifier(&wdt_notifier); - release_region(IO_INDEX_PORT,2); -} - -module_init(w83977f_wdt_init); -module_exit(w83977f_wdt_exit); - -MODULE_AUTHOR("Jose Goncalves "); -MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c deleted file mode 100644 index 950905d..0000000 --- a/drivers/char/watchdog/wafer5823wdt.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * ICP Wafer 5823 Single Board Computer WDT driver - * http://www.icpamerica.com/wafer_5823.php - * May also work on other similar models - * - * (c) Copyright 2002 Justin Cormack - * - * Release 0.02 - * - * Based on advantechwdt.c which is based on wdt.c. - * Original copyright messages: - * - * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define WATCHDOG_NAME "Wafer 5823 WDT" -#define PFX WATCHDOG_NAME ": " -#define WD_TIMO 60 /* 60 sec default timeout */ - -static unsigned long wafwdt_is_open; -static char expect_close; -static spinlock_t wafwdt_lock; - -/* - * You must set these - there is no sane way to probe for this board. - * - * To enable, write the timeout value in seconds (1 to 255) to I/O - * port WDT_START, then read the port to start the watchdog. To pat - * the dog, read port WDT_STOP to stop the timer, then read WDT_START - * to restart it again. - */ - -static int wdt_stop = 0x843; -static int wdt_start = 0x443; - -static int timeout = WD_TIMO; /* in seconds */ -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WD_TIMO) "."); - -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 wafwdt_ping(void) -{ - /* pat watchdog */ - spin_lock(&wafwdt_lock); - inb_p(wdt_stop); - inb_p(wdt_start); - spin_unlock(&wafwdt_lock); -} - -static void wafwdt_start(void) -{ - /* start up watchdog */ - outb_p(timeout, wdt_start); - inb_p(wdt_start); -} - -static void -wafwdt_stop(void) -{ - /* stop watchdog */ - inb_p(wdt_stop); -} - -static ssize_t wafwdt_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (count) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - /* scan to see whether or not we got the magic character */ - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - /* Well, anyhow someone wrote to us, we should return that favour */ - wafwdt_ping(); - } - return count; -} - -static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int new_timeout; - void __user *argp = (void __user *)arg; - int __user *p = argp; - static struct watchdog_info ident = { - .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .firmware_version = 1, - .identity = "Wafer 5823 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: - wafwdt_ping(); - break; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, p)) - return -EFAULT; - if ((new_timeout < 1) || (new_timeout > 255)) - return -EINVAL; - timeout = new_timeout; - wafwdt_stop(); - wafwdt_start(); - /* 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) { - wafwdt_start(); - retval = 0; - } - - if (options & WDIOS_ENABLECARD) { - wafwdt_stop(); - retval = 0; - } - - return retval; - } - - default: - return -ENOTTY; - } - return 0; -} - -static int wafwdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &wafwdt_is_open)) - return -EBUSY; - - /* - * Activate - */ - wafwdt_start(); - return nonseekable_open(inode, file); -} - -static int -wafwdt_close(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - wafwdt_stop(); - } else { - printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n"); - wafwdt_ping(); - } - clear_bit(0, &wafwdt_is_open); - expect_close = 0; - return 0; -} - -/* - * Notifier for system down - */ - -static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) { - /* Turn the WDT off */ - wafwdt_stop(); - } - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - -static const struct file_operations wafwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wafwdt_write, - .ioctl = wafwdt_ioctl, - .open = wafwdt_open, - .release = wafwdt_close, -}; - -static struct miscdevice wafwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wafwdt_fops, -}; - -/* - * The WDT needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wafwdt_notifier = { - .notifier_call = wafwdt_notify_sys, -}; - -static int __init wafwdt_init(void) -{ - int ret; - - printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n"); - - spin_lock_init(&wafwdt_lock); - - if (timeout < 1 || timeout > 255) { - timeout = WD_TIMO; - printk (KERN_INFO PFX "timeout value must be 1<=x<=255, using %d\n", - timeout); - } - - if (wdt_stop != wdt_start) { - if(!request_region(wdt_stop, 1, "Wafer 5823 WDT")) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_stop); - ret = -EIO; - goto error; - } - } - - if(!request_region(wdt_start, 1, "Wafer 5823 WDT")) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", - wdt_start); - ret = -EIO; - goto error2; - } - - ret = register_reboot_notifier(&wafwdt_notifier); - if (ret != 0) { - printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", - ret); - goto error3; - } - - ret = misc_register(&wafwdt_miscdev); - if (ret != 0) { - printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - WATCHDOG_MINOR, ret); - goto error4; - } - - printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", - timeout, nowayout); - - return ret; -error4: - unregister_reboot_notifier(&wafwdt_notifier); -error3: - release_region(wdt_start, 1); -error2: - if (wdt_stop != wdt_start) - release_region(wdt_stop, 1); -error: - return ret; -} - -static void __exit wafwdt_exit(void) -{ - misc_deregister(&wafwdt_miscdev); - unregister_reboot_notifier(&wafwdt_notifier); - if(wdt_stop != wdt_start) - release_region(wdt_stop, 1); - release_region(wdt_start, 1); -} - -module_init(wafwdt_init); -module_exit(wafwdt_exit); - -MODULE_AUTHOR("Justin Cormack"); -MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -/* end of wafer5823wdt.c */ diff --git a/drivers/char/watchdog/wd501p.h b/drivers/char/watchdog/wd501p.h deleted file mode 100644 index a4504f4..0000000 --- a/drivers/char/watchdog/wd501p.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Industrial Computer Source WDT500/501 driver - * - * (c) Copyright 1995 CymruNET Ltd - * Innovation Centre - * Singleton Park - * Swansea - * Wales - * UK - * SA2 8PP - * - * http://www.cymru.net - * - * This driver is provided under the GNU General Public License, incorporated - * herein by reference. The driver is provided without warranty or - * support. - * - * Release 0.04. - * - */ - - -#define WDT_COUNT0 (io+0) -#define WDT_COUNT1 (io+1) -#define WDT_COUNT2 (io+2) -#define WDT_CR (io+3) -#define WDT_SR (io+4) /* Start buzzer on PCI write */ -#define WDT_RT (io+5) /* Stop buzzer on PCI write */ -#define WDT_BUZZER (io+6) /* PCI only: rd=disable, wr=enable */ -#define WDT_DC (io+7) - -/* The following are only on the PCI card, they're outside of I/O space on - * the ISA card: */ -#define WDT_CLOCK (io+12) /* COUNT2: rd=16.67MHz, wr=2.0833MHz */ -/* inverted opto isolated reset output: */ -#define WDT_OPTONOTRST (io+13) /* wr=enable, rd=disable */ -/* opto isolated reset output: */ -#define WDT_OPTORST (io+14) /* wr=enable, rd=disable */ -/* programmable outputs: */ -#define WDT_PROGOUT (io+15) /* wr=enable, rd=disable */ - - /* FAN 501 500 */ -#define WDC_SR_WCCR 1 /* Active low */ /* X X X */ -#define WDC_SR_TGOOD 2 /* X X - */ -#define WDC_SR_ISOI0 4 /* X X X */ -#define WDC_SR_ISII1 8 /* X X X */ -#define WDC_SR_FANGOOD 16 /* X - - */ -#define WDC_SR_PSUOVER 32 /* Active low */ /* X X - */ -#define WDC_SR_PSUUNDR 64 /* Active low */ /* X X - */ -#define WDC_SR_IRQ 128 /* Active low */ /* X X X */ - diff --git a/drivers/char/watchdog/wdrtas.c b/drivers/char/watchdog/wdrtas.c deleted file mode 100644 index 1d64e27..0000000 --- a/drivers/char/watchdog/wdrtas.c +++ /dev/null @@ -1,695 +0,0 @@ -/* - * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as - * RTAS calls are available - */ - -/* - * RTAS watchdog driver - * - * (C) Copyright IBM Corp. 2005 - * device driver to exploit watchdog RTAS functions - * - * Authors : Utz Bacher - * - * 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, 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 - -#define WDRTAS_MAGIC_CHAR 42 -#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \ - WDIOF_MAGICCLOSE) - -MODULE_AUTHOR("Utz Bacher "); -MODULE_DESCRIPTION("RTAS watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -MODULE_ALIAS_MISCDEV(TEMP_MINOR); - -#ifdef CONFIG_WATCHDOG_NOWAYOUT -static int wdrtas_nowayout = 1; -#else -static int wdrtas_nowayout = 0; -#endif - -static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0); -static char wdrtas_expect_close = 0; - -static int wdrtas_interval; - -#define WDRTAS_THERMAL_SENSOR 3 -static int wdrtas_token_get_sensor_state; -#define WDRTAS_SURVEILLANCE_IND 9000 -static int wdrtas_token_set_indicator; -#define WDRTAS_SP_SPI 28 -static int wdrtas_token_get_sp; -static int wdrtas_token_event_scan; - -#define WDRTAS_DEFAULT_INTERVAL 300 - -#define WDRTAS_LOGBUFFER_LEN 128 -static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN]; - - -/*** watchdog access functions */ - -/** - * wdrtas_set_interval - sets the watchdog interval - * @interval: new interval - * - * returns 0 on success, <0 on failures - * - * wdrtas_set_interval sets the watchdog keepalive interval by calling the - * RTAS function set-indicator (surveillance). The unit of interval is - * seconds. - */ -static int -wdrtas_set_interval(int interval) -{ - long result; - static int print_msg = 10; - - /* rtas uses minutes */ - interval = (interval + 59) / 60; - - result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL, - WDRTAS_SURVEILLANCE_IND, 0, interval); - if ( (result < 0) && (print_msg) ) { - printk(KERN_ERR "wdrtas: setting the watchdog to %i " - "timeout failed: %li\n", interval, result); - print_msg--; - } - - return result; -} - -/** - * wdrtas_get_interval - returns the current watchdog interval - * @fallback_value: value (in seconds) to use, if the RTAS call fails - * - * returns the interval - * - * wdrtas_get_interval returns the current watchdog keepalive interval - * as reported by the RTAS function ibm,get-system-parameter. The unit - * of the return value is seconds. - */ -static int -wdrtas_get_interval(int fallback_value) -{ - long result; - char value[4]; - - result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL, - WDRTAS_SP_SPI, (void *)__pa(&value), 4); - if ( (value[0] != 0) || (value[1] != 2) || (value[3] != 0) || - (result < 0) ) { - printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog " - "timeout (%li). Continuing\n", result); - return fallback_value; - } - - /* rtas uses minutes */ - return ((int)value[2]) * 60; -} - -/** - * wdrtas_timer_start - starts watchdog - * - * wdrtas_timer_start starts the watchdog by calling the RTAS function - * set-interval (surveillance) - */ -static void -wdrtas_timer_start(void) -{ - wdrtas_set_interval(wdrtas_interval); -} - -/** - * wdrtas_timer_stop - stops watchdog - * - * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function - * set-interval (surveillance) - */ -static void -wdrtas_timer_stop(void) -{ - wdrtas_set_interval(0); -} - -/** - * wdrtas_log_scanned_event - logs an event we received during keepalive - * - * wdrtas_log_scanned_event prints a message to the log buffer dumping - * the results of the last event-scan call - */ -static void -wdrtas_log_scanned_event(void) -{ - int i; - - for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16) - printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x\n", - (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16), - wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1], - wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3], - wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5], - wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7], - wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9], - wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11], - wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13], - wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]); -} - -/** - * wdrtas_timer_keepalive - resets watchdog timer to keep system alive - * - * wdrtas_timer_keepalive restarts the watchdog timer by calling the - * RTAS function event-scan and repeats these calls as long as there are - * events available. All events will be dumped. - */ -static void -wdrtas_timer_keepalive(void) -{ - long result; - - do { - result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL, - RTAS_EVENT_SCAN_ALL_EVENTS, 0, - (void *)__pa(wdrtas_logbuffer), - WDRTAS_LOGBUFFER_LEN); - if (result < 0) - printk(KERN_ERR "wdrtas: event-scan failed: %li\n", - result); - if (result == 0) - wdrtas_log_scanned_event(); - } while (result == 0); -} - -/** - * wdrtas_get_temperature - returns current temperature - * - * returns temperature or <0 on failures - * - * wdrtas_get_temperature returns the current temperature in Fahrenheit. It - * uses the RTAS call get-sensor-state, token 3 to do so - */ -static int -wdrtas_get_temperature(void) -{ - long result; - int temperature = 0; - - result = rtas_call(wdrtas_token_get_sensor_state, 2, 2, - (void *)__pa(&temperature), - WDRTAS_THERMAL_SENSOR, 0); - - if (result < 0) - printk(KERN_WARNING "wdrtas: reading the thermal sensor " - "faild: %li\n", result); - else - temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */ - - return temperature; -} - -/** - * wdrtas_get_status - returns the status of the watchdog - * - * returns a bitmask of defines WDIOF_... as defined in - * include/linux/watchdog.h - */ -static int -wdrtas_get_status(void) -{ - return 0; /* TODO */ -} - -/** - * wdrtas_get_boot_status - returns the reason for the last boot - * - * returns a bitmask of defines WDIOF_... as defined in - * include/linux/watchdog.h, indicating why the watchdog rebooted the system - */ -static int -wdrtas_get_boot_status(void) -{ - return 0; /* TODO */ -} - -/*** watchdog API and operations stuff */ - -/* wdrtas_write - called when watchdog device is written to - * @file: file structure - * @buf: user buffer with data - * @len: amount to data written - * @ppos: position in file - * - * returns the number of successfully processed characters, which is always - * the number of bytes passed to this function - * - * wdrtas_write processes all the data given to it and looks for the magic - * character 'V'. This character allows the watchdog device to be closed - * properly. - */ -static ssize_t -wdrtas_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - int i; - char c; - - if (!len) - goto out; - - if (!wdrtas_nowayout) { - wdrtas_expect_close = 0; - /* look for 'V' */ - for (i = 0; i < len; i++) { - if (get_user(c, buf + i)) - return -EFAULT; - /* allow to close device */ - if (c == 'V') - wdrtas_expect_close = WDRTAS_MAGIC_CHAR; - } - } - - wdrtas_timer_keepalive(); - -out: - return len; -} - -/** - * wdrtas_ioctl - ioctl function for the watchdog device - * @inode: inode structure - * @file: file structure - * @cmd: command for ioctl - * @arg: argument pointer - * - * returns 0 on success, <0 on failure - * - * wdrtas_ioctl implements the watchdog API ioctls - */ -static int -wdrtas_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int __user *argp = (void __user *)arg; - int i; - static struct watchdog_info wdinfo = { - .options = WDRTAS_SUPPORTED_MASK, - .firmware_version = 0, - .identity = "wdrtas" - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &wdinfo, sizeof(wdinfo))) - return -EFAULT; - return 0; - - case WDIOC_GETSTATUS: - i = wdrtas_get_status(); - return put_user(i, argp); - - case WDIOC_GETBOOTSTATUS: - i = wdrtas_get_boot_status(); - return put_user(i, argp); - - case WDIOC_GETTEMP: - if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) - return -EOPNOTSUPP; - - i = wdrtas_get_temperature(); - return put_user(i, argp); - - case WDIOC_SETOPTIONS: - if (get_user(i, argp)) - return -EFAULT; - if (i & WDIOS_DISABLECARD) - wdrtas_timer_stop(); - if (i & WDIOS_ENABLECARD) { - wdrtas_timer_keepalive(); - wdrtas_timer_start(); - } - if (i & WDIOS_TEMPPANIC) { - /* not implemented. Done by H8 */ - } - return 0; - - case WDIOC_KEEPALIVE: - wdrtas_timer_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(i, argp)) - return -EFAULT; - - if (wdrtas_set_interval(i)) - return -EINVAL; - - wdrtas_timer_keepalive(); - - if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) - wdrtas_interval = i; - else - wdrtas_interval = wdrtas_get_interval(i); - /* fallthrough */ - - case WDIOC_GETTIMEOUT: - return put_user(wdrtas_interval, argp); - - default: - return -ENOTTY; - } -} - -/** - * wdrtas_open - open function of watchdog device - * @inode: inode structure - * @file: file structure - * - * returns 0 on success, -EBUSY if the file has been opened already, <0 on - * other failures - * - * function called when watchdog device is opened - */ -static int -wdrtas_open(struct inode *inode, struct file *file) -{ - /* only open once */ - if (atomic_inc_return(&wdrtas_miscdev_open) > 1) { - atomic_dec(&wdrtas_miscdev_open); - return -EBUSY; - } - - wdrtas_timer_start(); - wdrtas_timer_keepalive(); - - return nonseekable_open(inode, file); -} - -/** - * wdrtas_close - close function of watchdog device - * @inode: inode structure - * @file: file structure - * - * returns 0 on success - * - * close function. Always succeeds - */ -static int -wdrtas_close(struct inode *inode, struct file *file) -{ - /* only stop watchdog, if this was announced using 'V' before */ - if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR) - wdrtas_timer_stop(); - else { - printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog " - "not stopped.\n"); - wdrtas_timer_keepalive(); - } - - wdrtas_expect_close = 0; - atomic_dec(&wdrtas_miscdev_open); - return 0; -} - -/** - * wdrtas_temp_read - gives back the temperature in fahrenheit - * @file: file structure - * @buf: user buffer - * @count: number of bytes to be read - * @ppos: position in file - * - * returns always 1 or -EFAULT in case of user space copy failures, <0 on - * other failures - * - * wdrtas_temp_read gives the temperature to the users by copying this - * value as one byte into the user space buffer. The unit is Fahrenheit... - */ -static ssize_t -wdrtas_temp_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - int temperature = 0; - - temperature = wdrtas_get_temperature(); - if (temperature < 0) - return temperature; - - if (copy_to_user(buf, &temperature, 1)) - return -EFAULT; - - return 1; -} - -/** - * wdrtas_temp_open - open function of temperature device - * @inode: inode structure - * @file: file structure - * - * returns 0 on success, <0 on failure - * - * function called when temperature device is opened - */ -static int -wdrtas_temp_open(struct inode *inode, struct file *file) -{ - return nonseekable_open(inode, file); -} - -/** - * wdrtas_temp_close - close function of temperature device - * @inode: inode structure - * @file: file structure - * - * returns 0 on success - * - * close function. Always succeeds - */ -static int -wdrtas_temp_close(struct inode *inode, struct file *file) -{ - return 0; -} - -/** - * wdrtas_reboot - reboot notifier function - * @nb: notifier block structure - * @code: reboot code - * @ptr: unused - * - * returns NOTIFY_DONE - * - * wdrtas_reboot stops the watchdog in case of a reboot - */ -static int -wdrtas_reboot(struct notifier_block *this, unsigned long code, void *ptr) -{ - if ( (code==SYS_DOWN) || (code==SYS_HALT) ) - wdrtas_timer_stop(); - - return NOTIFY_DONE; -} - -/*** initialization stuff */ - -static const struct file_operations wdrtas_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wdrtas_write, - .ioctl = wdrtas_ioctl, - .open = wdrtas_open, - .release = wdrtas_close, -}; - -static struct miscdevice wdrtas_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdrtas_fops, -}; - -static const struct file_operations wdrtas_temp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = wdrtas_temp_read, - .open = wdrtas_temp_open, - .release = wdrtas_temp_close, -}; - -static struct miscdevice wdrtas_tempdev = { - .minor = TEMP_MINOR, - .name = "temperature", - .fops = &wdrtas_temp_fops, -}; - -static struct notifier_block wdrtas_notifier = { - .notifier_call = wdrtas_reboot, -}; - -/** - * wdrtas_get_tokens - reads in RTAS tokens - * - * returns 0 on succes, <0 on failure - * - * wdrtas_get_tokens reads in the tokens for the RTAS calls used in - * this watchdog driver. It tolerates, if "get-sensor-state" and - * "ibm,get-system-parameter" are not available. - */ -static int -wdrtas_get_tokens(void) -{ - wdrtas_token_get_sensor_state = rtas_token("get-sensor-state"); - if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) { - printk(KERN_WARNING "wdrtas: couldn't get token for " - "get-sensor-state. Trying to continue without " - "temperature support.\n"); - } - - wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter"); - if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) { - printk(KERN_WARNING "wdrtas: couldn't get token for " - "ibm,get-system-parameter. Trying to continue with " - "a default timeout value of %i seconds.\n", - WDRTAS_DEFAULT_INTERVAL); - } - - wdrtas_token_set_indicator = rtas_token("set-indicator"); - if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) { - printk(KERN_ERR "wdrtas: couldn't get token for " - "set-indicator. Terminating watchdog code.\n"); - return -EIO; - } - - wdrtas_token_event_scan = rtas_token("event-scan"); - if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) { - printk(KERN_ERR "wdrtas: couldn't get token for event-scan. " - "Terminating watchdog code.\n"); - return -EIO; - } - - return 0; -} - -/** - * wdrtas_unregister_devs - unregisters the misc dev handlers - * - * wdrtas_register_devs unregisters the watchdog and temperature watchdog - * misc devs - */ -static void -wdrtas_unregister_devs(void) -{ - misc_deregister(&wdrtas_miscdev); - if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) - misc_deregister(&wdrtas_tempdev); -} - -/** - * wdrtas_register_devs - registers the misc dev handlers - * - * returns 0 on succes, <0 on failure - * - * wdrtas_register_devs registers the watchdog and temperature watchdog - * misc devs - */ -static int -wdrtas_register_devs(void) -{ - int result; - - result = misc_register(&wdrtas_miscdev); - if (result) { - printk(KERN_ERR "wdrtas: couldn't register watchdog misc " - "device. Terminating watchdog code.\n"); - return result; - } - - if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) { - result = misc_register(&wdrtas_tempdev); - if (result) { - printk(KERN_WARNING "wdrtas: couldn't register " - "watchdog temperature misc device. Continuing " - "without temperature support.\n"); - wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE; - } - } - - return 0; -} - -/** - * wdrtas_init - init function of the watchdog driver - * - * returns 0 on succes, <0 on failure - * - * registers the file handlers and the reboot notifier - */ -static int __init -wdrtas_init(void) -{ - if (wdrtas_get_tokens()) - return -ENODEV; - - if (wdrtas_register_devs()) - return -ENODEV; - - if (register_reboot_notifier(&wdrtas_notifier)) { - printk(KERN_ERR "wdrtas: could not register reboot notifier. " - "Terminating watchdog code.\n"); - wdrtas_unregister_devs(); - return -ENODEV; - } - - if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) - wdrtas_interval = WDRTAS_DEFAULT_INTERVAL; - else - wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL); - - return 0; -} - -/** - * wdrtas_exit - exit function of the watchdog driver - * - * unregisters the file handlers and the reboot notifier - */ -static void __exit -wdrtas_exit(void) -{ - if (!wdrtas_nowayout) - wdrtas_timer_stop(); - - wdrtas_unregister_devs(); - - unregister_reboot_notifier(&wdrtas_notifier); -} - -module_init(wdrtas_init); -module_exit(wdrtas_exit); diff --git a/drivers/char/watchdog/wdt.c b/drivers/char/watchdog/wdt.c deleted file mode 100644 index 0a3de6a..0000000 --- a/drivers/char/watchdog/wdt.c +++ /dev/null @@ -1,640 +0,0 @@ -/* - * Industrial Computer Source WDT500/501 driver - * - * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * Release 0.10. - * - * Fixes - * Dave Gregorich : Modularisation and minor bugs - * Alan Cox : Added the watchdog ioctl() stuff - * Alan Cox : Fixed the reboot problem (as noted by - * Matt Crocker). - * Alan Cox : Added wdt= boot option - * Alan Cox : Cleaned up copy/user stuff - * Tim Hockin : Added insmod parameters, comment cleanup - * Parameterized timeout - * Tigran Aivazian : Restructured wdt_init() to handle failures - * Joel Becker : Added WDIOC_GET/SETTIMEOUT - * Matt Domsch : Added nowayout module option - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "wd501p.h" - -static unsigned long wdt_is_open; -static char expect_close; - -/* - * Module parameters - */ - -#define WD_TIMO 60 /* Default heartbeat = 60 seconds */ - -static int heartbeat = WD_TIMO; -static int wd_heartbeat; -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0>8, WDT_COUNT0+ctr); -} - -/** - * wdt_start: - * - * Start the watchdog driver. - */ - -static int wdt_start(void) -{ - inb_p(WDT_DC); /* Disable watchdog */ - wdt_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ - wdt_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ - wdt_ctr_mode(2,0); /* Program CTR2 for Mode 0: Pulse on Terminal Count */ - wdt_ctr_load(0, 8948); /* Count at 100Hz */ - wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ - wdt_ctr_load(2,65535); /* Length of reset pulse */ - outb_p(0, WDT_DC); /* Enable watchdog */ - return 0; -} - -/** - * wdt_stop: - * - * Stop the watchdog driver. - */ - -static int wdt_stop (void) -{ - /* Turn the card off */ - inb_p(WDT_DC); /* Disable watchdog */ - wdt_ctr_load(2,0); /* 0 length reset pulses now */ - return 0; -} - -/** - * wdt_ping: - * - * Reload counter one with the watchdog heartbeat. We don't bother reloading - * the cascade counter. - */ - -static int wdt_ping(void) -{ - /* Write a watchdog value */ - inb_p(WDT_DC); /* Disable watchdog */ - wdt_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ - wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ - outb_p(0, WDT_DC); /* Enable watchdog */ - return 0; -} - -/** - * wdt_set_heartbeat: - * @t: the new heartbeat value that needs to be set. - * - * Set a new heartbeat value for the watchdog device. If the heartbeat value is - * incorrect we keep the old value and return -EINVAL. If successfull we - * return 0. - */ -static int wdt_set_heartbeat(int t) -{ - if ((t < 1) || (t > 65535)) - return -EINVAL; - - heartbeat = t; - wd_heartbeat = t * 100; - return 0; -} - -/** - * wdt_get_status: - * @status: the new status. - * - * Extract the status information from a WDT watchdog device. There are - * several board variants so we have to know which bits are valid. Some - * bits default to one and some to zero in order to be maximally painful. - * - * we then map the bits onto the status ioctl flags. - */ - -static int wdt_get_status(int *status) -{ - unsigned char new_status=inb_p(WDT_SR); - - *status=0; - if (new_status & WDC_SR_ISOI0) - *status |= WDIOF_EXTERN1; - if (new_status & WDC_SR_ISII1) - *status |= WDIOF_EXTERN2; -#ifdef CONFIG_WDT_501 - if (!(new_status & WDC_SR_TGOOD)) - *status |= WDIOF_OVERHEAT; - if (!(new_status & WDC_SR_PSUOVER)) - *status |= WDIOF_POWEROVER; - if (!(new_status & WDC_SR_PSUUNDR)) - *status |= WDIOF_POWERUNDER; - if (tachometer) { - if (!(new_status & WDC_SR_FANGOOD)) - *status |= WDIOF_FANFAULT; - } -#endif /* CONFIG_WDT_501 */ - return 0; -} - -#ifdef CONFIG_WDT_501 -/** - * wdt_get_temperature: - * - * Reports the temperature in degrees Fahrenheit. The API is in - * farenheit. It was designed by an imperial measurement luddite. - */ - -static int wdt_get_temperature(int *temperature) -{ - unsigned short c=inb_p(WDT_RT); - - *temperature = (c * 11 / 15) + 7; - return 0; -} -#endif /* CONFIG_WDT_501 */ - -/** - * wdt_interrupt: - * @irq: Interrupt number - * @dev_id: Unused as we don't allow multiple devices. - * - * Handle an interrupt from the board. These are raised when the status - * map changes in what the board considers an interesting way. That means - * a failure condition occurring. - */ - -static irqreturn_t wdt_interrupt(int irq, void *dev_id) -{ - /* - * Read the status register see what is up and - * then printk it. - */ - unsigned char status=inb_p(WDT_SR); - - printk(KERN_CRIT "WDT status %d\n", status); - -#ifdef CONFIG_WDT_501 - if (!(status & WDC_SR_TGOOD)) - printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT)); - if (!(status & WDC_SR_PSUOVER)) - printk(KERN_CRIT "PSU over voltage.\n"); - if (!(status & WDC_SR_PSUUNDR)) - printk(KERN_CRIT "PSU under voltage.\n"); - if (tachometer) { - if (!(status & WDC_SR_FANGOOD)) - printk(KERN_CRIT "Possible fan fault.\n"); - } -#endif /* CONFIG_WDT_501 */ - if (!(status & WDC_SR_WCCR)) -#ifdef SOFTWARE_REBOOT -#ifdef ONLY_TESTING - printk(KERN_CRIT "Would Reboot.\n"); -#else - printk(KERN_CRIT "Initiating system reboot.\n"); - emergency_restart(); -#endif -#else - printk(KERN_CRIT "Reset in 5ms.\n"); -#endif - return IRQ_HANDLED; -} - - -/** - * wdt_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -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; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - wdt_ping(); - } - return count; -} - -/** - * wdt_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. We only actually usefully support - * querying capabilities and current status. - */ - -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_heartbeat; - int status; - - static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT| - WDIOF_MAGICCLOSE| - WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = "WDT500/501", - }; - - /* Add options according to the card we have */ - ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); -#ifdef CONFIG_WDT_501 - ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); - if (tachometer) - ident.options |= WDIOF_FANFAULT; -#endif /* CONFIG_WDT_501 */ - - switch(cmd) - { - default: - return -ENOTTY; - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; - - case WDIOC_GETSTATUS: - wdt_get_status(&status); - return put_user(status, p); - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_KEEPALIVE: - wdt_ping(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_heartbeat, p)) - return -EFAULT; - - if (wdt_set_heartbeat(new_heartbeat)) - return -EINVAL; - - wdt_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - } -} - -/** - * wdt_open: - * @inode: inode of device - * @file: file handle to device - * - * The watchdog device has been opened. The watchdog device is single - * open and on opening we load the counters. Counter zero is a 100Hz - * cascade, into counter 1 which downcounts to reboot. When the counter - * triggers counter 2 downcounts the length of the reset pulse which - * set set to be as long as possible. - */ - -static int wdt_open(struct inode *inode, struct file *file) -{ - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - /* - * Activate - */ - wdt_start(); - return nonseekable_open(inode, file); -} - -/** - * wdt_release: - * @inode: inode to board - * @file: file handle to board - * - * The watchdog has a configurable API. There is a religious dispute - * between people who want their watchdog to be able to shut down and - * those who want to be sure if the watchdog manager dies the machine - * reboots. In the former case we disable the counters, in the latter - * case you have to open it again very soon. - */ - -static int wdt_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - wdt_stop(); - clear_bit(0, &wdt_is_open); - } else { - printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); - wdt_ping(); - } - expect_close = 0; - return 0; -} - -#ifdef CONFIG_WDT_501 -/** - * wdt_temp_read: - * @file: file handle to the watchdog board - * @buf: buffer to write 1 byte into - * @count: length of buffer - * @ptr: offset (no seek allowed) - * - * Temp_read reports the temperature in degrees Fahrenheit. The API is in - * farenheit. It was designed by an imperial measurement luddite. - */ - -static ssize_t wdt_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) -{ - int temperature; - - if (wdt_get_temperature(&temperature)) - return -EFAULT; - - if (copy_to_user (buf, &temperature, 1)) - return -EFAULT; - - return 1; -} - -/** - * wdt_temp_open: - * @inode: inode of device - * @file: file handle to device - * - * The temperature device has been opened. - */ - -static int wdt_temp_open(struct inode *inode, struct file *file) -{ - return nonseekable_open(inode, file); -} - -/** - * wdt_temp_release: - * @inode: inode to board - * @file: file handle to board - * - * The temperature device has been closed. - */ - -static int wdt_temp_release(struct inode *inode, struct file *file) -{ - return 0; -} -#endif /* CONFIG_WDT_501 */ - -/** - * notify_sys: - * @this: our notifier block - * @code: the event being reported - * @unused: unused - * - * Our notifier is called on system shutdowns. We want to turn the card - * off at reboot otherwise the machine will reboot again during memory - * test or worse yet during the following fsck. This would suck, in fact - * trust me - if it happens it does suck. - */ - -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code==SYS_DOWN || code==SYS_HALT) { - /* Turn the card off */ - wdt_stop(); - } - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - - -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wdt_write, - .ioctl = wdt_ioctl, - .open = wdt_open, - .release = wdt_release, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - -#ifdef CONFIG_WDT_501 -static const struct file_operations wdt_temp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = wdt_temp_read, - .open = wdt_temp_open, - .release = wdt_temp_release, -}; - -static struct miscdevice temp_miscdev = { - .minor = TEMP_MINOR, - .name = "temperature", - .fops = &wdt_temp_fops, -}; -#endif /* CONFIG_WDT_501 */ - -/* - * The WDT card 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, -}; - -/** - * cleanup_module: - * - * Unload the watchdog. You cannot do this with any file handles open. - * If your watchdog is set to continue ticking on close and you unload - * it, well it keeps ticking. We won't get the interrupt but the board - * will not touch PC memory so all is fine. You just have to load a new - * module in 60 seconds or reboot. - */ - -static void __exit wdt_exit(void) -{ - misc_deregister(&wdt_miscdev); -#ifdef CONFIG_WDT_501 - misc_deregister(&temp_miscdev); -#endif /* CONFIG_WDT_501 */ - unregister_reboot_notifier(&wdt_notifier); - free_irq(irq, NULL); - release_region(io,8); -} - -/** - * wdt_init: - * - * Set up the WDT watchdog board. All we have to do is grab the - * resources we require and bitch if anyone beat us to them. - * The open() function will actually kick the board off. - */ - -static int __init wdt_init(void) -{ - int ret; - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (wdt_set_heartbeat(heartbeat)) { - wdt_set_heartbeat(WD_TIMO); - printk(KERN_INFO "wdt: heartbeat value must be 0, 1998 - * - * 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 -#include - -/* - * Define this to stop the watchdog actually rebooting the machine. - */ -#undef ONLY_TESTING - -static unsigned int soft_margin = 60; /* in seconds */ -static unsigned int reload; -static unsigned long timer_alive; - -#ifdef ONLY_TESTING -/* - * If the timer expires.. - */ -static void watchdog_fire(int irq, void *dev_id) -{ - printk(KERN_CRIT "Watchdog: Would Reboot.\n"); - *CSR_TIMER4_CNTL = 0; - *CSR_TIMER4_CLR = 0; -} -#endif - -/* - * Refresh the timer. - */ -static void watchdog_ping(void) -{ - *CSR_TIMER4_LOAD = reload; -} - -/* - * Allow only one person to hold it open - */ -static int watchdog_open(struct inode *inode, struct file *file) -{ - int ret; - - if (*CSR_SA110_CNTL & (1 << 13)) - return -EBUSY; - - if (test_and_set_bit(1, &timer_alive)) - return -EBUSY; - - reload = soft_margin * (mem_fclk_21285 / 256); - - *CSR_TIMER4_CLR = 0; - watchdog_ping(); - *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD - | TIMER_CNTL_DIV256; - -#ifdef ONLY_TESTING - ret = request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL); - if (ret) { - *CSR_TIMER4_CNTL = 0; - clear_bit(1, &timer_alive); - } -#else - /* - * Setting this bit is irreversible; once enabled, there is - * no way to disable the watchdog. - */ - *CSR_SA110_CNTL |= 1 << 13; - - ret = 0; -#endif - nonseekable_open(inode, file); - return ret; -} - -/* - * Shut off the timer. - * Note: if we really have enabled the watchdog, there - * is no way to turn off. - */ -static int watchdog_release(struct inode *inode, struct file *file) -{ -#ifdef ONLY_TESTING - free_irq(IRQ_TIMER4, NULL); - clear_bit(1, &timer_alive); -#endif - return 0; -} - -static ssize_t -watchdog_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - /* - * Refresh the timer. - */ - if (len) - watchdog_ping(); - - return len; -} - -static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT, - .identity = "Footbridge Watchdog", -}; - -static int -watchdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - unsigned int new_margin; - int ret = -ENOTTY; - - switch(cmd) { - case WDIOC_GETSUPPORT: - ret = 0; - if (copy_to_user((void *)arg, &ident, sizeof(ident))) - ret = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0,(int *)arg); - break; - - case WDIOC_KEEPALIVE: - watchdog_ping(); - ret = 0; - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(new_margin, (int *)arg); - if (ret) - break; - - /* Arbitrary, can't find the card's limits */ - if (new_margin < 0 || new_margin > 60) { - ret = -EINVAL; - break; - } - - soft_margin = new_margin; - reload = soft_margin * (mem_fclk_21285 / 256); - watchdog_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - ret = put_user(soft_margin, (int *)arg); - break; - } - return ret; -} - -static const 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, -}; - -static int __init footbridge_watchdog_init(void) -{ - int retval; - - if (machine_is_netwinder()) - return -ENODEV; - - retval = misc_register(&watchdog_miscdev); - if (retval < 0) - return retval; - - printk("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n", - soft_margin); - - if (machine_is_cats()) - printk("Warning: Watchdog reset may not work on this machine.\n"); - return 0; -} - -static void __exit footbridge_watchdog_exit(void) -{ - misc_deregister(&watchdog_miscdev); -} - -MODULE_AUTHOR("Phil Blundell "); -MODULE_DESCRIPTION("Footbridge watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -module_param(soft_margin, int, 0); -MODULE_PARM_DESC(soft_margin,"Watchdog timeout in seconds"); - -module_init(footbridge_watchdog_init); -module_exit(footbridge_watchdog_exit); diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c deleted file mode 100644 index 7d300ff..0000000 --- a/drivers/char/watchdog/wdt977.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Wdt977 0.04: A Watchdog Device for Netwinder W83977AF chip - * - * (c) Copyright 1998 Rebel.com (Woody Suwalski ) - * - * ----------------------- - * - * 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. - * - * ----------------------- - * 14-Dec-2001 Matt Domsch - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT - * 19-Dec-2001 Woody Suwalski: Netwinder fixes, ioctl interface - * 06-Jan-2002 Woody Suwalski: For compatibility, convert all timeouts - * from minutes to seconds. - * 07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in - * nwwatchdog_init. - * 25-Oct-2005 Woody Suwalski: Convert addresses to #defs, add spinlocks - * remove limitiation to be used on Netwinders only - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define WATCHDOG_VERSION "0.04" -#define WATCHDOG_NAME "Wdt977" -#define PFX WATCHDOG_NAME ": " -#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" - -#define IO_INDEX_PORT 0x370 /* on some systems it can be 0x3F0 */ -#define IO_DATA_PORT (IO_INDEX_PORT+1) - -#define UNLOCK_DATA 0x87 -#define LOCK_DATA 0xAA -#define DEVICE_REGISTER 0x07 - - -#define DEFAULT_TIMEOUT 60 /* default timeout in seconds */ - -static int timeout = DEFAULT_TIMEOUT; -static int timeoutM; /* timeout in minutes */ -static unsigned long timer_alive; -static int testmode; -static char expect_close; -static spinlock_t spinlock; - -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); -module_param(testmode, int, 0); -MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); - -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) ")"); - -/* - * Start the watchdog - */ - -static int wdt977_start(void) -{ - unsigned long flags; - - spin_lock_irqsave(&spinlock, flags); - - /* unlock the SuperIO chip */ - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - - /* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4 - * F2 has the timeout in minutes - * F3 could be set to the POWER LED blink (with GP17 set to PowerLed) - * at timeout, and to reset timer on kbd/mouse activity (not impl.) - * F4 is used to just clear the TIMEOUT'ed state (bit 0) - */ - outb_p(DEVICE_REGISTER, IO_INDEX_PORT); - outb_p(0x08, IO_DATA_PORT); - outb_p(0xF2, IO_INDEX_PORT); - outb_p(timeoutM, IO_DATA_PORT); - outb_p(0xF3, IO_INDEX_PORT); - outb_p(0x00, IO_DATA_PORT); /* another setting is 0E for kbd/mouse/LED */ - outb_p(0xF4, IO_INDEX_PORT); - outb_p(0x00, IO_DATA_PORT); - - /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ - /* in test mode watch the bit 1 on F4 to indicate "triggered" */ - if (!testmode) - { - outb_p(DEVICE_REGISTER, IO_INDEX_PORT); - outb_p(0x07, IO_DATA_PORT); - outb_p(0xE6, IO_INDEX_PORT); - outb_p(0x08, IO_DATA_PORT); - } - - /* lock the SuperIO chip */ - outb_p(LOCK_DATA, IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - printk(KERN_INFO PFX "activated.\n"); - - return 0; -} - -/* - * Stop the watchdog - */ - -static int wdt977_stop(void) -{ - unsigned long flags; - spin_lock_irqsave(&spinlock, flags); - - /* unlock the SuperIO chip */ - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - - /* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 - * F3 is reset to its default state - * F4 can clear the TIMEOUT'ed state (bit 0) - back to default - * We can not use GP17 as a PowerLed, as we use its usage as a RedLed - */ - outb_p(DEVICE_REGISTER, IO_INDEX_PORT); - outb_p(0x08, IO_DATA_PORT); - outb_p(0xF2, IO_INDEX_PORT); - outb_p(0xFF, IO_DATA_PORT); - outb_p(0xF3, IO_INDEX_PORT); - outb_p(0x00, IO_DATA_PORT); - outb_p(0xF4, IO_INDEX_PORT); - outb_p(0x00, IO_DATA_PORT); - outb_p(0xF2, IO_INDEX_PORT); - outb_p(0x00, IO_DATA_PORT); - - /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ - outb_p(DEVICE_REGISTER, IO_INDEX_PORT); - outb_p(0x07, IO_DATA_PORT); - outb_p(0xE6, IO_INDEX_PORT); - outb_p(0x08, IO_DATA_PORT); - - /* lock the SuperIO chip */ - outb_p(LOCK_DATA, IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - printk(KERN_INFO PFX "shutdown.\n"); - - return 0; -} - -/* - * Send a keepalive ping to the watchdog - * This is done by simply re-writing the timeout to reg. 0xF2 - */ - -static int wdt977_keepalive(void) -{ - unsigned long flags; - spin_lock_irqsave(&spinlock, flags); - - /* unlock the SuperIO chip */ - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - - /* select device Aux2 (device=8) and kicks watchdog reg F2 */ - /* F2 has the timeout in minutes */ - outb_p(DEVICE_REGISTER, IO_INDEX_PORT); - outb_p(0x08, IO_DATA_PORT); - outb_p(0xF2, IO_INDEX_PORT); - outb_p(timeoutM, IO_DATA_PORT); - - /* lock the SuperIO chip */ - outb_p(LOCK_DATA, IO_INDEX_PORT); - spin_unlock_irqrestore(&spinlock, flags); - - return 0; -} - -/* - * Set the watchdog timeout value - */ - -static int wdt977_set_timeout(int t) -{ - int tmrval; - - /* convert seconds to minutes, rounding up */ - tmrval = (t + 59) / 60; - - if (machine_is_netwinder()) { - /* we have a hw bug somewhere, so each 977 minute is actually only 30sec - * this limits the max timeout to half of device max of 255 minutes... - */ - tmrval += tmrval; - } - - if ((tmrval < 1) || (tmrval > 255)) - return -EINVAL; - - /* timeout is the timeout in seconds, timeoutM is the timeout in minutes) */ - timeout = t; - timeoutM = tmrval; - return 0; -} - -/* - * Get the watchdog status - */ - -static int wdt977_get_status(int *status) -{ - int new_status; - unsigned long flags; - - spin_lock_irqsave(&spinlock, flags); - - /* unlock the SuperIO chip */ - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - outb_p(UNLOCK_DATA, IO_INDEX_PORT); - - /* select device Aux2 (device=8) and read watchdog reg F4 */ - outb_p(DEVICE_REGISTER, IO_INDEX_PORT); - outb_p(0x08, IO_DATA_PORT); - outb_p(0xF4, IO_INDEX_PORT); - new_status = inb_p(IO_DATA_PORT); - - /* lock the SuperIO chip */ - outb_p(LOCK_DATA, IO_INDEX_PORT); - - spin_unlock_irqrestore(&spinlock, flags); - - *status=0; - if (new_status & 1) - *status |= WDIOF_CARDRESET; - - return 0; -} - - -/* - * /dev/watchdog handling - */ - -static int wdt977_open(struct inode *inode, struct file *file) -{ - /* If the watchdog is alive we don't need to start it again */ - if( test_and_set_bit(0,&timer_alive) ) - return -EBUSY; - - if (nowayout) - __module_get(THIS_MODULE); - - wdt977_start(); - return nonseekable_open(inode, file); -} - -static int wdt977_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - * Lock it in if it's a module and we set nowayout - */ - if (expect_close == 42) - { - wdt977_stop(); - clear_bit(0,&timer_alive); - } else { - wdt977_keepalive(); - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); - } - expect_close = 0; - return 0; -} - - -/* - * wdt977_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t wdt977_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (count) - { - if (!nowayout) - { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != count; i++) - { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - - /* someone wrote to us, we should restart timer */ - wdt977_keepalive(); - } - return count; -} - -/* - * wdt977_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - */ - -static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_MAGICCLOSE | - WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = WATCHDOG_NAME, -}; - -static int wdt977_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int status; - int new_options, retval = -EINVAL; - int new_timeout; - union { - struct watchdog_info __user *ident; - int __user *i; - } uarg; - - 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: - wdt977_get_status(&status); - return put_user(status, uarg.i); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, uarg.i); - - case WDIOC_KEEPALIVE: - wdt977_keepalive(); - return 0; - - case WDIOC_SETOPTIONS: - if (get_user (new_options, uarg.i)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) { - wdt977_stop(); - retval = 0; - } - - if (new_options & WDIOS_ENABLECARD) { - wdt977_start(); - retval = 0; - } - - return retval; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, uarg.i)) - return -EFAULT; - - if (wdt977_set_timeout(new_timeout)) - return -EINVAL; - - wdt977_keepalive(); - /* Fall */ - - case WDIOC_GETTIMEOUT: - return put_user(timeout, uarg.i); - - } -} - -static int wdt977_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if(code==SYS_DOWN || code==SYS_HALT) - wdt977_stop(); - return NOTIFY_DONE; -} - -static const struct file_operations wdt977_fops= -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wdt977_write, - .ioctl = wdt977_ioctl, - .open = wdt977_open, - .release = wdt977_release, -}; - -static struct miscdevice wdt977_miscdev= -{ - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt977_fops, -}; - -static struct notifier_block wdt977_notifier = { - .notifier_call = wdt977_notify_sys, -}; - -static int __init wd977_init(void) -{ - int rc; - - //if (!machine_is_netwinder()) - // return -ENODEV; - - printk(KERN_INFO PFX DRIVER_VERSION); - - spin_lock_init(&spinlock); - - /* Check that the timeout value is within it's range ; if not reset to the default */ - if (wdt977_set_timeout(timeout)) - { - wdt977_set_timeout(DEFAULT_TIMEOUT); - printk(KERN_INFO PFX "timeout value must be 60"); -MODULE_DESCRIPTION("W83977AF Watchdog driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/wdt_pci.c b/drivers/char/watchdog/wdt_pci.c deleted file mode 100644 index 6baf4ae..0000000 --- a/drivers/char/watchdog/wdt_pci.c +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Industrial Computer Source PCI-WDT500/501 driver - * - * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. - * http://www.redhat.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide - * warranty for any of this software. This material is provided - * "AS-IS" and at no charge. - * - * (c) Copyright 1995 Alan Cox - * - * Release 0.10. - * - * Fixes - * Dave Gregorich : Modularisation and minor bugs - * Alan Cox : Added the watchdog ioctl() stuff - * Alan Cox : Fixed the reboot problem (as noted by - * Matt Crocker). - * Alan Cox : Added wdt= boot option - * Alan Cox : Cleaned up copy/user stuff - * Tim Hockin : Added insmod parameters, comment cleanup - * Parameterized timeout - * JP Nollmann : Added support for PCI wdt501p - * Alan Cox : Split ISA and PCI cards into two drivers - * Jeff Garzik : PCI cleanups - * Tigran Aivazian : Restructured wdtpci_init_one() to handle failures - * Joel Becker : Added WDIOC_GET/SETTIMEOUT - * Zwane Mwaikambo : Magic char closing, locking changes, cleanups - * Matt Domsch : nowayout module option - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define WDT_IS_PCI -#include "wd501p.h" - -#define PFX "wdt_pci: " - -/* - * Until Access I/O gets their application for a PCI vendor ID approved, - * I don't think that it's appropriate to move these constants into the - * regular pci_ids.h file. -- JPN 2000/01/18 - */ - -#ifndef PCI_VENDOR_ID_ACCESSIO -#define PCI_VENDOR_ID_ACCESSIO 0x494f -#endif -#ifndef PCI_DEVICE_ID_WDG_CSM -#define PCI_DEVICE_ID_WDG_CSM 0x22c0 -#endif - -/* We can only use 1 card due to the /dev/watchdog restriction */ -static int dev_count; - -static struct semaphore open_sem; -static spinlock_t wdtpci_lock; -static char expect_close; - -static int io; -static int irq; - -/* Default timeout */ -#define WD_TIMO 60 /* Default heartbeat = 60 seconds */ - -static int heartbeat = WD_TIMO; -static int wd_heartbeat; -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0>8, WDT_COUNT0+ctr); -} - -/** - * wdtpci_start: - * - * Start the watchdog driver. - */ - -static int wdtpci_start(void) -{ - unsigned long flags; - - spin_lock_irqsave(&wdtpci_lock, flags); - - /* - * "pet" the watchdog, as Access says. - * This resets the clock outputs. - */ - inb_p(WDT_DC); /* Disable watchdog */ - wdtpci_ctr_mode(2,0); /* Program CTR2 for Mode 0: Pulse on Terminal Count */ - outb_p(0, WDT_DC); /* Enable watchdog */ - - inb_p(WDT_DC); /* Disable watchdog */ - outb_p(0, WDT_CLOCK); /* 2.0833MHz clock */ - inb_p(WDT_BUZZER); /* disable */ - inb_p(WDT_OPTONOTRST); /* disable */ - inb_p(WDT_OPTORST); /* disable */ - inb_p(WDT_PROGOUT); /* disable */ - wdtpci_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ - wdtpci_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ - wdtpci_ctr_mode(2,1); /* Program CTR2 for Mode 1: Retriggerable One-Shot */ - wdtpci_ctr_load(0,20833); /* count at 100Hz */ - wdtpci_ctr_load(1,wd_heartbeat);/* Heartbeat */ - /* DO NOT LOAD CTR2 on PCI card! -- JPN */ - outb_p(0, WDT_DC); /* Enable watchdog */ - - spin_unlock_irqrestore(&wdtpci_lock, flags); - return 0; -} - -/** - * wdtpci_stop: - * - * Stop the watchdog driver. - */ - -static int wdtpci_stop (void) -{ - unsigned long flags; - - /* Turn the card off */ - spin_lock_irqsave(&wdtpci_lock, flags); - inb_p(WDT_DC); /* Disable watchdog */ - wdtpci_ctr_load(2,0); /* 0 length reset pulses now */ - spin_unlock_irqrestore(&wdtpci_lock, flags); - return 0; -} - -/** - * wdtpci_ping: - * - * Reload counter one with the watchdog heartbeat. We don't bother reloading - * the cascade counter. - */ - -static int wdtpci_ping(void) -{ - unsigned long flags; - - /* Write a watchdog value */ - spin_lock_irqsave(&wdtpci_lock, flags); - inb_p(WDT_DC); /* Disable watchdog */ - wdtpci_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ - wdtpci_ctr_load(1,wd_heartbeat);/* Heartbeat */ - outb_p(0, WDT_DC); /* Enable watchdog */ - spin_unlock_irqrestore(&wdtpci_lock, flags); - return 0; -} - -/** - * wdtpci_set_heartbeat: - * @t: the new heartbeat value that needs to be set. - * - * Set a new heartbeat value for the watchdog device. If the heartbeat value is - * incorrect we keep the old value and return -EINVAL. If successfull we - * return 0. - */ -static int wdtpci_set_heartbeat(int t) -{ - /* Arbitrary, can't find the card's limits */ - if ((t < 1) || (t > 65535)) - return -EINVAL; - - heartbeat = t; - wd_heartbeat = t * 100; - return 0; -} - -/** - * wdtpci_get_status: - * @status: the new status. - * - * Extract the status information from a WDT watchdog device. There are - * several board variants so we have to know which bits are valid. Some - * bits default to one and some to zero in order to be maximally painful. - * - * we then map the bits onto the status ioctl flags. - */ - -static int wdtpci_get_status(int *status) -{ - unsigned char new_status=inb_p(WDT_SR); - - *status=0; - if (new_status & WDC_SR_ISOI0) - *status |= WDIOF_EXTERN1; - if (new_status & WDC_SR_ISII1) - *status |= WDIOF_EXTERN2; -#ifdef CONFIG_WDT_501_PCI - if (!(new_status & WDC_SR_TGOOD)) - *status |= WDIOF_OVERHEAT; - if (!(new_status & WDC_SR_PSUOVER)) - *status |= WDIOF_POWEROVER; - if (!(new_status & WDC_SR_PSUUNDR)) - *status |= WDIOF_POWERUNDER; - if (tachometer) { - if (!(new_status & WDC_SR_FANGOOD)) - *status |= WDIOF_FANFAULT; - } -#endif /* CONFIG_WDT_501_PCI */ - return 0; -} - -#ifdef CONFIG_WDT_501_PCI -/** - * wdtpci_get_temperature: - * - * Reports the temperature in degrees Fahrenheit. The API is in - * farenheit. It was designed by an imperial measurement luddite. - */ - -static int wdtpci_get_temperature(int *temperature) -{ - unsigned short c=inb_p(WDT_RT); - - *temperature = (c * 11 / 15) + 7; - return 0; -} -#endif /* CONFIG_WDT_501_PCI */ - -/** - * wdtpci_interrupt: - * @irq: Interrupt number - * @dev_id: Unused as we don't allow multiple devices. - * - * Handle an interrupt from the board. These are raised when the status - * map changes in what the board considers an interesting way. That means - * a failure condition occurring. - */ - -static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) -{ - /* - * Read the status register see what is up and - * then printk it. - */ - unsigned char status=inb_p(WDT_SR); - - printk(KERN_CRIT PFX "status %d\n", status); - -#ifdef CONFIG_WDT_501_PCI - if (!(status & WDC_SR_TGOOD)) - printk(KERN_CRIT PFX "Overheat alarm.(%d)\n",inb_p(WDT_RT)); - if (!(status & WDC_SR_PSUOVER)) - printk(KERN_CRIT PFX "PSU over voltage.\n"); - if (!(status & WDC_SR_PSUUNDR)) - printk(KERN_CRIT PFX "PSU under voltage.\n"); - if (tachometer) { - if (!(status & WDC_SR_FANGOOD)) - printk(KERN_CRIT PFX "Possible fan fault.\n"); - } -#endif /* CONFIG_WDT_501_PCI */ - if (!(status&WDC_SR_WCCR)) -#ifdef SOFTWARE_REBOOT -#ifdef ONLY_TESTING - printk(KERN_CRIT PFX "Would Reboot.\n"); -#else - printk(KERN_CRIT PFX "Initiating system reboot.\n"); - emergency_restart(NULL); -#endif -#else - printk(KERN_CRIT PFX "Reset in 5ms.\n"); -#endif - return IRQ_HANDLED; -} - - -/** - * wdtpci_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t wdtpci_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; - } - } - wdtpci_ping(); - } - - return count; -} - -/** - * wdtpci_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. We only actually usefully support - * querying capabilities and current status. - */ - -static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int new_heartbeat; - int status; - void __user *argp = (void __user *)arg; - int __user *p = argp; - - static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT| - WDIOF_MAGICCLOSE| - WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = "PCI-WDT500/501", - }; - - /* Add options according to the card we have */ - ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); -#ifdef CONFIG_WDT_501_PCI - ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); - if (tachometer) - ident.options |= WDIOF_FANFAULT; -#endif /* CONFIG_WDT_501_PCI */ - - switch(cmd) - { - default: - return -ENOTTY; - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; - - case WDIOC_GETSTATUS: - wdtpci_get_status(&status); - return put_user(status, p); - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_KEEPALIVE: - wdtpci_ping(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_heartbeat, p)) - return -EFAULT; - - if (wdtpci_set_heartbeat(new_heartbeat)) - return -EINVAL; - - wdtpci_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - } -} - -/** - * wdtpci_open: - * @inode: inode of device - * @file: file handle to device - * - * The watchdog device has been opened. The watchdog device is single - * open and on opening we load the counters. Counter zero is a 100Hz - * cascade, into counter 1 which downcounts to reboot. When the counter - * triggers counter 2 downcounts the length of the reset pulse which - * set set to be as long as possible. - */ - -static int wdtpci_open(struct inode *inode, struct file *file) -{ - if (down_trylock(&open_sem)) - return -EBUSY; - - if (nowayout) { - __module_get(THIS_MODULE); - } - /* - * Activate - */ - wdtpci_start(); - return nonseekable_open(inode, file); -} - -/** - * wdtpci_release: - * @inode: inode to board - * @file: file handle to board - * - * The watchdog has a configurable API. There is a religious dispute - * between people who want their watchdog to be able to shut down and - * those who want to be sure if the watchdog manager dies the machine - * reboots. In the former case we disable the counters, in the latter - * case you have to open it again very soon. - */ - -static int wdtpci_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - wdtpci_stop(); - } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping timer!"); - wdtpci_ping(); - } - expect_close = 0; - up(&open_sem); - return 0; -} - -#ifdef CONFIG_WDT_501_PCI -/** - * wdtpci_temp_read: - * @file: file handle to the watchdog board - * @buf: buffer to write 1 byte into - * @count: length of buffer - * @ptr: offset (no seek allowed) - * - * Read reports the temperature in degrees Fahrenheit. The API is in - * fahrenheit. It was designed by an imperial measurement luddite. - */ - -static ssize_t wdtpci_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) -{ - int temperature; - - if (wdtpci_get_temperature(&temperature)) - return -EFAULT; - - if (copy_to_user (buf, &temperature, 1)) - return -EFAULT; - - return 1; -} - -/** - * wdtpci_temp_open: - * @inode: inode of device - * @file: file handle to device - * - * The temperature device has been opened. - */ - -static int wdtpci_temp_open(struct inode *inode, struct file *file) -{ - return nonseekable_open(inode, file); -} - -/** - * wdtpci_temp_release: - * @inode: inode to board - * @file: file handle to board - * - * The temperature device has been closed. - */ - -static int wdtpci_temp_release(struct inode *inode, struct file *file) -{ - return 0; -} -#endif /* CONFIG_WDT_501_PCI */ - -/** - * notify_sys: - * @this: our notifier block - * @code: the event being reported - * @unused: unused - * - * Our notifier is called on system shutdowns. We want to turn the card - * off at reboot otherwise the machine will reboot again during memory - * test or worse yet during the following fsck. This would suck, in fact - * trust me - if it happens it does suck. - */ - -static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code==SYS_DOWN || code==SYS_HALT) { - /* Turn the card off */ - wdtpci_stop(); - } - return NOTIFY_DONE; -} - -/* - * Kernel Interfaces - */ - - -static const struct file_operations wdtpci_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wdtpci_write, - .ioctl = wdtpci_ioctl, - .open = wdtpci_open, - .release = wdtpci_release, -}; - -static struct miscdevice wdtpci_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdtpci_fops, -}; - -#ifdef CONFIG_WDT_501_PCI -static const struct file_operations wdtpci_temp_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = wdtpci_temp_read, - .open = wdtpci_temp_open, - .release = wdtpci_temp_release, -}; - -static struct miscdevice temp_miscdev = { - .minor = TEMP_MINOR, - .name = "temperature", - .fops = &wdtpci_temp_fops, -}; -#endif /* CONFIG_WDT_501_PCI */ - -/* - * The WDT card needs to learn about soft shutdowns in order to - * turn the timebomb registers off. - */ - -static struct notifier_block wdtpci_notifier = { - .notifier_call = wdtpci_notify_sys, -}; - - -static int __devinit wdtpci_init_one (struct pci_dev *dev, - const struct pci_device_id *ent) -{ - int ret = -EIO; - - dev_count++; - if (dev_count > 1) { - printk (KERN_ERR PFX "this driver only supports 1 device\n"); - return -ENODEV; - } - - if (pci_enable_device (dev)) { - printk (KERN_ERR PFX "Not possible to enable PCI Device\n"); - return -ENODEV; - } - - if (pci_resource_start (dev, 2) == 0x0000) { - printk (KERN_ERR PFX "No I/O-Address for card detected\n"); - ret = -ENODEV; - goto out_pci; - } - - sema_init(&open_sem, 1); - spin_lock_init(&wdtpci_lock); - - irq = dev->irq; - io = pci_resource_start (dev, 2); - - if (request_region (io, 16, "wdt_pci") == NULL) { - printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", io); - goto out_pci; - } - - if (request_irq (irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED, - "wdt_pci", &wdtpci_miscdev)) { - printk (KERN_ERR PFX "IRQ %d is not free\n", irq); - goto out_reg; - } - - printk ("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)\n", - io, irq); - - /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (wdtpci_set_heartbeat(heartbeat)) { - wdtpci_set_heartbeat(WD_TIMO); - printk(KERN_INFO PFX "heartbeat value must be 0 + in the kernel source. + + The watchdog is usually used together with the watchdog daemon + which is available from + . This daemon can + also monitor NFS connections and can reboot the machine when the process + table is full. + + If unsure, say N. + +if WATCHDOG + +config WATCHDOG_NOWAYOUT + bool "Disable watchdog shutdown on close" + help + The default watchdog behaviour (which you get if you say N here) is + to stop the timer if the process managing it closes the file + /dev/watchdog. It's always remotely possible that this process might + get killed. If you say Y here, the watchdog cannot be stopped once + it has been started. + +# +# General Watchdog drivers +# + +comment "Watchdog Device Drivers" + +# Architecture Independent + +config SOFT_WATCHDOG + tristate "Software watchdog" + help + A software monitoring watchdog. This will fail to reboot your system + from some situations that the hardware watchdog will recover + from. Equally it's a lot cheaper to install. + + To compile this driver as a module, choose M here: the + module will be called softdog. + +# ALPHA Architecture + +# ARM Architecture + +config AT91RM9200_WATCHDOG + tristate "AT91RM9200 watchdog" + depends on ARCH_AT91RM9200 + help + Watchdog timer embedded into AT91RM9200 chips. This will reboot your + system when the timeout is reached. + +config 21285_WATCHDOG + tristate "DC21285 watchdog" + depends on FOOTBRIDGE + help + 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 + driver as a module, which will be called wdt285. + + This driver does not work on all machines. In particular, early CATS + boards have hardware problems that will cause the machine to simply + lock up if the watchdog fires. + + "If in doubt, leave it out" - say N. + +config 977_WATCHDOG + tristate "NetWinder WB83C977 watchdog" + depends on FOOTBRIDGE && ARCH_NETWINDER + help + Say Y here to include support for the WB977 watchdog included in + NetWinder machines. Alternatively say M to compile the driver as + a module, which will be called wdt977. + + Not sure? It's safe to say N. + +config IXP2000_WATCHDOG + tristate "IXP2000 Watchdog" + depends on ARCH_IXP2000 + help + Say Y here if to include support for the watchdog timer + in the Intel IXP2000(2400, 2800, 2850) network processors. + This driver can be built as a module by choosing M. The module + will be called ixp2000_wdt. + + Say N if you are unsure. + +config IXP4XX_WATCHDOG + tristate "IXP4xx Watchdog" + depends on ARCH_IXP4XX + help + Say Y here if to include support for the watchdog timer + in the Intel IXP4xx network processors. This driver can + be built as a module by choosing M. The module will + be called ixp4xx_wdt. + + Note: The internal IXP4xx watchdog does a soft CPU reset + which doesn't reset any peripherals. There are circumstances + where the watchdog will fail to reset the board correctly + (e.g., if the boot ROM is in an unreadable state). + + Say N if you are unsure. + +config KS8695_WATCHDOG + tristate "KS8695 watchdog" + depends on ARCH_KS8695 + help + Watchdog timer embedded into KS8695 processor. This will reboot your + system when the timeout is reached. + +config S3C2410_WATCHDOG + tristate "S3C2410 Watchdog" + depends on ARCH_S3C2410 + help + Watchdog timer block in the Samsung S3C2410 chips. This will + reboot the system when the timer expires with the watchdog + enabled. + + The driver is limited by the speed of the system's PCLK + signal, so with reasonably fast systems (PCLK around 50-66MHz) + then watchdog intervals of over approximately 20seconds are + unavailable. + + The driver can be built as a module by choosing M, and will + be called s3c2410_wdt + +config SA1100_WATCHDOG + tristate "SA1100/PXA2xx watchdog" + depends on ARCH_SA1100 || ARCH_PXA + help + Watchdog timer embedded into SA11x0 and PXA2xx chips. This will + reboot your system when timeout is reached. + + NOTE: once enabled, this timer cannot be disabled. + + To compile this driver as a module, choose M here: the + module will be called sa1100_wdt. + +config MPCORE_WATCHDOG + tristate "MPcore watchdog" + depends on ARM_MPCORE_PLATFORM && LOCAL_TIMERS + help + Watchdog timer embedded into the MPcore system. + + To compile this driver as a module, choose M here: the + module will be called mpcore_wdt. + +config EP93XX_WATCHDOG + tristate "EP93xx Watchdog" + depends on ARCH_EP93XX + help + Say Y here if to include support for the watchdog timer + embedded in the Cirrus Logic EP93xx family of devices. + + To compile this driver as a module, choose M here: the + module will be called ep93xx_wdt. + +config OMAP_WATCHDOG + tristate "OMAP Watchdog" + depends on ARCH_OMAP16XX || ARCH_OMAP24XX + help + Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to + enable the OMAP1610/OMAP1710 watchdog timer. + +config PNX4008_WATCHDOG + tristate "PNX4008 Watchdog" + depends on ARCH_PNX4008 + help + Say Y here if to include support for the watchdog timer + in the PNX4008 processor. + This driver can be built as a module by choosing M. The module + will be called pnx4008_wdt. + + Say N if you are unsure. + +config IOP_WATCHDOG + tristate "IOP Watchdog" + depends on PLAT_IOP + select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) + help + Say Y here if to include support for the watchdog timer + in the Intel IOP3XX & IOP13XX I/O Processors. This driver can + be built as a module by choosing M. The module will + be called iop_wdt. + + Note: The IOP13XX watchdog does an Internal Bus Reset which will + affect both cores and the peripherals of the IOP. The ATU-X + and/or ATUe configuration registers will remain intact, but if + operating as an Root Complex and/or Central Resource, the PCI-X + and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER. + +config DAVINCI_WATCHDOG + tristate "DaVinci watchdog" + depends on ARCH_DAVINCI + help + Say Y here if to include support for the watchdog timer + in the DaVinci DM644x/DM646x processors. + To compile this driver as a module, choose M here: the + module will be called davinci_wdt. + + NOTE: once enabled, this timer cannot be disabled. + Say N if you are unsure. + +# ARM26 Architecture + +# AVR32 Architecture + +config AT32AP700X_WDT + tristate "AT32AP700x watchdog" + depends on CPU_AT32AP7000 + help + Watchdog timer embedded into AT32AP700x devices. This will reboot + your system when the timeout is reached. + +# BLACKFIN Architecture + +config BFIN_WDT + tristate "Blackfin On-Chip Watchdog Timer" + depends on BLACKFIN + ---help--- + If you say yes here you will get support for the Blackfin On-Chip + Watchdog Timer. If you have one of these processors and wish to + have watchdog support enabled, say Y, otherwise say N. + + To compile this driver as a module, choose M here: the + module will be called bfin_wdt. + +# CRIS Architecture + +# FRV Architecture + +# H8300 Architecture + +# X86 (i386 + ia64 + x86_64) Architecture + +config ACQUIRE_WDT + tristate "Acquire SBC Watchdog Timer" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on Single Board + Computers produced by Acquire Inc (and 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 acquirewdt. + + Most people will say N. + +config ADVANTECH_WDT + tristate "Advantech SBC Watchdog Timer" + depends on X86 + help + If you are configuring a Linux kernel for the Advantech single-board + computer, say `Y' here to support its built-in watchdog timer + feature. More information can be found at + + +config ALIM1535_WDT + tristate "ALi M1535 PMU Watchdog Timer" + depends on X86 && PCI + ---help--- + This is the driver for the hardware watchdog on the ALi M1535 PMU. + + To compile this driver as a module, choose M here: the + module will be called alim1535_wdt. + + Most people will say N. + +config ALIM7101_WDT + tristate "ALi M7101 PMU Computer Watchdog" + depends on X86 && PCI + help + This is the driver for the hardware watchdog on the ALi M7101 PMU + as used in the x86 Cobalt servers. + + To compile this driver as a module, choose M here: the + module will be called alim7101_wdt. + + Most people will say N. + +config SC520_WDT + tristate "AMD Elan SC520 processor Watchdog" + depends on X86 + help + This is the driver for the hardware watchdog built in to the + AMD "Elan" SC520 microcomputer commonly used in embedded systems. + 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. + + You can compile this driver directly into the kernel, or use + it as a module. The module will be called sc520_wdt. + +config EUROTECH_WDT + tristate "Eurotech CPU-1220/1410 Watchdog Timer" + depends on X86 + help + Enable support for the watchdog timer on the Eurotech CPU-1220 and + CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product + information are at . + +config IB700_WDT + tristate "IB700 SBC Watchdog Timer" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on the IB700 Single + Board Computer produced by TMC Technology (www.tmc-uk.com). 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. + + This driver is like the WDT501 driver but for slightly different hardware. + + To compile this driver as a module, choose M here: the + module will be called ib700wdt. + + Most people will say N. + +config IBMASR + tristate "IBM Automatic Server Restart" + depends on X86 + help + This is the driver for the IBM Automatic Server Restart watchdog + timer built-in into some eServer xSeries machines. + + To compile this driver as a module, choose M here: the + module will be called ibmasr. + +config WAFER_WDT + tristate "ICP Wafer 5823 Single Board Computer Watchdog" + depends on X86 + help + This is a driver for the hardware watchdog on the ICP Wafer 5823 + Single Board Computer (and probably other similar models). + + To compile this driver as a module, choose M here: the + module will be called wafer5823wdt. + +config I6300ESB_WDT + tristate "Intel 6300ESB Timer/Watchdog" + depends on X86 && PCI + ---help--- + Hardware driver for the watchdog timer built into the Intel + 6300ESB controller hub. + + To compile this driver as a module, choose M here: the + module will be called i6300esb. + +config ITCO_WDT + tristate "Intel TCO Timer/Watchdog" + depends on (X86 || IA64) && PCI + ---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 + controller hub. + + The TCO (Total Cost of Ownership) timer is a watchdog timer + that will reboot the machine after its second expiration. The + expiration time can be configured with the "heartbeat" parameter. + + On some motherboards the driver may fail to reset the chipset's + NO_REBOOT flag which prevents the watchdog from rebooting the + machine. If this is the case you will get a kernel message like + "failed to reset NO_REBOOT flag, reboot disabled by hardware". + + To compile this driver as a module, choose M here: the + module will be called iTCO_wdt. + +config ITCO_VENDOR_SUPPORT + bool "Intel TCO Timer/Watchdog Specific Vendor Support" + depends on ITCO_WDT + ---help--- + Add vendor specific support to the intel TCO timer based watchdog + devices. At this moment we only have additional support for some + SuperMicro Inc. motherboards. + +config SC1200_WDT + tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" + depends on X86 + help + This is a driver for National Semiconductor PC87307/PC97307 hardware + watchdog cards as found on the SC1200. This watchdog is mainly used + for power management purposes and can be used to power down the device + during inactivity periods (includes interrupt activity monitoring). + + To compile this driver as a module, choose M here: the + module will be called sc1200wdt. + + Most people will say N. + +config SCx200_WDT + tristate "National Semiconductor SCx200 Watchdog" + depends on SCx200 && PCI + help + Enable the built-in watchdog timer support on the National + Semiconductor SCx200 processors. + + If compiled as a module, it will be called scx200_wdt. + +config PC87413_WDT + tristate "NS PC87413 watchdog" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on the PC87413 chipset + 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 pc87413_wdt. + + Most people will say N. + +config 60XX_WDT + tristate "SBC-60XX Watchdog Timer" + depends on X86 + help + This driver can be used with the watchdog timer found on some + single board computers, namely the 6010 PII based computer. + It may well work with other cards. It reads port 0x443 to enable + and re-set the watchdog timer, and reads port 0x45 to disable + the watchdog. If you have a card that behave in similar ways, + you can probably make this driver work with your card as well. + + You can compile this driver directly into the kernel, or use + it as a module. The module will be called sbc60xxwdt. + +config SBC8360_WDT + tristate "SBC8360 Watchdog Timer" + depends on X86 + ---help--- + + This is the driver for the hardware watchdog on the SBC8360 Single + Board Computer produced by Axiomtek Co., Ltd. (www.axiomtek.com). + + To compile this driver as a module, choose M here: the + module will be called sbc8360.ko. + + Most people will say N. + +config CPU5_WDT + tristate "SMA CPU5 Watchdog" + depends on X86 + ---help--- + TBD. + To compile this driver as a module, choose M here: the + module will be called cpu5wdt. + +config SMSC37B787_WDT + tristate "Winbond SMsC37B787 Watchdog Timer" + depends on 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 + tristate "W83627HF Watchdog Timer" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on the W83627HF chipset + as used in Advantech PC-9578 and Tyan S2721-533 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 w83627hf_wdt. + + Most people will say N. + +config W83697HF_WDT + tristate "W83697HF/W83697HG Watchdog Timer" + depends on 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 + tristate "W83877F (EMACS) Watchdog Timer" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on the W83877F chipset + as used in EMACS PC-104 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 w83877f_wdt. + + Most people will say N. + +config W83977F_WDT + tristate "W83977F (PCM-5335) Watchdog Timer" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on the W83977F I/O chip + as used in AAEON's PCM-5335 SBC (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 w83977f_wdt. + +config MACHZ_WDT + tristate "ZF MachZ Watchdog" + depends on X86 + ---help--- + If you are using a ZF Micro MachZ processor, say Y here, otherwise + N. This is the driver for the watchdog timer built-in on that + processor using ZF-Logic interface. 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 machzwd. + +config SBC_EPX_C3_WATCHDOG + tristate "Winsystems SBC EPX-C3 watchdog" + depends on X86 + ---help--- + This is the driver for the built-in watchdog timer on the EPX-C3 + Single-board computer made by Winsystems, Inc. + + *Note*: This hardware watchdog is not probeable and thus there + is no way to know if writing to its IO address will corrupt + your system or have any real effect. The only way to be sure + that this driver does what you want is to make sure you + are running it on an EPX-C3 from Winsystems with the watchdog + timer at IO address 0x1ee and 0x1ef. It will write to both those + IO ports. Basically, the assumption is made that if you compile + this driver into your kernel and/or load it as a module, that you + know what you are doing and that you are in fact running on an + EPX-C3 board! + + To compile this driver as a module, choose M here: the + module will be called sbc_epx_c3. + +# M32R Architecture + +# M68K Architecture + +# M68KNOMMU Architecture + +# MIPS Architecture + +config INDYDOG + tristate "Indy/I2 Hardware Watchdog" + depends on SGI_IP22 + help + Hardware driver for the Indy's/I2's watchdog. This is a + watchdog timer that will reboot the machine after a 60 second + timer expired and no process has written to /dev/watchdog during + that time. + +config WDT_MTX1 + tristate "MTX-1 Hardware Watchdog" + depends on MIPS_MTX1 + help + Hardware driver for the MTX-1 boards. This is a watchdog timer that + will reboot the machine after a 100 seconds timer expired. + +config WDT_RM9K_GPI + tristate "RM9000/GPI hardware watchdog" + depends on CPU_RM9000 + help + Watchdog implementation using the GPI hardware found on + PMC-Sierra RM9xxx CPUs. + + To compile this driver as a module, choose M here: the + module will be called rm9k_wdt. + +# PARISC Architecture + +# POWERPC Architecture + +config MPC5200_WDT + tristate "MPC5200 Watchdog Timer" + depends on PPC_MPC52xx + +config 8xx_WDT + tristate "MPC8xx Watchdog Timer" + depends on 8xx + +config 83xx_WDT + tristate "MPC83xx Watchdog Timer" + depends on PPC_83xx + +config MV64X60_WDT + tristate "MV64X60 (Marvell Discovery) Watchdog Timer" + depends on MV64X60 + +config BOOKE_WDT + bool "PowerPC Book-E Watchdog Timer" + depends on BOOKE || 4xx + ---help--- + Please see Documentation/watchdog/watchdog-api.txt for + more information. + +# PPC64 Architecture + +config WATCHDOG_RTAS + tristate "RTAS watchdog" + depends on PPC_RTAS + help + This driver adds watchdog support for the RTAS watchdog. + + To compile this driver as a module, choose M here. The module + will be called wdrtas. + +# S390 Architecture + +config ZVM_WATCHDOG + tristate "z/VM Watchdog Timer" + depends on S390 + help + IBM s/390 and zSeries machines running under z/VM 5.1 or later + provide a virtual watchdog timer to their guest that cause a + user define Control Program command to be executed after a + timeout. + + To compile this driver as a module, choose M here. The module + will be called vmwatchdog. + +# SUPERH (sh + sh64) Architecture + +config SH_WDT + tristate "SuperH Watchdog" + depends on SUPERH && (CPU_SH3 || CPU_SH4) + help + This driver adds watchdog support for the integrated watchdog in the + SuperH processors. If you have one of these processors and wish + to have watchdog support enabled, say Y, otherwise say N. + + As a side note, saying Y here will automatically boost HZ to 1000 + so that the timer has a chance to clear the overflow counter. On + slower systems (such as the SH-2 and SH-3) this will likely yield + some performance issues. As such, the WDT should be avoided here + unless it is absolutely necessary. + + To compile this driver as a module, choose M here: the + module will be called shwdt. + +config SH_WDT_MMAP + bool "Allow mmap of SH WDT" + default n + depends on SH_WDT + help + If you say Y here, user applications will be able to mmap the + WDT/CPG registers. + +# SPARC Architecture + +# SPARC64 Architecture + +config WATCHDOG_CP1XXX + tristate "CP1XXX Hardware Watchdog support" + depends on SPARC64 && PCI + ---help--- + This is the driver for the hardware watchdog timers present on + Sun Microsystems CompactPCI models CP1400 and CP1500. + + To compile this driver as a module, choose M here: the + module will be called cpwatchdog. + + If you do not have a CompactPCI model CP1400 or CP1500, or + another UltraSPARC-IIi-cEngine boardset with hardware watchdog, + you should say N to this option. + +config WATCHDOG_RIO + tristate "RIO Hardware Watchdog support" + depends on SPARC64 && PCI + help + Say Y here to support the hardware watchdog capability on Sun RIO + machines. The watchdog timeout period is normally one minute but + can be changed with a boot-time parameter. + +# V850 Architecture + +# XTENSA Architecture + +# +# ISA-based Watchdog Cards +# + +comment "ISA-based Watchdog Cards" + depends on ISA + +config PCWATCHDOG + tristate "Berkshire Products ISA-PC Watchdog" + depends on ISA + ---help--- + This is the driver for the Berkshire Products ISA-PC Watchdog card. + This card 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. This driver is like the WDT501 driver but for different + hardware. Please read . The PC + watchdog cards can be ordered from . + + To compile this driver as a module, choose M here: the + module will be called pcwd. + + Most people will say N. + +config MIXCOMWD + tristate "Mixcom Watchdog" + depends on ISA + ---help--- + This is a driver for the Mixcom hardware watchdog cards. 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 mixcomwd. + + Most people will say N. + +config WDT + tristate "WDT Watchdog timer" + depends on ISA + ---help--- + If you have a WDT500P or WDT501P watchdog board, say Y here, + otherwise N. It is not possible to probe for this board, which means + that you have to inform the kernel about the IO port and IRQ that + is needed (you can do this via the io and irq parameters) + + To compile this driver as a module, choose M here: the + module will be called wdt. + +config WDT_501 + bool "WDT501 features" + depends on WDT + help + Saying Y here and creating a character special file /dev/temperature + with major number 10 and minor number 131 ("man mknod") will give + you a thermometer inside your computer: reading from + /dev/temperature yields one byte, the temperature in degrees + Fahrenheit. This works only if you have a WDT501P watchdog board + installed. + + If you want to enable the Fan Tachometer on the WDT501P, then you + can do this via the tachometer parameter. Only do this if you have a + fan tachometer actually set up. + +# +# PCI-based Watchdog Cards +# + +comment "PCI-based Watchdog Cards" + depends on PCI + +config PCIPCWATCHDOG + tristate "Berkshire Products PCI-PC Watchdog" + depends on PCI + ---help--- + This is the driver for the Berkshire Products PCI-PC Watchdog card. + This card 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. The card can also monitor the internal temperature of the PC. + More info is available at . + + To compile this driver as a module, choose M here: the + module will be called pcwd_pci. + + Most people will say N. + +config WDTPCI + tristate "PCI-WDT500/501 Watchdog timer" + depends on PCI + ---help--- + If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. + + To compile this driver as a module, choose M here: the + module will be called wdt_pci. + +config WDT_501_PCI + bool "PCI-WDT501 features" + depends on WDTPCI + help + Saying Y here and creating a character special file /dev/temperature + with major number 10 and minor number 131 ("man mknod") will give + you a thermometer inside your computer: reading from + /dev/temperature yields one byte, the temperature in degrees + Fahrenheit. This works only if you have a PCI-WDT501 watchdog board + installed. + + If you want to enable the Fan Tachometer on the PCI-WDT501, then you + can do this via the tachometer parameter. Only do this if you have a + fan tachometer actually set up. + +# +# USB-based Watchdog Cards +# + +comment "USB-based Watchdog Cards" + depends on USB + +config USBPCWATCHDOG + tristate "Berkshire Products USB-PC Watchdog" + depends on USB + ---help--- + This is the driver for the Berkshire Products USB-PC Watchdog card. + This card 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. The card can also monitor the internal temperature of the PC. + More info is available at . + + To compile this driver as a module, choose M here: the + module will be called pcwd_usb. + + Most people will say N. + +endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile new file mode 100644 index 0000000..389f8b1 --- /dev/null +++ b/drivers/watchdog/Makefile @@ -0,0 +1,120 @@ +# +# Makefile for the WatchDog device drivers. +# + +# 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. +# This means that if your ISA/PCI/USB card isn't detected that +# you can fall back to an architecture specific driver and if +# that also fails then you can fall back to the software watchdog +# to give you some cover. + +# ISA-based Watchdog Cards +obj-$(CONFIG_PCWATCHDOG) += pcwd.o +obj-$(CONFIG_MIXCOMWD) += mixcomwd.o +obj-$(CONFIG_WDT) += wdt.o + +# PCI-based Watchdog Cards +obj-$(CONFIG_PCIPCWATCHDOG) += pcwd_pci.o +obj-$(CONFIG_WDTPCI) += wdt_pci.o + +# USB-based Watchdog Cards +obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o + +# ALPHA Architecture + +# ARM Architecture +obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o +obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o +obj-$(CONFIG_21285_WATCHDOG) += wdt285.o +obj-$(CONFIG_977_WATCHDOG) += wdt977.o +obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o +obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o +obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o +obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o +obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o +obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o +obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o +obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o +obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o +obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o + +# ARM26 Architecture + +# AVR32 Architecture +obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o + +# BLACKFIN Architecture +obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o + +# CRIS Architecture + +# FRV Architecture + +# H8300 Architecture + +# X86 (i386 + ia64 + x86_64) Architecture +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_SC520_WDT) += sc520_wdt.o +obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o +obj-$(CONFIG_IB700_WDT) += ib700wdt.o +obj-$(CONFIG_IBMASR) += ibmasr.o +obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o +obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o +obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o iTCO_vendor_support.o +obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o +obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o +obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o +obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o +obj-$(CONFIG_SBC8360_WDT) += sbc8360.o +obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o +obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_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_W83977F_WDT) += w83977f_wdt.o +obj-$(CONFIG_MACHZ_WDT) += machzwd.o +obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o + +# M32R Architecture + +# M68K Architecture + +# M68KNOMMU Architecture + +# MIPS Architecture +obj-$(CONFIG_INDYDOG) += indydog.o +obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o +obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o + +# PARISC Architecture + +# POWERPC Architecture +obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o +obj-$(CONFIG_MPC5200_WDT) += mpc5200_wdt.o +obj-$(CONFIG_83xx_WDT) += mpc83xx_wdt.o +obj-$(CONFIG_MV64X60_WDT) += mv64x60_wdt.o +obj-$(CONFIG_BOOKE_WDT) += booke_wdt.o + +# PPC64 Architecture +obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o + +# S390 Architecture + +# SUPERH (sh + sh64) Architecture +obj-$(CONFIG_SH_WDT) += shwdt.o + +# SPARC Architecture + +# SPARC64 Architecture + +# V850 Architecture + +# XTENSA Architecture + +# Architecture Independant +obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c new file mode 100644 index 0000000..85269c3 --- /dev/null +++ b/drivers/watchdog/acquirewdt.c @@ -0,0 +1,348 @@ +/* + * Acquire Single Board Computer Watchdog Timer driver + * + * Based on wdt.c. Original copyright messages: + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * Can't add timeout - driver doesn't allow changing value + */ + +/* + * Theory of Operation: + * The Watch-Dog Timer is provided to ensure that standalone + * Systems can always recover from catastrophic conditions that + * caused the CPU to crash. This condition may have occured by + * external EMI or a software bug. When the CPU stops working + * correctly, hardware on the board will either perform a hardware + * reset (cold boot) or a non-maskable interrupt (NMI) to bring the + * system back to a known state. + * + * The Watch-Dog Timer is controlled by two I/O Ports. + * 443 hex - Read - Enable or refresh the Watch-Dog Timer + * 043 hex - Read - Disable the Watch-Dog Timer + * + * To enable the Watch-Dog Timer, a read from I/O port 443h must + * be performed. This will enable and activate the countdown timer + * which will eventually time out and either reset the CPU or cause + * an NMI depending on the setting of a jumper. To ensure that this + * reset condition does not occur, the Watch-Dog Timer must be + * periodically refreshed by reading the same I/O port 443h. + * The Watch-Dog Timer is disabled by reading I/O port 043h. + * + * The Watch-Dog Timer Time-Out Period is set via jumpers. + * It can be 1, 2, 10, 20, 110 or 220 seconds. + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +/* Includes */ +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For file operations */ +#include /* For io-port access */ +#include /* For platform_driver framework */ +#include /* For __init/__exit/... */ + +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ + +/* Module information */ +#define DRV_NAME "acquirewdt" +#define PFX DRV_NAME ": " +#define WATCHDOG_NAME "Acquire WDT" +#define WATCHDOG_HEARTBEAT 0 /* There is no way to see what the correct time-out period is */ + +/* internal variables */ +static struct platform_device *acq_platform_device; /* the watchdog platform device */ +static unsigned long acq_is_open; +static char expect_close; + +/* module parameters */ +static int wdt_stop = 0x43; /* You must set this - there is no sane way to probe for this board. */ +module_param(wdt_stop, int, 0); +MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)"); + +static int wdt_start = 0x443; /* You must set this - there is no sane way to probe for this board. */ +module_param(wdt_start, int, 0); +MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)"); + +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) ")"); + +/* + * Watchdog Operations + */ + +static void acq_keepalive(void) +{ + /* Write a watchdog value */ + inb_p(wdt_start); +} + +static void acq_stop(void) +{ + /* Turn the card off */ + inb_p(wdt_stop); +} + +/* + * /dev/watchdog handling + */ + +static ssize_t acq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) { + if (!nowayout) { + size_t i; + + /* note: just in case someone wrote the magic character + * five months ago... */ + expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + + /* Well, anyhow someone wrote to us, we should return that favour */ + acq_keepalive(); + } + return count; +} + +static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int options, retval = -EINVAL; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = + { + .options = 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; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + acq_keepalive(); + return 0; + + case WDIOC_GETTIMEOUT: + return put_user(WATCHDOG_HEARTBEAT, p); + + case WDIOC_SETOPTIONS: + { + if (get_user(options, p)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) + { + acq_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) + { + acq_keepalive(); + retval = 0; + } + + return retval; + } + + default: + return -ENOTTY; + } +} + +static int acq_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &acq_is_open)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate */ + acq_keepalive(); + return nonseekable_open(inode, file); +} + +static int acq_close(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + acq_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + acq_keepalive(); + } + clear_bit(0, &acq_is_open); + expect_close = 0; + return 0; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations acq_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = acq_write, + .ioctl = acq_ioctl, + .open = acq_open, + .release = acq_close, +}; + +static struct miscdevice acq_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &acq_fops, +}; + +/* + * Init & exit routines + */ + +static int __devinit acq_probe(struct platform_device *dev) +{ + int ret; + + if (wdt_stop != wdt_start) { + if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_stop); + ret = -EIO; + goto out; + } + } + + if (!request_region(wdt_start, 1, WATCHDOG_NAME)) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_start); + ret = -EIO; + goto unreg_stop; + } + + ret = misc_register(&acq_miscdev); + if (ret != 0) { + printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto unreg_regions; + } + + printk (KERN_INFO PFX "initialized. (nowayout=%d)\n", + nowayout); + + return 0; + +unreg_regions: + release_region(wdt_start, 1); +unreg_stop: + if (wdt_stop != wdt_start) + release_region(wdt_stop, 1); +out: + return ret; +} + +static int __devexit acq_remove(struct platform_device *dev) +{ + misc_deregister(&acq_miscdev); + release_region(wdt_start,1); + if(wdt_stop != wdt_start) + release_region(wdt_stop,1); + + return 0; +} + +static void acq_shutdown(struct platform_device *dev) +{ + /* Turn the WDT off if we have a soft shutdown */ + acq_stop(); +} + +static struct platform_driver acquirewdt_driver = { + .probe = acq_probe, + .remove = __devexit_p(acq_remove), + .shutdown = acq_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init acq_init(void) +{ + int err; + + printk(KERN_INFO "WDT driver for Acquire single board computer initialising.\n"); + + err = platform_driver_register(&acquirewdt_driver); + if (err) + return err; + + acq_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); + if (IS_ERR(acq_platform_device)) { + err = PTR_ERR(acq_platform_device); + goto unreg_platform_driver; + } + + return 0; + +unreg_platform_driver: + platform_driver_unregister(&acquirewdt_driver); + return err; +} + +static void __exit acq_exit(void) +{ + platform_device_unregister(acq_platform_device); + platform_driver_unregister(&acquirewdt_driver); + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); +} + +module_init(acq_init); +module_exit(acq_exit); + +MODULE_AUTHOR("David Woodhouse"); +MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c new file mode 100644 index 0000000..8121cc2 --- /dev/null +++ b/drivers/watchdog/advantechwdt.c @@ -0,0 +1,362 @@ +/* + * Advantech Single Board Computer WDT driver + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * Based on acquirewdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * + * 16-Oct-2002 Rob Radez + * Clean up ioctls, clean up init + exit, add expect close support, + * add wdt_start and wdt_stop as parameters. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DRV_NAME "advantechwdt" +#define PFX DRV_NAME ": " +#define WATCHDOG_NAME "Advantech WDT" +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ + +static struct platform_device *advwdt_platform_device; /* the watchdog platform device */ +static unsigned long advwdt_is_open; +static char adv_expect_close; + +/* + * You must set these - there is no sane way to probe for this board. + * + * To enable or restart, write the timeout value in seconds (1 to 63) + * to I/O port wdt_start. To disable, read I/O port wdt_stop. + * Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but + * check your manual (at least the PCA-6159 seems to be different - + * the manual says wdt_stop is 0x43, not 0x443). + * (0x43 is also a write-only control register for the 8254 timer!) + */ + +static int wdt_stop = 0x443; +module_param(wdt_stop, int, 0); +MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)"); + +static int wdt_start = 0x443; +module_param(wdt_start, int, 0); +MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)"); + +static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=63, 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) ")"); + +/* + * Watchdog Operations + */ + +static void +advwdt_ping(void) +{ + /* Write a watchdog value */ + outb_p(timeout, wdt_start); +} + +static void +advwdt_disable(void) +{ + inb_p(wdt_stop); +} + +static int +advwdt_set_heartbeat(int t) +{ + if ((t < 1) || (t > 63)) + return -EINVAL; + + timeout = t; + return 0; +} + +/* + * /dev/watchdog handling + */ + +static ssize_t +advwdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + adv_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf+i)) + return -EFAULT; + if (c == 'V') + adv_expect_close = 42; + } + } + advwdt_ping(); + } + return count; +} + +static int +advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_timeout; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = WATCHDOG_NAME, + }; + + 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: + advwdt_ping(); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, p)) + return -EFAULT; + if (advwdt_set_heartbeat(new_timeout)) + return -EINVAL; + advwdt_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) { + advwdt_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + advwdt_ping(); + retval = 0; + } + + return retval; + } + + default: + return -ENOTTY; + } + return 0; +} + +static int +advwdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &advwdt_is_open)) + return -EBUSY; + /* + * Activate + */ + + advwdt_ping(); + return nonseekable_open(inode, file); +} + +static int +advwdt_close(struct inode *inode, struct file *file) +{ + if (adv_expect_close == 42) { + advwdt_disable(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + advwdt_ping(); + } + clear_bit(0, &advwdt_is_open); + adv_expect_close = 0; + return 0; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations advwdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = advwdt_write, + .ioctl = advwdt_ioctl, + .open = advwdt_open, + .release = advwdt_close, +}; + +static struct miscdevice advwdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &advwdt_fops, +}; + +/* + * Init & exit routines + */ + +static int __devinit +advwdt_probe(struct platform_device *dev) +{ + int ret; + + if (wdt_stop != wdt_start) { + if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_stop); + ret = -EIO; + goto out; + } + } + + if (!request_region(wdt_start, 1, WATCHDOG_NAME)) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_start); + ret = -EIO; + goto unreg_stop; + } + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (advwdt_set_heartbeat(timeout)) { + advwdt_set_heartbeat(WATCHDOG_TIMEOUT); + printk (KERN_INFO PFX "timeout value must be 1<=x<=63, using %d\n", + timeout); + } + + ret = misc_register(&advwdt_miscdev); + if (ret != 0) { + printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto unreg_regions; + } + + printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + +out: + return ret; +unreg_regions: + release_region(wdt_start, 1); +unreg_stop: + if (wdt_stop != wdt_start) + release_region(wdt_stop, 1); + goto out; +} + +static int __devexit +advwdt_remove(struct platform_device *dev) +{ + misc_deregister(&advwdt_miscdev); + release_region(wdt_start,1); + if(wdt_stop != wdt_start) + release_region(wdt_stop,1); + + return 0; +} + +static void +advwdt_shutdown(struct platform_device *dev) +{ + /* Turn the WDT off if we have a soft shutdown */ + advwdt_disable(); +} + +static struct platform_driver advwdt_driver = { + .probe = advwdt_probe, + .remove = __devexit_p(advwdt_remove), + .shutdown = advwdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init +advwdt_init(void) +{ + int err; + + printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n"); + + err = platform_driver_register(&advwdt_driver); + if (err) + return err; + + advwdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); + if (IS_ERR(advwdt_platform_device)) { + err = PTR_ERR(advwdt_platform_device); + goto unreg_platform_driver; + } + + return 0; + +unreg_platform_driver: + platform_driver_unregister(&advwdt_driver); + return err; +} + +static void __exit +advwdt_exit(void) +{ + platform_device_unregister(advwdt_platform_device); + platform_driver_unregister(&advwdt_driver); + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); +} + +module_init(advwdt_init); +module_exit(advwdt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Michalkiewicz "); +MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c new file mode 100644 index 0000000..c404fc6 --- /dev/null +++ b/drivers/watchdog/alim1535_wdt.c @@ -0,0 +1,465 @@ +/* + * Watchdog for the 7101 PMU version found in the ALi M1535 chipsets + * + * 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 + +#define WATCHDOG_NAME "ALi_M1535" +#define PFX WATCHDOG_NAME ": " +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ + +/* internal variables */ +static unsigned long ali_is_open; +static char ali_expect_release; +static struct pci_dev *ali_pci; +static u32 ali_timeout_bits; /* stores the computed timeout */ +static spinlock_t ali_lock; /* Guards the hardware */ + +/* module parameters */ +static int timeout = WATCHDOG_TIMEOUT; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (0= 18000) { + timeout = WATCHDOG_TIMEOUT; + printk(KERN_INFO PFX "timeout value must be 0 + * and the Cobalt kernel WDT timer driver by Tim Hockin + * + * + * (c)2002 Steve Hill + * + * This WDT driver is different from most other Linux WDT + * drivers in that the driver will ping the watchdog by itself, + * because this particular WDT has a very short timeout (1.6 + * seconds) and it would be insane to count on any userspace + * daemon always getting scheduled within that time frame. + * + * Additions: + * Aug 23, 2004 - Added use_gpio module parameter for use on revision a1d PMUs + * found on very old cobalt hardware. + * -- Mike Waychison + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define OUR_NAME "alim7101_wdt" +#define PFX OUR_NAME ": " + +#define WDT_ENABLE 0x9C +#define WDT_DISABLE 0x8C + +#define ALI_7101_WDT 0x92 +#define ALI_7101_GPIO 0x7D +#define ALI_7101_GPIO_O 0x7E +#define ALI_WDT_ARM 0x01 + +/* + * We're going to use a 1 second timeout. + * If we reset the watchdog every ~250ms we should be safe. */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + */ + +#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ +static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +static int use_gpio = 0; /* Use the pic (for a1d revision alim7101) */ +module_param(use_gpio, int, 0); +MODULE_PARM_DESC(use_gpio, "Use the gpio watchdog. (required by old cobalt boards)"); + +static void wdt_timer_ping(unsigned long); +static DEFINE_TIMER(timer, wdt_timer_ping, 0, 1); +static unsigned long next_heartbeat; +static unsigned long wdt_is_open; +static char wdt_expect_close; +static struct pci_dev *alim7101_pmu; + +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) ")"); + +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + char tmp; + + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT (this is actually a disarm/arm sequence) */ + pci_read_config_byte(alim7101_pmu, 0x92, &tmp); + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); + if (use_gpio) { + pci_read_config_byte(alim7101_pmu, ALI_7101_GPIO_O, &tmp); + pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp + | 0x20); + pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp + & ~0x20); + } + } else { + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); + } + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); +} + +/* + * Utility routines + */ + +static void wdt_change(int writeval) +{ + char tmp; + + pci_read_config_byte(alim7101_pmu, ALI_7101_WDT, &tmp); + if (writeval == WDT_ENABLE) { + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); + if (use_gpio) { + pci_read_config_byte(alim7101_pmu, ALI_7101_GPIO_O, &tmp); + pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp & ~0x20); + } + + } else { + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); + if (use_gpio) { + pci_read_config_byte(alim7101_pmu, ALI_7101_GPIO_O, &tmp); + pci_write_config_byte(alim7101_pmu, ALI_7101_GPIO_O, tmp | 0x20); + } + } +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + /* We must enable before we kick off the timer in case the timer + occurs as we ping it */ + + wdt_change(WDT_ENABLE); + + /* Start the timer */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + + printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); +} + +static void wdt_turnoff(void) +{ + /* Stop the timer */ + del_timer_sync(&timer); + wdt_change(WDT_DISABLE); + printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); +} + +static void wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); +} + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) { + if (!nowayout) { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* now scan */ + for (ofs = 0; ofs != count; ofs++) { + char c; + if (get_user(c, buf+ofs)) + return -EFAULT; + if (c == 'V') + wdt_expect_close = 42; + } + } + /* someone wrote to us, we should restart timer */ + wdt_keepalive(); + } + return count; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* Good, fire up the show */ + wdt_startup(); + return nonseekable_open(inode, file); +} + +static int fop_close(struct inode * inode, struct file * file) +{ + if(wdt_expect_close == 42) + wdt_turnoff(); + else { + /* wim: shouldn't there be a: del_timer(&timer); */ + printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n"); + } + clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; + return 0; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = + { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "ALiM7101", + }; + + 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: + wdt_keepalive(); + return 0; + case WDIOC_SETOPTIONS: + { + int new_options, retval = -EINVAL; + + if(get_user(new_options, p)) + return -EFAULT; + + if(new_options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(new_options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } + case WDIOC_SETTIMEOUT: + { + int new_timeout; + + if(get_user(new_timeout, p)) + return -EFAULT; + + if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ + return -EINVAL; + + timeout = new_timeout; + wdt_keepalive(); + /* Fall through */ + } + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + default: + return -ENOTTY; + } +} + +static const struct file_operations wdt_fops = { + .owner= THIS_MODULE, + .llseek= no_llseek, + .write= fop_write, + .open= fop_open, + .release= fop_close, + .ioctl= fop_ioctl, +}; + +static struct miscdevice wdt_miscdev = { + .minor=WATCHDOG_MINOR, + .name="watchdog", + .fops=&wdt_fops, +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + + if (code==SYS_RESTART) { + /* + * Cobalt devices have no way of rebooting themselves other than + * getting the watchdog to pull reset, so we restart the watchdog on + * reboot with no heartbeat + */ + wdt_change(WDT_ENABLE); + printk(KERN_INFO PFX "Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n"); + } + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier= +{ + .notifier_call = wdt_notify_sys, +}; + +static void __exit alim7101_wdt_unload(void) +{ + wdt_turnoff(); + /* Deregister */ + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + pci_dev_put(alim7101_pmu); +} + +static int __init alim7101_wdt_init(void) +{ + int rc = -EBUSY; + struct pci_dev *ali1543_south; + char tmp; + + printk(KERN_INFO PFX "Steve Hill .\n"); + alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, + NULL); + if (!alim7101_pmu) { + printk(KERN_INFO PFX "ALi M7101 PMU not present - WDT not set\n"); + return -EBUSY; + } + + /* Set the WDT in the PMU to 1 second */ + pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02); + + ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, + NULL); + if (!ali1543_south) { + printk(KERN_INFO PFX "ALi 1543 South-Bridge not present - WDT not set\n"); + goto err_out; + } + pci_read_config_byte(ali1543_south, 0x5e, &tmp); + pci_dev_put(ali1543_south); + if ((tmp & 0x1e) == 0x00) { + if (!use_gpio) { + printk(KERN_INFO PFX "Detected old alim7101 revision 'a1d'. If this is a cobalt board, set the 'use_gpio' module parameter.\n"); + goto err_out; + } + nowayout = 1; + } else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) { + printk(KERN_INFO PFX "ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n"); + goto err_out; + } + + if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ + { + timeout = WATCHDOG_TIMEOUT; + printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", + timeout); + } + + rc = misc_register(&wdt_miscdev); + if (rc) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + wdt_miscdev.minor, rc); + goto err_out; + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + rc); + goto err_out_miscdev; + } + + if (nowayout) { + __module_get(THIS_MODULE); + } + + printk(KERN_INFO PFX "WDT driver for ALi M7101 initialised. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out: + pci_dev_put(alim7101_pmu); + return rc; +} + +module_init(alim7101_wdt_init); +module_exit(alim7101_wdt_unload); + +static struct pci_device_id alim7101_pci_tbl[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) }, + { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, alim7101_pci_tbl); + +MODULE_AUTHOR("Steve Hill"); +MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c new file mode 100644 index 0000000..54a5161 --- /dev/null +++ b/drivers/watchdog/at32ap700x_wdt.c @@ -0,0 +1,386 @@ +/* + * Watchdog driver for Atmel AT32AP700X devices + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_MIN 1 +#define TIMEOUT_MAX 2 +#define TIMEOUT_DEFAULT TIMEOUT_MAX + +/* module parameters */ +static int timeout = TIMEOUT_DEFAULT; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Timeout value. Limited to be 1 or 2 seconds. (default=" + __MODULE_STRING(TIMEOUT_DEFAULT) ")"); + +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) ")"); + +/* Watchdog registers and write/read macro */ +#define WDT_CTRL 0x00 +#define WDT_CTRL_EN 0 +#define WDT_CTRL_PSEL 8 +#define WDT_CTRL_KEY 24 + +#define WDT_CLR 0x04 + +#define WDT_BIT(name) (1 << WDT_##name) +#define WDT_BF(name, value) ((value) << WDT_##name) + +#define wdt_readl(dev, reg) \ + __raw_readl((dev)->regs + WDT_##reg) +#define wdt_writel(dev, reg, value) \ + __raw_writel((value), (dev)->regs + WDT_##reg) + +struct wdt_at32ap700x { + void __iomem *regs; + spinlock_t io_lock; + int timeout; + unsigned long users; + struct miscdevice miscdev; +}; + +static struct wdt_at32ap700x *wdt; +static char expect_release; + +/* + * Disable the watchdog. + */ +static inline void at32_wdt_stop(void) +{ + unsigned long psel; + + spin_lock(&wdt->io_lock); + psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f); + wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55)); + wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa)); + spin_unlock(&wdt->io_lock); +} + +/* + * Enable and reset the watchdog. + */ +static inline void at32_wdt_start(void) +{ + /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */ + unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe; + + spin_lock(&wdt->io_lock); + wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) + | WDT_BF(CTRL_PSEL, psel) + | WDT_BF(CTRL_KEY, 0x55)); + wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN) + | WDT_BF(CTRL_PSEL, psel) + | WDT_BF(CTRL_KEY, 0xaa)); + spin_unlock(&wdt->io_lock); +} + +/* + * Pat the watchdog timer. + */ +static inline void at32_wdt_pat(void) +{ + spin_lock(&wdt->io_lock); + wdt_writel(wdt, CLR, 0x42); + spin_unlock(&wdt->io_lock); +} + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int at32_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &wdt->users)) + return -EBUSY; + + at32_wdt_start(); + return nonseekable_open(inode, file); +} + +/* + * Close the watchdog device. + */ +static int at32_wdt_close(struct inode *inode, struct file *file) +{ + if (expect_release == 42) { + at32_wdt_stop(); + } else { + dev_dbg(wdt->miscdev.parent, + "Unexpected close, not stopping watchdog!\n"); + at32_wdt_pat(); + } + clear_bit(1, &wdt->users); + expect_release = 0; + return 0; +} + +/* + * Change the watchdog time interval. + */ +static int at32_wdt_settimeout(int time) +{ + /* + * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is + * 2 ^ 16 allowing up to 2 seconds timeout. + */ + if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX)) + return -EINVAL; + + /* + * Set new watchdog time. It will be used when at32_wdt_start() is + * called. + */ + wdt->timeout = time; + return 0; +} + +static struct watchdog_info at32_wdt_info = { + .identity = "at32ap700x watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +/* + * Handle commands from user-space. + */ +static int at32_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = -ENOTTY; + int time; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + switch (cmd) { + case WDIOC_KEEPALIVE: + at32_wdt_pat(); + ret = 0; + break; + case WDIOC_GETSUPPORT: + ret = copy_to_user(argp, &at32_wdt_info, + sizeof(at32_wdt_info)) ? -EFAULT : 0; + break; + case WDIOC_SETTIMEOUT: + ret = get_user(time, p); + if (ret) + break; + ret = at32_wdt_settimeout(time); + if (ret) + break; + /* Enable new time value */ + at32_wdt_start(); + /* fall through */ + case WDIOC_GETTIMEOUT: + ret = put_user(wdt->timeout, p); + break; + case WDIOC_GETSTATUS: /* fall through */ + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, p); + break; + case WDIOC_SETOPTIONS: + ret = get_user(time, p); + if (ret) + break; + if (time & WDIOS_DISABLECARD) + at32_wdt_stop(); + if (time & WDIOS_ENABLECARD) + at32_wdt_start(); + ret = 0; + break; + } + + return ret; +} + +static ssize_t at32_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 pat the watchdog */ + at32_wdt_pat(); + } + return len; +} + +static const struct file_operations at32_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = at32_wdt_ioctl, + .open = at32_wdt_open, + .release = at32_wdt_close, + .write = at32_wdt_write, +}; + +static int __init at32_wdt_probe(struct platform_device *pdev) +{ + struct resource *regs; + int ret; + + if (wdt) { + dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n"); + return -EBUSY; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "missing mmio resource\n"); + return -ENXIO; + } + + wdt = kzalloc(sizeof(struct wdt_at32ap700x), GFP_KERNEL); + if (!wdt) { + dev_dbg(&pdev->dev, "no memory for wdt structure\n"); + return -ENOMEM; + } + + wdt->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!wdt->regs) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "could not map I/O memory\n"); + goto err_free; + } + spin_lock_init(&wdt->io_lock); + wdt->users = 0; + wdt->miscdev.minor = WATCHDOG_MINOR; + wdt->miscdev.name = "watchdog"; + wdt->miscdev.fops = &at32_wdt_fops; + + if (at32_wdt_settimeout(timeout)) { + at32_wdt_settimeout(TIMEOUT_DEFAULT); + dev_dbg(&pdev->dev, + "default timeout invalid, set to %d sec.\n", + TIMEOUT_DEFAULT); + } + + ret = misc_register(&wdt->miscdev); + if (ret) { + dev_dbg(&pdev->dev, "failed to register wdt miscdev\n"); + goto err_iounmap; + } + + platform_set_drvdata(pdev, wdt); + wdt->miscdev.parent = &pdev->dev; + dev_info(&pdev->dev, + "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", + wdt->regs, wdt->timeout, nowayout); + + return 0; + +err_iounmap: + iounmap(wdt->regs); +err_free: + kfree(wdt); + wdt = NULL; + return ret; +} + +static int __exit at32_wdt_remove(struct platform_device *pdev) +{ + if (wdt && platform_get_drvdata(pdev) == wdt) { + /* Stop the timer before we leave */ + if (!nowayout) + at32_wdt_stop(); + + misc_deregister(&wdt->miscdev); + iounmap(wdt->regs); + kfree(wdt); + wdt = NULL; + platform_set_drvdata(pdev, NULL); + } + + return 0; +} + +static void at32_wdt_shutdown(struct platform_device *pdev) +{ + at32_wdt_stop(); +} + +#ifdef CONFIG_PM +static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message) +{ + at32_wdt_stop(); + return 0; +} + +static int at32_wdt_resume(struct platform_device *pdev) +{ + if (wdt->users) + at32_wdt_start(); + return 0; +} +#else +#define at32_wdt_suspend NULL +#define at32_wdt_resume NULL +#endif + +static struct platform_driver at32_wdt_driver = { + .remove = __exit_p(at32_wdt_remove), + .suspend = at32_wdt_suspend, + .resume = at32_wdt_resume, + .driver = { + .name = "at32_wdt", + .owner = THIS_MODULE, + }, + .shutdown = at32_wdt_shutdown, +}; + +static int __init at32_wdt_init(void) +{ + return platform_driver_probe(&at32_wdt_driver, at32_wdt_probe); +} +module_init(at32_wdt_init); + +static void __exit at32_wdt_exit(void) +{ + platform_driver_unregister(&at32_wdt_driver); +} +module_exit(at32_wdt_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c new file mode 100644 index 0000000..38bd373 --- /dev/null +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -0,0 +1,288 @@ +/* + * Watchdog driver for Atmel AT91RM9200 (Thunder) + * + * Copyright (C) 2003 SAN People (Pty) Ltd + * + * 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 + + +#define WDT_DEFAULT_TIME 5 /* seconds */ +#define WDT_MAX_TIME 256 /* seconds */ + +static int wdt_time = WDT_DEFAULT_TIME; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_time, int, 0); +MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +#endif + + +static unsigned long at91wdt_busy; + +/* ......................................................................... */ + +/* + * Disable the watchdog. + */ +static void inline at91_wdt_stop(void) +{ + at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); +} + +/* + * Enable and reset the watchdog. + */ +static void inline at91_wdt_start(void) +{ + at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); + at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); +} + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static void inline at91_wdt_reload(void) +{ + at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); +} + +/* ......................................................................... */ + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int at91_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &at91wdt_busy)) + return -EBUSY; + + at91_wdt_start(); + return nonseekable_open(inode, file); +} + +/* + * Close the watchdog device. + * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also + * disabled. + */ +static int at91_wdt_close(struct inode *inode, struct file *file) +{ + if (!nowayout) + at91_wdt_stop(); /* Disable the watchdog when file is closed */ + + clear_bit(0, &at91wdt_busy); + return 0; +} + +/* + * Change the watchdog time interval. + */ +static int at91_wdt_settimeout(int new_time) +{ + /* + * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz + * + * Since WDV is a 16-bit counter, the maximum period is + * 65536 / 0.256 = 256 seconds. + */ + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; + + /* Set new watchdog time. It will be used when at91_wdt_start() is called. */ + wdt_time = new_time; + return 0; +} + +static struct watchdog_info at91_wdt_info = { + .identity = "at91 watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +/* + * Handle commands from user-space. + */ +static int at91_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_value; + + switch(cmd) { + case WDIOC_KEEPALIVE: + at91_wdt_reload(); /* pat the watchdog */ + return 0; + + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + if (at91_wdt_settimeout(new_value)) + return -EINVAL; + + /* Enable new time value */ + at91_wdt_start(); + + /* Return current value */ + return put_user(wdt_time, p); + + case WDIOC_GETTIMEOUT: + return put_user(wdt_time, p); + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + + if (new_value & WDIOS_DISABLECARD) + at91_wdt_stop(); + if (new_value & WDIOS_ENABLECARD) + at91_wdt_start(); + return 0; + + default: + return -ENOTTY; + } +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + at91_wdt_reload(); /* pat the watchdog */ + return len; +} + +/* ......................................................................... */ + +static const struct file_operations at91wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = at91_wdt_ioctl, + .open = at91_wdt_open, + .release = at91_wdt_close, + .write = at91_wdt_write, +}; + +static struct miscdevice at91wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &at91wdt_fops, +}; + +static int __init at91wdt_probe(struct platform_device *pdev) +{ + int res; + + if (at91wdt_miscdev.parent) + return -EBUSY; + at91wdt_miscdev.parent = &pdev->dev; + + res = misc_register(&at91wdt_miscdev); + if (res) + return res; + + printk("AT91 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : ""); + return 0; +} + +static int __exit at91wdt_remove(struct platform_device *pdev) +{ + int res; + + res = misc_deregister(&at91wdt_miscdev); + if (!res) + at91wdt_miscdev.parent = NULL; + + return res; +} + +static void at91wdt_shutdown(struct platform_device *pdev) +{ + at91_wdt_stop(); +} + +#ifdef CONFIG_PM + +static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) +{ + at91_wdt_stop(); + return 0; +} + +static int at91wdt_resume(struct platform_device *pdev) +{ + if (at91wdt_busy) + at91_wdt_start(); + return 0; +} + +#else +#define at91wdt_suspend NULL +#define at91wdt_resume NULL +#endif + +static struct platform_driver at91wdt_driver = { + .probe = at91wdt_probe, + .remove = __exit_p(at91wdt_remove), + .shutdown = at91wdt_shutdown, + .suspend = at91wdt_suspend, + .resume = at91wdt_resume, + .driver = { + .name = "at91_wdt", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_wdt_init(void) +{ + /* Check that the heartbeat value is within range; if not reset to the default */ + if (at91_wdt_settimeout(wdt_time)) { + at91_wdt_settimeout(WDT_DEFAULT_TIME); + pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); + } + + return platform_driver_register(&at91wdt_driver); +} + +static void __exit at91_wdt_exit(void) +{ + platform_driver_unregister(&at91wdt_driver); +} + +module_init(at91_wdt_init); +module_exit(at91_wdt_exit); + +MODULE_AUTHOR("Andrew Victor"); +MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c new file mode 100644 index 0000000..309d279 --- /dev/null +++ b/drivers/watchdog/bfin_wdt.c @@ -0,0 +1,490 @@ +/* + * Blackfin On-Chip Watchdog Driver + * Supports BF53[123]/BF53[467]/BF54[2489]/BF561 + * + * Originally based on softdog.c + * Copyright 2006-2007 Analog Devices Inc. + * Copyright 2006-2007 Michele d'Amico + * Copyright 1996 Alan Cox + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) +#define stampit() stamp("here i am") + +#define WATCHDOG_NAME "bfin-wdt" +#define PFX WATCHDOG_NAME ": " + +/* The BF561 has two watchdogs (one per core), but since Linux + * only runs on core A, we'll just work with that one. + */ +#ifdef BF561_FAMILY +# define bfin_read_WDOG_CTL() bfin_read_WDOGA_CTL() +# define bfin_read_WDOG_CNT() bfin_read_WDOGA_CNT() +# define bfin_read_WDOG_STAT() bfin_read_WDOGA_STAT() +# define bfin_write_WDOG_CTL(x) bfin_write_WDOGA_CTL(x) +# define bfin_write_WDOG_CNT(x) bfin_write_WDOGA_CNT(x) +# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x) +#endif + +/* Bit in SWRST that indicates boot caused by watchdog */ +#define SWRST_RESET_WDOG 0x4000 + +/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */ +#define WDOG_EXPIRED 0x8000 + +/* Masks for WDEV field in WDOG_CTL register */ +#define ICTL_RESET 0x0 +#define ICTL_NMI 0x2 +#define ICTL_GPI 0x4 +#define ICTL_NONE 0x6 +#define ICTL_MASK 0x6 + +/* Masks for WDEN field in WDOG_CTL register */ +#define WDEN_MASK 0x0FF0 +#define WDEN_ENABLE 0x0000 +#define WDEN_DISABLE 0x0AD0 + +/* some defaults */ +#define WATCHDOG_TIMEOUT 20 + +static unsigned int timeout = WATCHDOG_TIMEOUT; +static int nowayout = WATCHDOG_NOWAYOUT; +static struct watchdog_info bfin_wdt_info; +static unsigned long open_check; +static char expect_close; +static spinlock_t bfin_wdt_spinlock = SPIN_LOCK_UNLOCKED; + +/** + * bfin_wdt_keepalive - Keep the Userspace Watchdog Alive + * + * The Userspace watchdog got a KeepAlive: schedule the next timeout. + */ +static int bfin_wdt_keepalive(void) +{ + stampit(); + bfin_write_WDOG_STAT(0); + return 0; +} + +/** + * bfin_wdt_stop - Stop the Watchdog + * + * Stops the on-chip watchdog. + */ +static int bfin_wdt_stop(void) +{ + stampit(); + bfin_write_WDOG_CTL(WDEN_DISABLE); + return 0; +} + +/** + * bfin_wdt_start - Start the Watchdog + * + * Starts the on-chip watchdog. Automatically loads WDOG_CNT + * into WDOG_STAT for us. + */ +static int bfin_wdt_start(void) +{ + stampit(); + bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET); + return 0; +} + +/** + * bfin_wdt_running - Check Watchdog status + * + * See if the watchdog is running. + */ +static int bfin_wdt_running(void) +{ + stampit(); + return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE); +} + +/** + * bfin_wdt_set_timeout - Set the Userspace Watchdog timeout + * @t: new timeout value (in seconds) + * + * Translate the specified timeout in seconds into System Clock + * terms which is what the on-chip Watchdog requires. + */ +static int bfin_wdt_set_timeout(unsigned long t) +{ + u32 cnt; + unsigned long flags; + + stampit(); + + cnt = t * get_sclk(); + if (cnt < get_sclk()) { + printk(KERN_WARNING PFX "timeout value is too large\n"); + return -EINVAL; + } + + spin_lock_irqsave(&bfin_wdt_spinlock, flags); + { + int run = bfin_wdt_running(); + bfin_wdt_stop(); + bfin_write_WDOG_CNT(cnt); + if (run) bfin_wdt_start(); + } + spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); + + timeout = t; + + return 0; +} + +/** + * bfin_wdt_open - Open the Device + * @inode: inode of device + * @file: file handle of device + * + * Watchdog device is opened and started. + */ +static int bfin_wdt_open(struct inode *inode, struct file *file) +{ + stampit(); + + if (test_and_set_bit(0, &open_check)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + bfin_wdt_keepalive(); + bfin_wdt_start(); + + return nonseekable_open(inode, file); +} + +/** + * bfin_wdt_close - Close the Device + * @inode: inode of device + * @file: file handle of device + * + * Watchdog device is closed and stopped. + */ +static int bfin_wdt_release(struct inode *inode, struct file *file) +{ + stampit(); + + if (expect_close == 42) { + bfin_wdt_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + bfin_wdt_keepalive(); + } + + expect_close = 0; + clear_bit(0, &open_check); + + return 0; +} + +/** + * bfin_wdt_write - Write to Device + * @file: file handle of device + * @buf: buffer to write + * @count: length of buffer + * @ppos: offset + * + * Pings the watchdog on write. + */ +static ssize_t bfin_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + stampit(); + + 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 = 42; + } + } + bfin_wdt_keepalive(); + } + + return len; +} + +/** + * bfin_wdt_ioctl - Query Device + * @inode: inode of device + * @file: file handle of device + * @cmd: watchdog command + * @arg: argument + * + * Query basic information from the device or ping it, as outlined by the + * watchdog API. + */ +static int bfin_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + + stampit(); + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info))) + return -EFAULT; + else + return 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p); + + case WDIOC_KEEPALIVE: + bfin_wdt_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: { + int new_timeout; + + if (get_user(new_timeout, p)) + return -EFAULT; + + if (bfin_wdt_set_timeout(new_timeout)) + return -EINVAL; + } + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + + case WDIOC_SETOPTIONS: { + unsigned long flags; + int options, ret = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + spin_lock_irqsave(&bfin_wdt_spinlock, flags); + + if (options & WDIOS_DISABLECARD) { + bfin_wdt_stop(); + ret = 0; + } + + if (options & WDIOS_ENABLECARD) { + bfin_wdt_start(); + ret = 0; + } + + spin_unlock_irqrestore(&bfin_wdt_spinlock, flags); + + return ret; + } + } +} + +/** + * bfin_wdt_notify_sys - Notifier Handler + * @this: notifier block + * @code: notifier event + * @unused: unused + * + * Handles specific events, such as turning off the watchdog during a + * shutdown event. + */ +static int bfin_wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + stampit(); + + if (code == SYS_DOWN || code == SYS_HALT) + bfin_wdt_stop(); + + return NOTIFY_DONE; +} + +#ifdef CONFIG_PM +static int state_before_suspend; + +/** + * bfin_wdt_suspend - suspend the watchdog + * @pdev: device being suspended + * @state: requested suspend state + * + * Remember if the watchdog was running and stop it. + * TODO: is this even right? Doesn't seem to be any + * standard in the watchdog world ... + */ +static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + stampit(); + + state_before_suspend = bfin_wdt_running(); + bfin_wdt_stop(); + + return 0; +} + +/** + * bfin_wdt_resume - resume the watchdog + * @pdev: device being resumed + * + * If the watchdog was running, turn it back on. + */ +static int bfin_wdt_resume(struct platform_device *pdev) +{ + stampit(); + + if (state_before_suspend) { + bfin_wdt_set_timeout(timeout); + bfin_wdt_start(); + } + + return 0; +} +#else +# define bfin_wdt_suspend NULL +# 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 struct file_operations bfin_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = bfin_wdt_write, + .ioctl = bfin_wdt_ioctl, + .open = bfin_wdt_open, + .release = bfin_wdt_release, +}; + +static struct miscdevice bfin_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &bfin_wdt_fops, +}; + +static struct watchdog_info bfin_wdt_info = { + .identity = "Blackfin Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static struct notifier_block bfin_wdt_notifier = { + .notifier_call = bfin_wdt_notify_sys, +}; + +/** + * bfin_wdt_init - Initialize module + * + * Registers the device and notifier handler. Actual device + * initialization is handled by bfin_wdt_open(). + */ +static int __init bfin_wdt_init(void) +{ + int ret; + + stampit(); + + /* Check that the timeout value is within range */ + if (bfin_wdt_set_timeout(timeout)) + return -EINVAL; + + /* 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); + if (ret) { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); + return ret; + } + + ret = misc_register(&bfin_wdt_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&bfin_wdt_notifier); + return ret; + } + + printk(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(). + */ +static void __exit bfin_wdt_exit(void) +{ + misc_deregister(&bfin_wdt_miscdev); + unregister_reboot_notifier(&bfin_wdt_notifier); +} + +module_init(bfin_wdt_init); +module_exit(bfin_wdt_exit); + +MODULE_AUTHOR("Michele d'Amico, Mike Frysinger "); +MODULE_DESCRIPTION("Blackfin Watchdog Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c new file mode 100644 index 0000000..d362f5b --- /dev/null +++ b/drivers/watchdog/booke_wdt.c @@ -0,0 +1,194 @@ +/* + * drivers/char/watchdog/booke_wdt.c + * + * Watchdog timer for PowerPC Book-E systems + * + * Author: Matthew McClintock + * Maintainer: Kumar Gala + * + * Copyright 2005 Freescale Semiconductor 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 + +/* If the kernel parameter wdt=1, the watchdog will be enabled at boot. + * Also, the wdt_period sets the watchdog timer period timeout. + * For E500 cpus the wdt_period sets which bit changing from 0->1 will + * trigger a watchog timeout. This watchdog timeout will occur 3 times, the + * first time nothing will happen, the second time a watchdog exception will + * occur, and the final time the board will reset. + */ + +#ifdef CONFIG_FSL_BOOKE +#define WDT_PERIOD_DEFAULT 63 /* Ex. wdt_period=28 bus=333Mhz , reset=~40sec */ +#else +#define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */ +#endif /* for timing information */ + +u32 booke_wdt_enabled = 0; +u32 booke_wdt_period = WDT_PERIOD_DEFAULT; + +#ifdef CONFIG_FSL_BOOKE +#define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15)) +#else +#define WDTP(x) (TCR_WP(x)) +#endif + +/* + * booke_wdt_ping: + */ +static __inline__ void booke_wdt_ping(void) +{ + mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); +} + +/* + * booke_wdt_enable: + */ +static __inline__ void booke_wdt_enable(void) +{ + u32 val; + + /* clear status before enabling watchdog */ + booke_wdt_ping(); + val = mfspr(SPRN_TCR); + val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period)); + + mtspr(SPRN_TCR, val); +} + +/* + * booke_wdt_write: + */ +static ssize_t booke_wdt_write (struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + booke_wdt_ping(); + return count; +} + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "PowerPC Book-E Watchdog", +}; + +/* + * booke_wdt_ioctl: + */ +static int booke_wdt_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + u32 tmp = 0; + u32 __user *p = (u32 __user *)arg; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user ((struct watchdog_info __user *) arg, &ident, + sizeof(struct watchdog_info))) + return -EFAULT; + case WDIOC_GETSTATUS: + return put_user(ident.options, p); + case WDIOC_GETBOOTSTATUS: + /* XXX: something is clearing TSR */ + tmp = mfspr(SPRN_TSR) & TSR_WRS(3); + /* returns 1 if last reset was caused by the WDT */ + return (tmp ? 1 : 0); + case WDIOC_KEEPALIVE: + booke_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(booke_wdt_period, p)) + return -EFAULT; + mtspr(SPRN_TCR, (mfspr(SPRN_TCR)&~WDTP(0))|WDTP(booke_wdt_period)); + return 0; + case WDIOC_GETTIMEOUT: + return put_user(booke_wdt_period, p); + case WDIOC_SETOPTIONS: + if (get_user(tmp, p)) + return -EINVAL; + if (tmp == WDIOS_ENABLECARD) { + booke_wdt_ping(); + break; + } else + return -EINVAL; + return 0; + default: + return -ENOTTY; + } + + return 0; +} +/* + * booke_wdt_open: + */ +static int booke_wdt_open (struct inode *inode, struct file *file) +{ + if (booke_wdt_enabled == 0) { + booke_wdt_enabled = 1; + booke_wdt_enable(); + printk (KERN_INFO "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", + booke_wdt_period); + } + + return nonseekable_open(inode, file); +} + +static const struct file_operations booke_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = booke_wdt_write, + .ioctl = booke_wdt_ioctl, + .open = booke_wdt_open, +}; + +static struct miscdevice booke_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &booke_wdt_fops, +}; + +static void __exit booke_wdt_exit(void) +{ + misc_deregister(&booke_wdt_miscdev); +} + +/* + * booke_wdt_init: + */ +static int __init booke_wdt_init(void) +{ + int ret = 0; + + printk (KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n"); + ident.firmware_version = cur_cpu_spec->pvr_value; + + ret = misc_register(&booke_wdt_miscdev); + if (ret) { + printk (KERN_CRIT "Cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + return ret; + } + + if (booke_wdt_enabled == 1) { + printk (KERN_INFO "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", + booke_wdt_period); + booke_wdt_enable(); + } + + return ret; +} +device_initcall(booke_wdt_init); diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c new file mode 100644 index 0000000..5941ca6 --- /dev/null +++ b/drivers/watchdog/cpu5wdt.c @@ -0,0 +1,304 @@ +/* + * sma cpu5 watchdog driver + * + * Copyright (C) 2003 Heiko Ronsdorf + * + * 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 + +/* adjustable parameters */ + +static int verbose = 0; +static int port = 0x91; +static int ticks = 10000; + +#define PFX "cpu5wdt: " + +#define CPU5WDT_EXTENT 0x0A + +#define CPU5WDT_STATUS_REG 0x00 +#define CPU5WDT_TIME_A_REG 0x02 +#define CPU5WDT_TIME_B_REG 0x03 +#define CPU5WDT_MODE_REG 0x04 +#define CPU5WDT_TRIGGER_REG 0x07 +#define CPU5WDT_ENABLE_REG 0x08 +#define CPU5WDT_RESET_REG 0x09 + +#define CPU5WDT_INTERVAL (HZ/10+1) + +/* some device data */ + +static struct { + struct completion stop; + volatile int running; + struct timer_list timer; + volatile int queue; + int default_ticks; + unsigned long inuse; +} cpu5wdt_device; + +/* generic helper functions */ + +static void cpu5wdt_trigger(unsigned long unused) +{ + if ( verbose > 2 ) + printk(KERN_DEBUG PFX "trigger at %i ticks\n", ticks); + + if( cpu5wdt_device.running ) + ticks--; + + /* keep watchdog alive */ + outb(1, port + CPU5WDT_TRIGGER_REG); + + /* requeue?? */ + if (cpu5wdt_device.queue && ticks) + mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL); + else { + /* ticks doesn't matter anyway */ + complete(&cpu5wdt_device.stop); + } + +} + +static void cpu5wdt_reset(void) +{ + ticks = cpu5wdt_device.default_ticks; + + if ( verbose ) + printk(KERN_DEBUG PFX "reset (%i ticks)\n", (int) ticks); + +} + +static void cpu5wdt_start(void) +{ + if ( !cpu5wdt_device.queue ) { + cpu5wdt_device.queue = 1; + outb(0, port + CPU5WDT_TIME_A_REG); + outb(0, port + CPU5WDT_TIME_B_REG); + outb(1, port + CPU5WDT_MODE_REG); + outb(0, port + CPU5WDT_RESET_REG); + outb(0, port + CPU5WDT_ENABLE_REG); + mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL); + } + /* if process dies, counter is not decremented */ + cpu5wdt_device.running++; +} + +static int cpu5wdt_stop(void) +{ + if ( cpu5wdt_device.running ) + cpu5wdt_device.running = 0; + + ticks = cpu5wdt_device.default_ticks; + + if ( verbose ) + printk(KERN_CRIT PFX "stop not possible\n"); + + return -EIO; +} + +/* filesystem operations */ + +static int cpu5wdt_open(struct inode *inode, struct file *file) +{ + if ( test_and_set_bit(0, &cpu5wdt_device.inuse) ) + return -EBUSY; + + return nonseekable_open(inode, file); +} + +static int cpu5wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &cpu5wdt_device.inuse); + return 0; +} + +static int cpu5wdt_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 = "CPU5 WDT", + }; + + switch(cmd) { + case WDIOC_KEEPALIVE: + cpu5wdt_reset(); + break; + case WDIOC_GETSTATUS: + value = inb(port + CPU5WDT_STATUS_REG); + value = (value >> 2) & 1; + if ( copy_to_user(argp, &value, sizeof(int)) ) + return -EFAULT; + break; + 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: + cpu5wdt_start(); + break; + case WDIOS_DISABLECARD: + return cpu5wdt_stop(); + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + +static ssize_t cpu5wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + if ( !count ) + return -EIO; + + cpu5wdt_reset(); + + return count; +} + +static const struct file_operations cpu5wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = cpu5wdt_ioctl, + .open = cpu5wdt_open, + .write = cpu5wdt_write, + .release = cpu5wdt_release, +}; + +static struct miscdevice cpu5wdt_misc = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &cpu5wdt_fops, +}; + +/* init/exit function */ + +static int __devinit cpu5wdt_init(void) +{ + unsigned int val; + int err; + + if ( verbose ) + printk(KERN_DEBUG PFX "port=0x%x, verbose=%i\n", port, verbose); + + if ( !request_region(port, CPU5WDT_EXTENT, PFX) ) { + printk(KERN_ERR PFX "request_region failed\n"); + err = -EBUSY; + goto no_port; + } + + if ( (err = misc_register(&cpu5wdt_misc)) < 0 ) { + printk(KERN_ERR PFX "misc_register failed\n"); + goto no_misc; + } + + /* watchdog reboot? */ + val = inb(port + CPU5WDT_STATUS_REG); + val = (val >> 2) & 1; + if ( !val ) + printk(KERN_INFO PFX "sorry, was my fault\n"); + + init_completion(&cpu5wdt_device.stop); + cpu5wdt_device.queue = 0; + + clear_bit(0, &cpu5wdt_device.inuse); + + setup_timer(&cpu5wdt_device.timer, cpu5wdt_trigger, 0); + + cpu5wdt_device.default_ticks = ticks; + + printk(KERN_INFO PFX "init success\n"); + + return 0; + +no_misc: + release_region(port, CPU5WDT_EXTENT); +no_port: + return err; +} + +static int __devinit cpu5wdt_init_module(void) +{ + return cpu5wdt_init(); +} + +static void __devexit cpu5wdt_exit(void) +{ + if ( cpu5wdt_device.queue ) { + cpu5wdt_device.queue = 0; + wait_for_completion(&cpu5wdt_device.stop); + } + + misc_deregister(&cpu5wdt_misc); + + release_region(port, CPU5WDT_EXTENT); + +} + +static void __devexit cpu5wdt_exit_module(void) +{ + cpu5wdt_exit(); +} + +/* module entry points */ + +module_init(cpu5wdt_init_module); +module_exit(cpu5wdt_exit_module); + +MODULE_AUTHOR("Heiko Ronsdorf "); +MODULE_DESCRIPTION("sma cpu5 watchdog driver"); +MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +module_param(port, int, 0); +MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91"); + +module_param(verbose, int, 0); +MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)"); + +module_param(ticks, int, 0); +MODULE_PARM_DESC(ticks, "count down ticks, default is 10000"); diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c new file mode 100644 index 0000000..19db530 --- /dev/null +++ b/drivers/watchdog/davinci_wdt.c @@ -0,0 +1,281 @@ +/* + * drivers/char/watchdog/davinci_wdt.c + * + * Watchdog driver for DaVinci DM644x/DM646x processors + * + * Copyright (C) 2006 Texas Instruments. + * + * 2007 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MODULE_NAME "DAVINCI-WDT: " + +#define DEFAULT_HEARTBEAT 60 +#define MAX_HEARTBEAT 600 /* really the max margin is 264/27MHz*/ + +/* Timer register set definition */ +#define PID12 (0x0) +#define EMUMGT (0x4) +#define TIM12 (0x10) +#define TIM34 (0x14) +#define PRD12 (0x18) +#define PRD34 (0x1C) +#define TCR (0x20) +#define TGCR (0x24) +#define WDTCR (0x28) + +/* TCR bit definitions */ +#define ENAMODE12_DISABLED (0 << 6) +#define ENAMODE12_ONESHOT (1 << 6) +#define ENAMODE12_PERIODIC (2 << 6) + +/* TGCR bit definitions */ +#define TIM12RS_UNRESET (1 << 0) +#define TIM34RS_UNRESET (1 << 1) +#define TIMMODE_64BIT_WDOG (2 << 2) + +/* WDTCR bit definitions */ +#define WDEN (1 << 14) +#define WDFLAG (1 << 15) +#define WDKEY_SEQ0 (0xa5c6 << 16) +#define WDKEY_SEQ1 (0xda7e << 16) + +static int heartbeat = DEFAULT_HEARTBEAT; + +static spinlock_t io_lock; +static unsigned long wdt_status; +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 +#define WDT_REGION_INITED 2 +#define WDT_DEVICE_INITED 3 + +static struct resource *wdt_mem; +static void __iomem *wdt_base; + +static void wdt_service(void) +{ + spin_lock(&io_lock); + + /* put watchdog in service state */ + davinci_writel(WDKEY_SEQ0, wdt_base + WDTCR); + /* put watchdog in active state */ + davinci_writel(WDKEY_SEQ1, wdt_base + WDTCR); + + spin_unlock(&io_lock); +} + +static void wdt_enable(void) +{ + u32 tgcr; + u32 timer_margin; + + spin_lock(&io_lock); + + /* disable, internal clock source */ + davinci_writel(0, wdt_base + TCR); + /* reset timer, set mode to 64-bit watchdog, and unreset */ + davinci_writel(0, wdt_base + TGCR); + tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET; + davinci_writel(tgcr, wdt_base + TGCR); + /* clear counter regs */ + davinci_writel(0, wdt_base + TIM12); + davinci_writel(0, wdt_base + TIM34); + /* set timeout period */ + timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff); + davinci_writel(timer_margin, wdt_base + PRD12); + timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32); + davinci_writel(timer_margin, wdt_base + PRD34); + /* enable run continuously */ + davinci_writel(ENAMODE12_PERIODIC, wdt_base + TCR); + /* Once the WDT is in pre-active state write to + * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are + * write protected (except for the WDKEY field) + */ + /* put watchdog in pre-active state */ + davinci_writel(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR); + /* put watchdog in active state */ + davinci_writel(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR); + + spin_unlock(&io_lock); +} + +static int davinci_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +static ssize_t +davinci_wdt_write(struct file *file, const char *data, size_t len, + loff_t *ppos) +{ + if (len) + wdt_service(); + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING, + .identity = "DaVinci Watchdog", +}; + +static int +davinci_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_service(); + ret = 0; + break; + } + return ret; +} + +static int davinci_wdt_release(struct inode *inode, struct file *file) +{ + wdt_service(); + clear_bit(WDT_IN_USE, &wdt_status); + + return 0; +} + +static const struct file_operations davinci_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = davinci_wdt_write, + .ioctl = davinci_wdt_ioctl, + .open = davinci_wdt_open, + .release = davinci_wdt_release, +}; + +static struct miscdevice davinci_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &davinci_wdt_fops, +}; + +static int davinci_wdt_probe(struct platform_device *pdev) +{ + int ret = 0, size; + struct resource *res; + + spin_lock_init(&io_lock); + + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; + + printk(KERN_INFO MODULE_NAME + "DaVinci Watchdog Timer: heartbeat %d sec\n", heartbeat); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk(KERN_INFO MODULE_NAME + "failed to get memory region resource\n"); + return -ENOENT; + } + + size = res->end - res->start + 1; + wdt_mem = request_mem_region(res->start, size, pdev->name); + + if (wdt_mem == NULL) { + printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); + return -ENOENT; + } + wdt_base = (void __iomem *)(res->start); + + ret = misc_register(&davinci_wdt_miscdev); + if (ret < 0) { + printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); + release_resource(wdt_mem); + kfree(wdt_mem); + } else { + set_bit(WDT_DEVICE_INITED, &wdt_status); + } + + return ret; +} + +static int davinci_wdt_remove(struct platform_device *pdev) +{ + misc_deregister(&davinci_wdt_miscdev); + if (wdt_mem) { + release_resource(wdt_mem); + kfree(wdt_mem); + wdt_mem = NULL; + } + return 0; +} + +static struct platform_driver platform_wdt_driver = { + .driver = { + .name = "watchdog", + }, + .probe = davinci_wdt_probe, + .remove = davinci_wdt_remove, +}; + +static int __init davinci_wdt_init(void) +{ + return platform_driver_register(&platform_wdt_driver); +} + +static void __exit davinci_wdt_exit(void) +{ + return platform_driver_unregister(&platform_wdt_driver); +} + +module_init(davinci_wdt_init); +module_exit(davinci_wdt_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("DaVinci Watchdog Driver"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(MAX_HEARTBEAT) ", default " + __MODULE_STRING(DEFAULT_HEARTBEAT)); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c new file mode 100644 index 0000000..0e4787a --- /dev/null +++ b/drivers/watchdog/ep93xx_wdt.c @@ -0,0 +1,253 @@ +/* + * Watchdog driver for Cirrus Logic EP93xx family of devices. + * + * Copyright (c) 2004 Ray Lehtiniemi + * Copyright (c) 2006 Tower Technologies + * Based on ep93xx driver, bits from alim7101_wdt.c + * + * Authors: Ray Lehtiniemi , + * Alessandro Zummo + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * This watchdog fires after 250msec, which is a too short interval + * for us to rely on the user space daemon alone. So we ping the + * wdt each ~200msec and eventually stop doing it if the user space + * daemon dies. + * + * TODO: + * + * - Test last reset from watchdog status + * - Add a few missing ioctls + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define WDT_VERSION "0.3" +#define PFX "ep93xx_wdt: " + +/* default timeout (secs) */ +#define WDT_TIMEOUT 30 + +static int nowayout = WATCHDOG_NOWAYOUT; +static int timeout = WDT_TIMEOUT; + +static struct timer_list timer; +static unsigned long next_heartbeat; +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x)) +#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00) +#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04) + +/* reset the wdt every ~200ms */ +#define WDT_INTERVAL (HZ/5) + +static void wdt_enable(void) +{ + __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); +} + +static void wdt_disable(void) +{ + __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); +} + +static inline void wdt_ping(void) +{ + __raw_writew(0x5555, EP93XX_WDT_WATCHDOG); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + wdt_enable(); + mod_timer(&timer, jiffies + WDT_INTERVAL); +} + +static void wdt_shutdown(void) +{ + del_timer_sync(&timer); + wdt_disable(); +} + +static void wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); +} + +static int ep93xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_startup(); + + return nonseekable_open(inode, file); +} + +static ssize_t +ep93xx_wdt_write(struct file *file, const char __user *data, size_t len, + loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + else + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_keepalive(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, + .identity = "EP93xx Watchdog", +}; + +static int +ep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int __user *)arg); + break; + + case WDIOC_GETTIMEOUT: + /* actually, it is 0.250 seconds.... */ + ret = put_user(1, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + ret = 0; + break; + } + return ret; +} + +static int ep93xx_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + wdt_shutdown(); + else + printk(KERN_CRIT PFX "Device closed unexpectedly - " + "timer will not stop\n"); + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static const struct file_operations ep93xx_wdt_fops = { + .owner = THIS_MODULE, + .write = ep93xx_wdt_write, + .ioctl = ep93xx_wdt_ioctl, + .open = ep93xx_wdt_open, + .release = ep93xx_wdt_release, +}; + +static struct miscdevice ep93xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ep93xx_wdt_fops, +}; + +static void ep93xx_timer_ping(unsigned long data) +{ + if (time_before(jiffies, next_heartbeat)) + wdt_ping(); + + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); +} + +static int __init ep93xx_wdt_init(void) +{ + int err; + + err = misc_register(&ep93xx_wdt_miscdev); + + boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; + + printk(KERN_INFO PFX "EP93XX watchdog, driver version " + WDT_VERSION "%s\n", + (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) + ? " (nCS1 disable detected)" : ""); + + if (timeout < 1 || timeout > 3600) { + timeout = WDT_TIMEOUT; + printk(KERN_INFO PFX + "timeout value must be 1<=x<=3600, using %d\n", + timeout); + } + + setup_timer(&timer, ep93xx_timer_ping, 1); + return err; +} + +static void __exit ep93xx_wdt_exit(void) +{ + wdt_shutdown(); + misc_deregister(&ep93xx_wdt_miscdev); +} + +module_init(ep93xx_wdt_init); +module_exit(ep93xx_wdt_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +MODULE_AUTHOR("Ray Lehtiniemi ," + "Alessandro Zummo "); +MODULE_DESCRIPTION("EP93xx Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(WDT_VERSION); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c new file mode 100644 index 0000000..b14e9d1 --- /dev/null +++ b/drivers/watchdog/eurotechwdt.c @@ -0,0 +1,473 @@ +/* + * Eurotech CPU-1220/1410/1420 on board WDT driver + * + * (c) Copyright 2001 Ascensit + * (c) Copyright 2001 Rodolfo Giometti + * (c) Copyright 2002 Rob Radez + * + * Based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox * + */ + +/* Changelog: + * + * 2001 - Rodolfo Giometti + * Initial release + * + * 2002/04/25 - Rob Radez + * clean up #includes + * clean up locking + * make __setup param unique + * proper options in watchdog_info + * add WDIOC_GETSTATUS and WDIOC_SETOPTIONS ioctls + * add expect_close support + * + * 2002.05.30 - Joel Becker + * Added Matt Domsch's nowayout module option. + */ + +/* + * The eurotech CPU-1220/1410/1420's watchdog is a part + * of the on-board SUPER I/O device SMSC FDC 37B782. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static unsigned long eurwdt_is_open; +static int eurwdt_timeout; +static char eur_expect_close; + +/* + * You must set these - there is no sane way to probe for this board. + * You can use eurwdt=x,y to set these now. + */ + +static int io = 0x3f0; +static int irq = 10; +static char *ev = "int"; + +#define WDT_TIMEOUT 60 /* 1 minute */ + +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) ")"); + +/* + * Some symbolic names + */ + +#define WDT_CTRL_REG 0x30 +#define WDT_OUTPIN_CFG 0xe2 +#define WDT_EVENT_INT 0x00 +#define WDT_EVENT_REBOOT 0x08 +#define WDT_UNIT_SEL 0xf1 +#define WDT_UNIT_SECS 0x80 +#define WDT_TIMEOUT_VAL 0xf2 +#define WDT_TIMER_CFG 0xf3 + + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)"); +module_param(irq, int, 0); +MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)"); +module_param(ev, charp, 0); +MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')"); + + +/* + * Programming support + */ + +static inline void eurwdt_write_reg(u8 index, u8 data) +{ + outb(index, io); + outb(data, io+1); +} + +static inline void eurwdt_lock_chip(void) +{ + outb(0xaa, io); +} + +static inline void eurwdt_unlock_chip(void) +{ + outb(0x55, io); + eurwdt_write_reg(0x07, 0x08); /* set the logical device */ +} + +static inline void eurwdt_set_timeout(int timeout) +{ + eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout); +} + +static inline void eurwdt_disable_timer(void) +{ + eurwdt_set_timeout(0); +} + +static void eurwdt_activate_timer(void) +{ + eurwdt_disable_timer(); + eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */ + eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT); + + /* Setting interrupt line */ + if (irq == 2 || irq > 15 || irq < 0) { + printk(KERN_ERR ": invalid irq number\n"); + irq = 0; /* if invalid we disable interrupt */ + } + if (irq == 0) + printk(KERN_INFO ": interrupt disabled\n"); + + eurwdt_write_reg(WDT_TIMER_CFG, irq<<4); + + eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */ + eurwdt_set_timeout(0); /* the default timeout */ +} + + +/* + * Kernel methods. + */ + +static irqreturn_t eurwdt_interrupt(int irq, void *dev_id) +{ + printk(KERN_CRIT "timeout WDT timeout\n"); + +#ifdef ONLY_TESTING + printk(KERN_CRIT "Would Reboot.\n"); +#else + printk(KERN_CRIT "Initiating system reboot.\n"); + emergency_restart(); +#endif + return IRQ_HANDLED; +} + + +/** + * eurwdt_ping: + * + * Reload counter one with the watchdog timeout. + */ + +static void eurwdt_ping(void) +{ + /* Write the watchdog default value */ + eurwdt_set_timeout(eurwdt_timeout); +} + +/** + * eurwdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t eurwdt_write(struct file *file, const char __user *buf, +size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + eur_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if(get_user(c, buf+i)) + return -EFAULT; + if (c == 'V') + eur_expect_close = 42; + } + } + eurwdt_ping(); /* the default timeout */ + } + + return count; +} + +/** + * eurwdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static int eurwdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "WDT Eurotech CPU-1220/1410", + }; + + int time; + int options, retval = -EINVAL; + + switch(cmd) { + default: + return -ENOTTY; + + 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: + eurwdt_ping(); + return 0; + + case WDIOC_SETTIMEOUT: + if (copy_from_user(&time, p, sizeof(int))) + return -EFAULT; + + /* Sanity check */ + if (time < 0 || time > 255) + return -EINVAL; + + eurwdt_timeout = time; + eurwdt_set_timeout(time); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(eurwdt_timeout, p); + + case WDIOC_SETOPTIONS: + if (get_user(options, p)) + return -EFAULT; + if (options & WDIOS_DISABLECARD) { + eurwdt_disable_timer(); + retval = 0; + } + if (options & WDIOS_ENABLECARD) { + eurwdt_activate_timer(); + eurwdt_ping(); + retval = 0; + } + return retval; + } +} + +/** + * eurwdt_open: + * @inode: inode of device + * @file: file handle to device + * + * The misc device has been opened. The watchdog device is single + * open and on opening we load the counter. + */ + +static int eurwdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &eurwdt_is_open)) + return -EBUSY; + eurwdt_timeout = WDT_TIMEOUT; /* initial timeout */ + /* Activate the WDT */ + eurwdt_activate_timer(); + return nonseekable_open(inode, file); +} + +/** + * eurwdt_release: + * @inode: inode to board + * @file: file handle to board + * + * The watchdog has a configurable API. There is a religious dispute + * between people who want their watchdog to be able to shut down and + * those who want to be sure if the watchdog manager dies the machine + * reboots. In the former case we disable the counters, in the latter + * case you have to open it again very soon. + */ + +static int eurwdt_release(struct inode *inode, struct file *file) +{ + if (eur_expect_close == 42) { + eurwdt_disable_timer(); + } else { + printk(KERN_CRIT "eurwdt: Unexpected close, not stopping watchdog!\n"); + eurwdt_ping(); + } + clear_bit(0, &eurwdt_is_open); + eur_expect_close = 0; + return 0; +} + +/** + * eurwdt_notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ + +static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the card off */ + eurwdt_disable_timer(); + } + + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + + +static const struct file_operations eurwdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = eurwdt_write, + .ioctl = eurwdt_ioctl, + .open = eurwdt_open, + .release = eurwdt_release, +}; + +static struct miscdevice eurwdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &eurwdt_fops, +}; + +/* + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block eurwdt_notifier = { + .notifier_call = eurwdt_notify_sys, +}; + +/** + * cleanup_module: + * + * Unload the watchdog. You cannot do this with any file handles open. + * If your watchdog is set to continue ticking on close and you unload + * it, well it keeps ticking. We won't get the interrupt but the board + * will not touch PC memory so all is fine. You just have to load a new + * module in 60 seconds or reboot. + */ + +static void __exit eurwdt_exit(void) +{ + eurwdt_lock_chip(); + + misc_deregister(&eurwdt_miscdev); + + unregister_reboot_notifier(&eurwdt_notifier); + release_region(io, 2); + free_irq(irq, NULL); +} + +/** + * eurwdt_init: + * + * Set up the WDT watchdog board. After grabbing the resources + * we require we need also to unlock the device. + * The open() function will actually kick the board off. + */ + +static int __init eurwdt_init(void) +{ + int ret; + + ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL); + if(ret) { + printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); + goto out; + } + + if (!request_region(io, 2, "eurwdt")) { + printk(KERN_ERR "eurwdt: IO %X is not free.\n", io); + ret = -EBUSY; + goto outirq; + } + + ret = register_reboot_notifier(&eurwdt_notifier); + if (ret) { + printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret); + goto outreg; + } + + ret = misc_register(&eurwdt_miscdev); + if (ret) { + printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", + WATCHDOG_MINOR); + goto outreboot; + } + + eurwdt_unlock_chip(); + + ret = 0; + printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" + " - timeout event: %s\n", + io, irq, (!strcmp("int", ev) ? "int" : "reboot")); + +out: + return ret; + +outreboot: + unregister_reboot_notifier(&eurwdt_notifier); + +outreg: + release_region(io, 2); + +outirq: + free_irq(irq, NULL); + goto out; +} + +module_init(eurwdt_init); +module_exit(eurwdt_exit); + +MODULE_AUTHOR("Rodolfo Giometti"); +MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c new file mode 100644 index 0000000..c598250 --- /dev/null +++ b/drivers/watchdog/i6300esb.c @@ -0,0 +1,527 @@ +/* + * i6300esb: Watchdog timer driver for Intel 6300ESB chipset + * + * (c) Copyright 2004 Google Inc. + * (c) Copyright 2005 David Härdeman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * based on i810-tco.c which is in turn based on softdog.c + * + * The timer is implemented in the following I/O controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * 6300ESB chip : document number 300641-003 + * + * 2004YYZZ Ross Biro + * Initial version 0.01 + * 2004YYZZ Ross Biro + * Version 0.02 + * 20050210 David Härdeman + * Ported driver to kernel 2.6 + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Module and version information */ +#define ESB_VERSION "0.03" +#define ESB_MODULE_NAME "i6300ESB timer" +#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION +#define PFX ESB_MODULE_NAME ": " + +/* PCI configuration registers */ +#define ESB_CONFIG_REG 0x60 /* Config register */ +#define ESB_LOCK_REG 0x68 /* WDT lock register */ + +/* Memory mapped registers */ +#define ESB_TIMER1_REG BASEADDR + 0x00 /* Timer1 value after each reset */ +#define ESB_TIMER2_REG BASEADDR + 0x04 /* Timer2 value after each reset */ +#define ESB_GINTSR_REG BASEADDR + 0x08 /* General Interrupt Status Register */ +#define ESB_RELOAD_REG BASEADDR + 0x0c /* Reload register */ + +/* Lock register bits */ +#define ESB_WDT_FUNC ( 0x01 << 2 ) /* Watchdog functionality */ +#define ESB_WDT_ENABLE ( 0x01 << 1 ) /* Enable WDT */ +#define ESB_WDT_LOCK ( 0x01 << 0 ) /* Lock (nowayout) */ + +/* Config register bits */ +#define ESB_WDT_REBOOT ( 0x01 << 5 ) /* Enable reboot on timeout */ +#define ESB_WDT_FREQ ( 0x01 << 2 ) /* Decrement frequency */ +#define ESB_WDT_INTTYPE ( 0x11 << 0 ) /* Interrupt type on timer1 timeout */ + +/* Reload register bits */ +#define ESB_WDT_RELOAD ( 0x01 << 8 ) /* prevent timeout */ + +/* Magic constants */ +#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ +#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ + +/* internal variables */ +static void __iomem *BASEADDR; +static spinlock_t esb_lock; /* Guards the hardware */ +static unsigned long timer_alive; +static struct pci_dev *esb_pci; +static unsigned short triggered; /* The status of the watchdog upon boot */ +static char esb_expect_close; + +/* module parameters */ +#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (1 (2 * 0x03ff)) + return -EINVAL; + + spin_lock(&esb_lock); + + /* We shift by 9, so if we are passed a value of 1 sec, + * val will be 1 << 9 = 512, then write that to two + * timers => 2 * 512 = 1024 (which is decremented at 1KHz) + */ + val = time << 9; + + /* Write timer 1 */ + esb_unlock_registers(); + writel(val, ESB_TIMER1_REG); + + /* Write timer 2 */ + esb_unlock_registers(); + writel(val, ESB_TIMER2_REG); + + /* Reload */ + esb_unlock_registers(); + writew(ESB_WDT_RELOAD, ESB_RELOAD_REG); + + /* FIXME: Do we need to flush everything out? */ + + /* Done */ + heartbeat = time; + spin_unlock(&esb_lock); + return 0; +} + +static int esb_timer_read (void) +{ + u32 count; + + /* This isn't documented, and doesn't take into + * acount which stage is running, but it looks + * like a 20 bit count down, so we might as well report it. + */ + pci_read_config_dword(esb_pci, 0x64, &count); + return (int)count; +} + +/* + * /dev/watchdog handling + */ + +static int esb_open (struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &timer_alive)) + return -EBUSY; + + /* Reload and activate timer */ + esb_timer_keepalive (); + esb_timer_start (); + + return nonseekable_open(inode, file); +} + +static int esb_release (struct inode *inode, struct file *file) +{ + /* Shut off the timer. */ + if (esb_expect_close == 42) { + esb_timer_stop (); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + esb_timer_keepalive (); + } + clear_bit(0, &timer_alive); + esb_expect_close = 0; + return 0; +} + +static ssize_t esb_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... */ + esb_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') + esb_expect_close = 42; + } + } + + /* someone wrote to us, we should reload the timer */ + esb_timer_keepalive (); + } + return len; +} + +static int esb_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 = ESB_MODULE_NAME, + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, + sizeof (ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + return put_user (esb_timer_read(), p); + + case WDIOC_GETBOOTSTATUS: + return put_user (triggered, p); + + case WDIOC_KEEPALIVE: + esb_timer_keepalive (); + return 0; + + case WDIOC_SETOPTIONS: + { + if (get_user (new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + esb_timer_stop (); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + esb_timer_keepalive (); + esb_timer_start (); + retval = 0; + } + + return retval; + } + + case WDIOC_SETTIMEOUT: + { + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (esb_timer_set_heartbeat(new_heartbeat)) + return -EINVAL; + + esb_timer_keepalive (); + /* Fall */ + } + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + + default: + return -ENOTTY; + } +} + +/* + * Notify system + */ + +static int esb_notify_sys (struct notifier_block *this, unsigned long code, void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) { + /* Turn the WDT off */ + esb_timer_stop (); + } + + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations esb_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = esb_write, + .ioctl = esb_ioctl, + .open = esb_open, + .release = esb_release, +}; + +static struct miscdevice esb_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &esb_fops, +}; + +static struct notifier_block esb_notifier = { + .notifier_call = esb_notify_sys, +}; + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id esb_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), }, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE (pci, esb_pci_tbl); + +/* + * Init & exit routines + */ + +static unsigned char __init esb_getdevice (void) +{ + u8 val1; + unsigned short val2; + + struct pci_dev *dev = NULL; + /* + * Find the PCI device + */ + + for_each_pci_dev(dev) { + if (pci_match_id(esb_pci_tbl, dev)) { + esb_pci = dev; + break; + } + } + + if (esb_pci) { + if (pci_enable_device(esb_pci)) { + printk (KERN_ERR PFX "failed to enable device\n"); + goto err_devput; + } + + if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) { + printk (KERN_ERR PFX "failed to request region\n"); + goto err_disable; + } + + BASEADDR = ioremap(pci_resource_start(esb_pci, 0), + pci_resource_len(esb_pci, 0)); + if (BASEADDR == NULL) { + /* Something's wrong here, BASEADDR has to be set */ + printk (KERN_ERR PFX "failed to get BASEADDR\n"); + goto err_release; + } + + /* + * The watchdog has two timers, it can be setup so that the + * expiry of timer1 results in an interrupt and the expiry of + * timer2 results in a reboot. We set it to not generate + * any interrupts as there is not much we can do with it + * right now. + * + * We also enable reboots and set the timer frequency to + * the PCI clock divided by 2^15 (approx 1KHz). + */ + pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003); + + /* Check that the WDT isn't already locked */ + pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1); + if (val1 & ESB_WDT_LOCK) + printk (KERN_WARNING PFX "nowayout already set\n"); + + /* Set the timer to watchdog mode and disable it for now */ + pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00); + + /* Check if the watchdog was previously triggered */ + esb_unlock_registers(); + val2 = readw(ESB_RELOAD_REG); + triggered = (val2 & (0x01 << 9) >> 9); + + /* Reset trigger flag and timers */ + esb_unlock_registers(); + writew((0x11 << 8), ESB_RELOAD_REG); + + /* Done */ + return 1; + +err_release: + pci_release_region(esb_pci, 0); +err_disable: + pci_disable_device(esb_pci); +err_devput: + pci_dev_put(esb_pci); + } + return 0; +} + +static int __init watchdog_init (void) +{ + int ret; + + spin_lock_init(&esb_lock); + + /* Check whether or not the hardware watchdog is there */ + if (!esb_getdevice () || esb_pci == NULL) + return -ENODEV; + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (esb_timer_set_heartbeat (heartbeat)) { + esb_timer_set_heartbeat (WATCHDOG_HEARTBEAT); + printk(KERN_INFO PFX "heartbeat value must be 1. + * + * 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. + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +/* Module and version information */ +#define DRV_NAME "iTCO_vendor_support" +#define DRV_VERSION "1.01" +#define DRV_RELDATE "11-Nov-2006" +#define PFX DRV_NAME ": " + +/* Includes */ +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For __init/__exit/... */ +#include /* For io-port access */ + +#include /* For inb/outb/... */ + +/* iTCO defines */ +#define SMI_EN acpibase + 0x30 /* SMI Control and Enable Register */ +#define TCOBASE acpibase + 0x60 /* TCO base address */ +#define TCO1_STS TCOBASE + 0x04 /* TCO1 Status Register */ + +/* List of vendor support modes */ +#define SUPERMICRO_OLD_BOARD 1 /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */ +#define SUPERMICRO_NEW_BOARD 2 /* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */ + +static int vendorsupport = 0; +module_param(vendorsupport, int, 0); +MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+"); + +/* + * Vendor Specific Support + */ + +/* + * Vendor Support: 1 + * Board: Super Micro Computer Inc. 370SSE+-OEM1/P3TSSE + * iTCO chipset: ICH2 + * + * Code contributed by: R. Seretny + * Documentation obtained by R. Seretny from SuperMicro Technical Support + * + * To enable Watchdog function: + * BIOS setup -> Power -> TCO Logic SMI Enable -> Within5Minutes + * This setting enables SMI to clear the watchdog expired flag. + * If BIOS or CPU fail which may cause SMI hang, then system will + * reboot. When application starts to use watchdog function, + * application has to take over the control from SMI. + * + * For P3TSSE, J36 jumper needs to be removed to enable the Watchdog + * function. + * + * Note: The system will reboot when Expire Flag is set TWICE. + * So, if the watchdog timer is 20 seconds, then the maximum hang + * time is about 40 seconds, and the minimum hang time is about + * 20.6 seconds. + */ + +static void supermicro_old_pre_start(unsigned long acpibase) +{ + unsigned long val32; + + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); /* Needed to activate watchdog */ +} + +static void supermicro_old_pre_stop(unsigned long acpibase) +{ + unsigned long val32; + + val32 = inl(SMI_EN); + val32 &= 0x00002000; /* Turn on SMI clearing watchdog */ + outl(val32, SMI_EN); /* Needed to deactivate watchdog */ +} + +static void supermicro_old_pre_keepalive(unsigned long acpibase) +{ + /* Reload TCO Timer (done in iTCO_wdt_keepalive) + */ + /* Clear "Expire Flag" (Bit 3 of TC01_STS register) */ + outb(0x08, TCO1_STS); +} + +/* + * Vendor Support: 2 + * Board: Super Micro Computer Inc. P4SBx, P4DPx + * iTCO chipset: ICH4 + * + * Code contributed by: R. Seretny + * Documentation obtained by R. Seretny from SuperMicro Technical Support + * + * To enable Watchdog function: + * 1. BIOS + * For P4SBx: + * BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature + * For P4DPx: + * BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog + * This setting enables or disables Watchdog function. When enabled, the + * default watchdog timer is set to be 5 minutes (about 4’35â€). It is + * enough to load and run the OS. The application (service or driver) has + * to take over the control once OS is running up and before watchdog + * expires. + * + * 2. JUMPER + * For P4SBx: JP39 + * For P4DPx: JP37 + * This jumper is used for safety. Closed is enabled. This jumper + * prevents user enables watchdog in BIOS by accident. + * + * To enable Watch Dog function, both BIOS and JUMPER must be enabled. + * + * The documentation lists motherboards P4SBx and P4DPx series as of + * 20-March-2002. However, this code works flawlessly with much newer + * motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82). + * + * The original iTCO driver as written does not actually reset the + * watchdog timer on these machines, as a result they reboot after five + * minutes. + * + * NOTE: You may leave the Watchdog function disabled in the SuperMicro + * BIOS to avoid a "boot-race"... This driver will enable watchdog + * functionality even if it's disabled in the BIOS once the /dev/watchdog + * file is opened. + */ + +/* I/O Port's */ +#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */ +#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */ + +/* Control Register's */ +#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */ +#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */ + +#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */ + +#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */ + +#define SM_ENDWATCH 0xAA /* Watchdog lock control page */ + +#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */ + /* (Bit 3: 0 = seconds, 1 = minutes */ + +#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */ + +#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */ + /* Bit 6: timer is reset by kbd interrupt */ + /* Bit 7: timer is reset by mouse interrupt */ + +static void supermicro_new_unlock_watchdog(void) +{ + outb(SM_WATCHPAGE, SM_REGINDEX); /* Write 0x87 to port 0x2e twice */ + outb(SM_WATCHPAGE, SM_REGINDEX); + + outb(SM_CTLPAGESW, SM_REGINDEX); /* Switch to watchdog control page */ + outb(SM_CTLPAGE, SM_DATAIO); +} + +static void supermicro_new_lock_watchdog(void) +{ + outb(SM_ENDWATCH, SM_REGINDEX); +} + +static void supermicro_new_pre_start(unsigned int heartbeat) +{ + unsigned int val; + + supermicro_new_unlock_watchdog(); + + /* Watchdog timer setting needs to be in seconds*/ + outb(SM_COUNTMODE, SM_REGINDEX); + val = inb(SM_DATAIO); + val &= 0xF7; + outb(val, SM_DATAIO); + + /* Write heartbeat interval to WDOG */ + outb (SM_WATCHTIMER, SM_REGINDEX); + outb((heartbeat & 255), SM_DATAIO); + + /* Make sure keyboard/mouse interrupts don't interfere */ + outb(SM_RESETCONTROL, SM_REGINDEX); + val = inb(SM_DATAIO); + val &= 0x3f; + outb(val, SM_DATAIO); + + /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */ + outb(SM_WATCHENABLE, SM_REGINDEX); + val = inb(SM_DATAIO); + val |= 0x01; + outb(val, SM_DATAIO); + + supermicro_new_lock_watchdog(); +} + +static void supermicro_new_pre_stop(void) +{ + unsigned int val; + + supermicro_new_unlock_watchdog(); + + /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */ + outb(SM_WATCHENABLE, SM_REGINDEX); + val = inb(SM_DATAIO); + val &= 0xFE; + outb(val, SM_DATAIO); + + supermicro_new_lock_watchdog(); +} + +static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) +{ + supermicro_new_unlock_watchdog(); + + /* reset watchdog timeout to heartveat value */ + outb(SM_WATCHTIMER, SM_REGINDEX); + outb((heartbeat & 255), SM_DATAIO); + + supermicro_new_lock_watchdog(); +} + +/* + * Generic Support Functions + */ + +void iTCO_vendor_pre_start(unsigned long acpibase, + unsigned int heartbeat) +{ + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_start(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) + supermicro_new_pre_start(heartbeat); +} +EXPORT_SYMBOL(iTCO_vendor_pre_start); + +void iTCO_vendor_pre_stop(unsigned long acpibase) +{ + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_stop(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) + supermicro_new_pre_stop(); +} +EXPORT_SYMBOL(iTCO_vendor_pre_stop); + +void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat) +{ + if (vendorsupport == SUPERMICRO_OLD_BOARD) + supermicro_old_pre_keepalive(acpibase); + else if (vendorsupport == SUPERMICRO_NEW_BOARD) + supermicro_new_pre_set_heartbeat(heartbeat); +} +EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); + +void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat) +{ + if (vendorsupport == SUPERMICRO_NEW_BOARD) + supermicro_new_pre_set_heartbeat(heartbeat); +} +EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat); + +int iTCO_vendor_check_noreboot_on(void) +{ + switch(vendorsupport) { + case SUPERMICRO_OLD_BOARD: + return 0; + default: + return 1; + } +} +EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on); + +static int __init iTCO_vendor_init_module(void) +{ + printk (KERN_INFO PFX "vendor-support=%d\n", vendorsupport); + return 0; +} + +static void __exit iTCO_vendor_exit_module(void) +{ + printk (KERN_INFO PFX "Module Unloaded\n"); +} + +module_init(iTCO_vendor_init_module); +module_exit(iTCO_vendor_exit_module); + +MODULE_AUTHOR("Wim Van Sebroeck , R. Seretny "); +MODULE_DESCRIPTION("Intel TCO Vendor Specific WatchDog Timer Driver Support"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); + diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c new file mode 100644 index 0000000..cd5a565 --- /dev/null +++ b/drivers/watchdog/iTCO_wdt.c @@ -0,0 +1,804 @@ +/* + * intel TCO Watchdog Driver (Used in i82801 and i6300ESB chipsets) + * + * (c) Copyright 2006-2007 Wim Van Sebroeck . + * + * 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. + * + * The TCO watchdog is implemented in the following I/O controller hubs: + * (See the intel documentation on http://developer.intel.com.) + * 82801AA (ICH) : document number 290655-003, 290677-014, + * 82801AB (ICHO) : document number 290655-003, 290677-014, + * 82801BA (ICH2) : document number 290687-002, 298242-027, + * 82801BAM (ICH2-M) : document number 290687-002, 298242-027, + * 82801CA (ICH3-S) : document number 290733-003, 290739-013, + * 82801CAM (ICH3-M) : document number 290716-001, 290718-007, + * 82801DB (ICH4) : document number 290744-001, 290745-020, + * 82801DBM (ICH4-M) : document number 252337-001, 252663-005, + * 82801E (C-ICH) : document number 273599-001, 273645-002, + * 82801EB (ICH5) : document number 252516-001, 252517-003, + * 82801ER (ICH5R) : document number 252516-001, 252517-003, + * 82801FB (ICH6) : document number 301473-002, 301474-007, + * 82801FR (ICH6R) : document number 301473-002, 301474-007, + * 82801FBM (ICH6-M) : document number 301473-002, 301474-007, + * 82801FW (ICH6W) : document number 301473-001, 301474-007, + * 82801FRW (ICH6RW) : document number 301473-001, 301474-007, + * 82801GB (ICH7) : document number 307013-002, 307014-009, + * 82801GR (ICH7R) : document number 307013-002, 307014-009, + * 82801GDH (ICH7DH) : 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, + * 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, + * 82801IB (ICH9) : document number 316972-001, 316973-001, + * 82801IR (ICH9R) : document number 316972-001, 316973-001, + * 82801IH (ICH9DH) : document number 316972-001, 316973-001, + * 6300ESB (6300ESB) : document number 300641-003, 300884-010, + * 631xESB (631xESB) : document number 313082-001, 313075-005, + * 632xESB (632xESB) : document number 313082-001, 313075-005 + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +/* Module and version information */ +#define DRV_NAME "iTCO_wdt" +#define DRV_VERSION "1.02" +#define DRV_RELDATE "26-Jul-2007" +#define PFX DRV_NAME ": " + +/* Includes */ +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For platform_driver framework */ +#include /* For pci functions */ +#include /* For io-port access */ +#include /* For spin_lock/spin_unlock/... */ + +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ + +/* TCO related info */ +enum iTCO_chipsets { + TCO_ICH = 0, /* ICH */ + TCO_ICH0, /* ICH0 */ + TCO_ICH2, /* ICH2 */ + TCO_ICH2M, /* ICH2-M */ + TCO_ICH3, /* ICH3-S */ + TCO_ICH3M, /* ICH3-M */ + TCO_ICH4, /* ICH4 */ + TCO_ICH4M, /* ICH4-M */ + TCO_CICH, /* C-ICH */ + TCO_ICH5, /* ICH5 & ICH5R */ + TCO_6300ESB, /* 6300ESB */ + TCO_ICH6, /* ICH6 & ICH6R */ + TCO_ICH6M, /* ICH6-M */ + TCO_ICH6W, /* ICH6W & ICH6RW */ + TCO_ICH7, /* ICH7 & ICH7R */ + TCO_ICH7M, /* ICH7-M */ + TCO_ICH7MDH, /* ICH7-M DH */ + TCO_ICH8, /* ICH8 & ICH8R */ + TCO_ICH8DH, /* ICH8DH */ + TCO_ICH8DO, /* ICH8DO */ + TCO_ICH9, /* ICH9 */ + TCO_ICH9R, /* ICH9R */ + TCO_ICH9DH, /* ICH9DH */ + TCO_631XESB, /* 631xESB/632xESB */ +}; + +static struct { + char *name; + unsigned int iTCO_version; +} iTCO_chipset_info[] __devinitdata = { + {"ICH", 1}, + {"ICH0", 1}, + {"ICH2", 1}, + {"ICH2-M", 1}, + {"ICH3-S", 1}, + {"ICH3-M", 1}, + {"ICH4", 1}, + {"ICH4-M", 1}, + {"C-ICH", 1}, + {"ICH5 or ICH5R", 1}, + {"6300ESB", 1}, + {"ICH6 or ICH6R", 2}, + {"ICH6-M", 2}, + {"ICH6W or ICH6RW", 2}, + {"ICH7 or ICH7R", 2}, + {"ICH7-M", 2}, + {"ICH7-M DH", 2}, + {"ICH8 or ICH8R", 2}, + {"ICH8DH", 2}, + {"ICH8DO", 2}, + {"ICH9", 2}, + {"ICH9R", 2}, + {"ICH9DH", 2}, + {"631xESB/632xESB", 2}, + {NULL,0} +}; + +/* + * This data only exists for exporting the supported PCI ids + * via MODULE_DEVICE_TABLE. We do not actually register a + * pci_driver, because the I/O Controller Hub has also other + * functions that probably will be registered by other drivers. + */ +static struct pci_device_id iTCO_wdt_pci_tbl[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH0 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH2M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH3M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH4M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_CICH }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH5 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_6300ESB }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6M }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH6W }, + { 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_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 }, + { PCI_VENDOR_ID_INTEL, 0x2918, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH9 }, + { PCI_VENDOR_ID_INTEL, 0x2916, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH9R }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_ICH9DH }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { PCI_VENDOR_ID_INTEL, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TCO_631XESB }, + { 0, }, /* End of list */ +}; +MODULE_DEVICE_TABLE (pci, iTCO_wdt_pci_tbl); + +/* Address definitions for the TCO */ +#define TCOBASE iTCO_wdt_private.ACPIBASE + 0x60 /* TCO base address */ +#define SMI_EN iTCO_wdt_private.ACPIBASE + 0x30 /* SMI Control and Enable Register */ + +#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 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 */ + 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) */ + spinlock_t io_lock; /* the lock for io operations */ + struct pci_dev *pdev; /* the PCI-device */ +} iTCO_wdt_private; + +static struct platform_device *iTCO_wdt_platform_device; /* the watchdog platform device */ + +/* module parameters */ +#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ +static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2 0 = The TCO timer is enabled to count */ + val = inw(TCO1_CNT); + val &= 0xf7ff; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + spin_unlock(&iTCO_wdt_private.io_lock); + + if (val & 0x0800) + return -1; + return 0; +} + +static int iTCO_wdt_stop(void) +{ + unsigned int val; + + spin_lock(&iTCO_wdt_private.io_lock); + + iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE); + + /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ + val = inw(TCO1_CNT); + val |= 0x0800; + outw(val, TCO1_CNT); + val = inw(TCO1_CNT); + + /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ + iTCO_wdt_set_NO_REBOOT_bit(); + + spin_unlock(&iTCO_wdt_private.io_lock); + + if ((val & 0x0800) == 0) + return -1; + return 0; +} + +static int iTCO_wdt_keepalive(void) +{ + spin_lock(&iTCO_wdt_private.io_lock); + + iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat); + + /* Reload the timer by writing to the TCO Timer Counter register */ + if (iTCO_wdt_private.iTCO_version == 2) { + outw(0x01, TCO_RLD); + } else if (iTCO_wdt_private.iTCO_version == 1) { + outb(0x01, TCO_RLD); + } + + spin_unlock(&iTCO_wdt_private.io_lock); + return 0; +} + +static int iTCO_wdt_set_heartbeat(int t) +{ + unsigned int val16; + unsigned char val8; + unsigned int tmrval; + + tmrval = seconds_to_ticks(t); + /* from the specs: */ + /* "Values of 0h-3h are ignored and should not be attempted" */ + if (tmrval < 0x04) + return -EINVAL; + if (((iTCO_wdt_private.iTCO_version == 2) && (tmrval > 0x3ff)) || + ((iTCO_wdt_private.iTCO_version == 1) && (tmrval > 0x03f))) + return -EINVAL; + + iTCO_vendor_pre_set_heartbeat(tmrval); + + /* Write new heartbeat to watchdog */ + if (iTCO_wdt_private.iTCO_version == 2) { + spin_lock(&iTCO_wdt_private.io_lock); + val16 = inw(TCOv2_TMR); + val16 &= 0xfc00; + val16 |= tmrval; + outw(val16, TCOv2_TMR); + val16 = inw(TCOv2_TMR); + spin_unlock(&iTCO_wdt_private.io_lock); + + if ((val16 & 0x3ff) != tmrval) + return -EINVAL; + } else if (iTCO_wdt_private.iTCO_version == 1) { + spin_lock(&iTCO_wdt_private.io_lock); + val8 = inb(TCOv1_TMR); + val8 &= 0xc0; + val8 |= (tmrval & 0xff); + outb(val8, TCOv1_TMR); + val8 = inb(TCOv1_TMR); + spin_unlock(&iTCO_wdt_private.io_lock); + + if ((val8 & 0x3f) != tmrval) + return -EINVAL; + } + + heartbeat = t; + return 0; +} + +static int iTCO_wdt_get_timeleft (int *time_left) +{ + unsigned int val16; + unsigned char val8; + + /* read the TCO Timer */ + if (iTCO_wdt_private.iTCO_version == 2) { + spin_lock(&iTCO_wdt_private.io_lock); + val16 = inw(TCO_RLD); + val16 &= 0x3ff; + spin_unlock(&iTCO_wdt_private.io_lock); + + *time_left = (val16 * 6) / 10; + } else if (iTCO_wdt_private.iTCO_version == 1) { + spin_lock(&iTCO_wdt_private.io_lock); + val8 = inb(TCO_RLD); + val8 &= 0x3f; + spin_unlock(&iTCO_wdt_private.io_lock); + + *time_left = (val8 * 6) / 10; + } else + return -EINVAL; + return 0; +} + +/* + * /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, +}; + +/* + * Init & exit routines + */ + +static int iTCO_wdt_init(struct pci_dev *pdev, const struct pci_device_id *ent, struct platform_device *dev) +{ + int ret; + u32 base_address; + unsigned long RCBA; + unsigned long val32; + + /* + * Find the ACPI/PM base I/O address which is the base + * for the TCO registers (TCOBASE=ACPIBASE + 0x60) + * ACPIBASE is bits [15:7] from 0x40-0x43 + */ + pci_read_config_dword(pdev, 0x40, &base_address); + base_address &= 0x0000ff80; + if (base_address == 0x00000000) { + /* Something's wrong here, ACPIBASE has to be set */ + printk(KERN_ERR PFX "failed to get TCOBASE address\n"); + pci_dev_put(pdev); + return -ENODEV; + } + iTCO_wdt_private.iTCO_version = iTCO_chipset_info[ent->driver_data].iTCO_version; + iTCO_wdt_private.ACPIBASE = base_address; + iTCO_wdt_private.pdev = pdev; + + /* Get the Memory-Mapped GCS register, we need it for the NO_REBOOT flag (TCO v2) */ + /* To get access to it you have to read RCBA from PCI Config space 0xf0 + and use it as base. GCS = RCBA + ICH6_GCS(0x3410). */ + 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); + } + + /* Check chipset's NO_REBOOT bit */ + if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { + printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); + ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ + goto out; + } + + /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ + iTCO_wdt_set_NO_REBOOT_bit(); + + /* Set the TCO_EN bit in SMI_EN register */ + if (!request_region(SMI_EN, 4, "iTCO_wdt")) { + printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n", + SMI_EN ); + ret = -EIO; + goto out; + } + val32 = inl(SMI_EN); + val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */ + outl(val32, SMI_EN); + release_region(SMI_EN, 4); + + /* The TCO I/O registers reside in a 32-byte range pointed to by the TCOBASE value */ + if (!request_region (TCOBASE, 0x20, "iTCO_wdt")) { + printk (KERN_ERR PFX "I/O address 0x%04lx already in use\n", + TCOBASE); + ret = -EIO; + goto out; + } + + printk(KERN_INFO PFX "Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n", + iTCO_chipset_info[ent->driver_data].name, + iTCO_chipset_info[ent->driver_data].iTCO_version, + TCOBASE); + + /* Clear out the (probably old) status */ + outb(0, TCO1_STS); + outb(3, TCO2_STS); + + /* Make sure the watchdog is not running */ + iTCO_wdt_stop(); + + /* 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); + printk(KERN_INFO PFX "heartbeat value must be 2"); +MODULE_DESCRIPTION("Intel TCO WatchDog Timer Driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c new file mode 100644 index 0000000..c3a60f5 --- /dev/null +++ b/drivers/watchdog/ib700wdt.c @@ -0,0 +1,408 @@ +/* + * IB700 Single Board Computer WDT driver + * + * (c) Copyright 2001 Charles Howes + * + * Based on advantechwdt.c which is based on acquirewdt.c which + * is based on wdt.c. + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * Based on acquirewdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * Added timeout module option to override default + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct platform_device *ibwdt_platform_device; +static unsigned long ibwdt_is_open; +static spinlock_t ibwdt_lock; +static char expect_close; + +/* Module information */ +#define DRV_NAME "ib700wdt" +#define PFX DRV_NAME ": " + +/* + * + * Watchdog Timer Configuration + * + * The function of the watchdog timer is to reset the system + * automatically and is defined at I/O port 0443H. To enable the + * watchdog timer and allow the system to reset, write I/O port 0443H. + * To disable the timer, write I/O port 0441H for the system to stop the + * watchdog function. The timer has a tolerance of 20% for its + * intervals. + * + * The following describes how the timer should be programmed. + * + * Enabling Watchdog: + * MOV AX,000FH (Choose the values from 0 to F) + * MOV DX,0443H + * OUT DX,AX + * + * Disabling Watchdog: + * MOV AX,000FH (Any value is fine.) + * MOV DX,0441H + * OUT DX,AX + * + * Watchdog timer control table: + * Level Value Time/sec | Level Value Time/sec + * 1 F 0 | 9 7 16 + * 2 E 2 | 10 6 18 + * 3 D 4 | 11 5 20 + * 4 C 6 | 12 4 22 + * 5 B 8 | 13 3 24 + * 6 A 10 | 14 2 26 + * 7 9 12 | 15 1 28 + * 8 8 14 | 16 0 30 + * + */ + +static int wd_times[] = { + 30, /* 0x0 */ + 28, /* 0x1 */ + 26, /* 0x2 */ + 24, /* 0x3 */ + 22, /* 0x4 */ + 20, /* 0x5 */ + 18, /* 0x6 */ + 16, /* 0x7 */ + 14, /* 0x8 */ + 12, /* 0x9 */ + 10, /* 0xA */ + 8, /* 0xB */ + 6, /* 0xC */ + 4, /* 0xD */ + 2, /* 0xE */ + 0, /* 0xF */ +}; + +#define WDT_STOP 0x441 +#define WDT_START 0x443 + +/* Default timeout */ +#define WD_TIMO 0 /* 30 seconds +/- 20%, from table */ + +static int wd_margin = WD_TIMO; + +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) ")"); + + +/* + * Watchdog Operations + */ + +static void +ibwdt_ping(void) +{ + spin_lock(&ibwdt_lock); + + /* Write a watchdog value */ + outb_p(wd_margin, WDT_START); + + spin_unlock(&ibwdt_lock); +} + +static void +ibwdt_disable(void) +{ + spin_lock(&ibwdt_lock); + outb_p(0, WDT_STOP); + spin_unlock(&ibwdt_lock); +} + +static int +ibwdt_set_heartbeat(int t) +{ + int i; + + if ((t < 0) || (t > 30)) + return -EINVAL; + + for (i = 0x0F; i > -1; i--) + if (wd_times[i] > t) + break; + wd_margin = i; + return 0; +} + +/* + * /dev/watchdog handling + */ + +static ssize_t +ibwdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + ibwdt_ping(); + } + return count; +} + +static int +ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "IB700 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: + ibwdt_ping(); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, p)) + return -EFAULT; + if (ibwdt_set_heartbeat(new_margin)) + return -EINVAL; + ibwdt_ping(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(wd_times[wd_margin], p); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + ibwdt_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + ibwdt_ping(); + retval = 0; + } + + return retval; + } + + default: + return -ENOTTY; + } + return 0; +} + +static int +ibwdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &ibwdt_is_open)) { + return -EBUSY; + } + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate */ + ibwdt_ping(); + return nonseekable_open(inode, file); +} + +static int +ibwdt_close(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + ibwdt_disable(); + } else { + printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n"); + ibwdt_ping(); + } + clear_bit(0, &ibwdt_is_open); + expect_close = 0; + return 0; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations ibwdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = ibwdt_write, + .ioctl = ibwdt_ioctl, + .open = ibwdt_open, + .release = ibwdt_close, +}; + +static struct miscdevice ibwdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ibwdt_fops, +}; + +/* + * Init & exit routines + */ + +static int __devinit ibwdt_probe(struct platform_device *dev) +{ + int res; + + spin_lock_init(&ibwdt_lock); + +#if WDT_START != WDT_STOP + if (!request_region(WDT_STOP, 1, "IB700 WDT")) { + printk (KERN_ERR PFX "STOP method I/O %X is not available.\n", WDT_STOP); + res = -EIO; + goto out_nostopreg; + } +#endif + + if (!request_region(WDT_START, 1, "IB700 WDT")) { + printk (KERN_ERR PFX "START method I/O %X is not available.\n", WDT_START); + res = -EIO; + goto out_nostartreg; + } + + res = misc_register(&ibwdt_miscdev); + if (res) { + printk (KERN_ERR PFX "failed to register misc device\n"); + goto out_nomisc; + } + return 0; + +out_nomisc: + release_region(WDT_START, 1); +out_nostartreg: +#if WDT_START != WDT_STOP + release_region(WDT_STOP, 1); +#endif +out_nostopreg: + return res; +} + +static int __devexit ibwdt_remove(struct platform_device *dev) +{ + misc_deregister(&ibwdt_miscdev); + release_region(WDT_START,1); +#if WDT_START != WDT_STOP + release_region(WDT_STOP,1); +#endif + return 0; +} + +static void ibwdt_shutdown(struct platform_device *dev) +{ + /* Turn the WDT off if we have a soft shutdown */ + ibwdt_disable(); +} + +static struct platform_driver ibwdt_driver = { + .probe = ibwdt_probe, + .remove = __devexit_p(ibwdt_remove), + .shutdown = ibwdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init ibwdt_init(void) +{ + int err; + + printk(KERN_INFO PFX "WDT driver for IB700 single board computer initialising.\n"); + + err = platform_driver_register(&ibwdt_driver); + if (err) + return err; + + ibwdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); + if (IS_ERR(ibwdt_platform_device)) { + err = PTR_ERR(ibwdt_platform_device); + goto unreg_platform_driver; + } + + return 0; + +unreg_platform_driver: + platform_driver_unregister(&ibwdt_driver); + return err; +} + +static void __exit ibwdt_exit(void) +{ + platform_device_unregister(ibwdt_platform_device); + platform_driver_unregister(&ibwdt_driver); + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); +} + +module_init(ibwdt_init); +module_exit(ibwdt_exit); + +MODULE_AUTHOR("Charles Howes "); +MODULE_DESCRIPTION("IB700 SBC watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +/* end of ib700wdt.c */ diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c new file mode 100644 index 0000000..94155f6 --- /dev/null +++ b/drivers/watchdog/ibmasr.c @@ -0,0 +1,403 @@ +/* + * IBM Automatic Server Restart driver. + * + * Copyright (c) 2005 Andrey Panin + * + * Based on driver written by Pete Reynolds. + * Copyright (c) IBM Corporation, 1998-2004. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +enum { + ASMTYPE_UNKNOWN, + ASMTYPE_TOPAZ, + ASMTYPE_JASPER, + ASMTYPE_PEARL, + ASMTYPE_JUNIPER, + ASMTYPE_SPRUCE, +}; + +#define PFX "ibmasr: " + +#define TOPAZ_ASR_REG_OFFSET 4 +#define TOPAZ_ASR_TOGGLE 0x40 +#define TOPAZ_ASR_DISABLE 0x80 + +/* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */ +#define PEARL_BASE 0xe04 +#define PEARL_WRITE 0xe06 +#define PEARL_READ 0xe07 + +#define PEARL_ASR_DISABLE_MASK 0x80 /* bit 7: disable = 1, enable = 0 */ +#define PEARL_ASR_TOGGLE_MASK 0x40 /* bit 6: 0, then 1, then 0 */ + +/* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */ +#define JASPER_ASR_REG_OFFSET 0x38 + +#define JASPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1, enable = 0 */ +#define JASPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ + +#define JUNIPER_BASE_ADDRESS 0x54b /* Base address of Juniper ASR */ +#define JUNIPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1 enable = 0 */ +#define JUNIPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */ + +#define SPRUCE_BASE_ADDRESS 0x118e /* Base address of Spruce ASR */ +#define SPRUCE_ASR_DISABLE_MASK 0x01 /* bit 1: disable = 1 enable = 0 */ +#define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */ + + +static int nowayout = WATCHDOG_NOWAYOUT; + +static unsigned long asr_is_open; +static char asr_expect_close; + +static unsigned int asr_type, asr_base, asr_length; +static unsigned int asr_read_addr, asr_write_addr; +static unsigned char asr_toggle_mask, asr_disable_mask; + +static void asr_toggle(void) +{ + unsigned char reg = inb(asr_read_addr); + + outb(reg & ~asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); + + outb(reg | asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); + + outb(reg & ~asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); +} + +static void asr_enable(void) +{ + unsigned char reg; + + if (asr_type == ASMTYPE_TOPAZ) { + /* asr_write_addr == asr_read_addr */ + reg = inb(asr_read_addr); + outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE), + asr_read_addr); + } else { + /* + * First make sure the hardware timer is reset by toggling + * ASR hardware timer line. + */ + asr_toggle(); + + reg = inb(asr_read_addr); + outb(reg & ~asr_disable_mask, asr_write_addr); + } + reg = inb(asr_read_addr); +} + +static void asr_disable(void) +{ + unsigned char reg = inb(asr_read_addr); + + if (asr_type == ASMTYPE_TOPAZ) + /* asr_write_addr == asr_read_addr */ + outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE, + asr_read_addr); + else { + outb(reg | asr_toggle_mask, asr_write_addr); + reg = inb(asr_read_addr); + + outb(reg | asr_disable_mask, asr_write_addr); + } + reg = inb(asr_read_addr); +} + +static int __init asr_get_base_address(void) +{ + unsigned char low, high; + const char *type = ""; + + asr_length = 1; + + switch (asr_type) { + case ASMTYPE_TOPAZ: + /* SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F) */ + outb(0x07, 0x2e); + outb(0x07, 0x2f); + + /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */ + outb(0x60, 0x2e); + high = inb(0x2f); + + /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */ + outb(0x61, 0x2e); + low = inb(0x2f); + + asr_base = (high << 16) | low; + asr_read_addr = asr_write_addr = + asr_base + TOPAZ_ASR_REG_OFFSET; + asr_length = 5; + + break; + + case ASMTYPE_JASPER: + type = "Jaspers "; + + /* FIXME: need to use pci_config_lock here, but it's not exported */ + +/* spin_lock_irqsave(&pci_config_lock, flags);*/ + + /* Select the SuperIO chip in the PCI I/O port register */ + outl(0x8000f858, 0xcf8); + + /* + * Read the base address for the SuperIO chip. + * Only the lower 16 bits are valid, but the address is word + * aligned so the last bit must be masked off. + */ + asr_base = inl(0xcfc) & 0xfffe; + +/* spin_unlock_irqrestore(&pci_config_lock, flags);*/ + + asr_read_addr = asr_write_addr = + asr_base + JASPER_ASR_REG_OFFSET; + asr_toggle_mask = JASPER_ASR_TOGGLE_MASK; + asr_disable_mask = JASPER_ASR_DISABLE_MASK; + asr_length = JASPER_ASR_REG_OFFSET + 1; + + break; + + case ASMTYPE_PEARL: + type = "Pearls "; + asr_base = PEARL_BASE; + asr_read_addr = PEARL_READ; + asr_write_addr = PEARL_WRITE; + asr_toggle_mask = PEARL_ASR_TOGGLE_MASK; + asr_disable_mask = PEARL_ASR_DISABLE_MASK; + asr_length = 4; + break; + + case ASMTYPE_JUNIPER: + type = "Junipers "; + asr_base = JUNIPER_BASE_ADDRESS; + asr_read_addr = asr_write_addr = asr_base; + asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK; + asr_disable_mask = JUNIPER_ASR_DISABLE_MASK; + break; + + case ASMTYPE_SPRUCE: + type = "Spruce's "; + asr_base = SPRUCE_BASE_ADDRESS; + asr_read_addr = asr_write_addr = asr_base; + asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK; + asr_disable_mask = SPRUCE_ASR_DISABLE_MASK; + break; + } + + if (!request_region(asr_base, asr_length, "ibmasr")) { + printk(KERN_ERR PFX "address %#x already in use\n", + asr_base); + return -EBUSY; + } + + printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base); + + return 0; +} + + +static ssize_t asr_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + asr_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + asr_expect_close = 42; + } + } + asr_toggle(); + } + return count; +} + +static int asr_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static const struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "IBM ASR" + }; + void __user *argp = (void __user *)arg; + int __user *p = argp; + int heartbeat; + + 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: + asr_toggle(); + return 0; + + /* + * The hardware has a fixed timeout value, so no WDIOC_SETTIMEOUT + * and WDIOC_GETTIMEOUT always returns 256. + */ + case WDIOC_GETTIMEOUT: + heartbeat = 256; + return put_user(heartbeat, p); + + case WDIOC_SETOPTIONS: { + int new_options, retval = -EINVAL; + + if (get_user(new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + asr_disable(); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + asr_enable(); + asr_toggle(); + retval = 0; + } + + return retval; + } + } + + return -ENOTTY; +} + +static int asr_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(0, &asr_is_open)) + return -EBUSY; + + asr_toggle(); + asr_enable(); + + return nonseekable_open(inode, file); +} + +static int asr_release(struct inode *inode, struct file *file) +{ + if (asr_expect_close == 42) + asr_disable(); + else { + printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); + asr_toggle(); + } + clear_bit(0, &asr_is_open); + asr_expect_close = 0; + return 0; +} + +static const struct file_operations asr_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = asr_write, + .ioctl = asr_ioctl, + .open = asr_open, + .release = asr_release, +}; + +static struct miscdevice asr_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &asr_fops, +}; + + +struct ibmasr_id { + const char *desc; + int type; +}; + +static struct ibmasr_id __initdata ibmasr_id_table[] = { + { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ }, + { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL }, + { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER }, + { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER }, + { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE }, + { NULL } +}; + +static int __init ibmasr_init(void) +{ + struct ibmasr_id *id; + int rc; + + for (id = ibmasr_id_table; id->desc; id++) { + if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) { + asr_type = id->type; + break; + } + } + + if (!asr_type) + return -ENODEV; + + rc = asr_get_base_address(); + if (rc) + return rc; + + rc = misc_register(&asr_miscdev); + if (rc < 0) { + release_region(asr_base, asr_length); + printk(KERN_ERR PFX "failed to register misc device\n"); + return rc; + } + + return 0; +} + +static void __exit ibmasr_exit(void) +{ + if (!nowayout) + asr_disable(); + + misc_deregister(&asr_miscdev); + + release_region(asr_base, asr_length); +} + +module_init(ibmasr_init); +module_exit(ibmasr_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_DESCRIPTION("IBM Automatic Server Restart driver"); +MODULE_AUTHOR("Andrey Panin"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c new file mode 100644 index 0000000..788245b --- /dev/null +++ b/drivers/watchdog/indydog.c @@ -0,0 +1,215 @@ +/* + * IndyDog 0.3 A Hardware Watchdog Device for SGI IP22 + * + * (c) Copyright 2002 Guido Guenther , All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * based on softdog.c by Alan Cox + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "indydog: " +static int indydog_alive; + +#define WATCHDOG_TIMEOUT 30 /* 30 sec default 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 indydog_start(void) +{ + u32 mc_ctrl0 = sgimc->cpuctrl0; + + mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG; + sgimc->cpuctrl0 = mc_ctrl0; +} + +static void indydog_stop(void) +{ + u32 mc_ctrl0 = sgimc->cpuctrl0; + + mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; + sgimc->cpuctrl0 = mc_ctrl0; + + printk(KERN_INFO PFX "Stopped watchdog timer.\n"); +} + +static void indydog_ping(void) +{ + sgimc->watchdogt = 0; +} + +/* + * Allow only one person to hold it open + */ +static int indydog_open(struct inode *inode, struct file *file) +{ + if (indydog_alive) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate timer */ + indydog_start(); + indydog_ping(); + + indydog_alive = 1; + printk(KERN_INFO "Started watchdog timer.\n"); + + return nonseekable_open(inode, file); +} + +static int indydog_release(struct inode *inode, struct file *file) +{ + /* Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT */ + if (!nowayout) + indydog_stop(); /* Turn the WDT off */ + + indydog_alive = 0; + + return 0; +} + +static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Refresh the timer. */ + if (len) { + indydog_ping(); + } + return len; +} + +static int indydog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int options, retval = -EINVAL; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "Hardware Watchdog for SGI IP22", + }; + + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, + &ident, sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0,(int *)arg); + case WDIOC_KEEPALIVE: + indydog_ping(); + return 0; + case WDIOC_GETTIMEOUT: + return put_user(WATCHDOG_TIMEOUT,(int *)arg); + case WDIOC_SETOPTIONS: + { + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + indydog_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + indydog_start(); + retval = 0; + } + + return retval; + } + } +} + +static int indydog_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + indydog_stop(); /* Turn the WDT off */ + + return NOTIFY_DONE; +} + +static const struct file_operations indydog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = indydog_write, + .ioctl = indydog_ioctl, + .open = indydog_open, + .release = indydog_release, +}; + +static struct miscdevice indydog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &indydog_fops, +}; + +static struct notifier_block indydog_notifier = { + .notifier_call = indydog_notify_sys, +}; + +static char banner[] __initdata = + KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n"; + +static int __init watchdog_init(void) +{ + int ret; + + ret = register_reboot_notifier(&indydog_notifier); + if (ret) { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + ret); + return ret; + } + + ret = misc_register(&indydog_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&indydog_notifier); + return ret; + } + + printk(banner); + + return 0; +} + +static void __exit watchdog_exit(void) +{ + misc_deregister(&indydog_miscdev); + unregister_reboot_notifier(&indydog_notifier); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Guido Guenther "); +MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c new file mode 100644 index 0000000..bbbd91a --- /dev/null +++ b/drivers/watchdog/iop_wdt.c @@ -0,0 +1,262 @@ +/* + * drivers/char/watchdog/iop_wdt.c + * + * WDT driver for Intel I/O Processors + * Copyright (C) 2005, Intel Corporation. + * + * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Curt E Bruns + * Peter Milne + * Dan Williams + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int nowayout = WATCHDOG_NOWAYOUT; +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 +#define WDT_ENABLED 2 + +static unsigned long iop_watchdog_timeout(void) +{ + return (0xffffffffUL / get_iop_tick_rate()); +} + +/** + * wdt_supports_disable - determine if we are accessing a iop13xx watchdog + * or iop3xx by whether it has a disable command + */ +static int wdt_supports_disable(void) +{ + int can_disable; + + if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) + can_disable = 1; + else + can_disable = 0; + + return can_disable; +} + +static void wdt_enable(void) +{ + /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF + * Takes approx. 10.7s to timeout + */ + write_wdtcr(IOP_WDTCR_EN_ARM); + write_wdtcr(IOP_WDTCR_EN); +} + +/* returns 0 if the timer was successfully disabled */ +static int wdt_disable(void) +{ + /* Stop Counting */ + if (wdt_supports_disable()) { + write_wdtcr(IOP_WDTCR_DIS_ARM); + write_wdtcr(IOP_WDTCR_DIS); + clear_bit(WDT_ENABLED, &wdt_status); + printk(KERN_INFO "WATCHDOG: Disabled\n"); + return 0; + } else + return 1; +} + +static int iop_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + set_bit(WDT_ENABLED, &wdt_status); + + return nonseekable_open(inode, file); +} + +static ssize_t +iop_wdt_write(struct file *file, const char *data, size_t len, + loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "iop watchdog", +}; + +static int +iop_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int options; + int ret = -ENOTTY; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user + ((struct watchdog_info *)arg, &ident, sizeof ident)) + ret = -EFAULT; + else + ret = 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int *)arg); + break; + + case WDIOC_GETTIMEOUT: + ret = put_user(iop_watchdog_timeout(), (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + + case WDIOC_SETOPTIONS: + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + if (!nowayout) { + if (wdt_disable() == 0) { + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + ret = 0; + } else + ret = -ENXIO; + } else + ret = 0; + } + + if (options & WDIOS_ENABLECARD) { + wdt_enable(); + ret = 0; + } + break; + } + + return ret; +} + +static int iop_wdt_release(struct inode *inode, struct file *file) +{ + int state = 1; + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + if (test_bit(WDT_ENABLED, &wdt_status)) + state = wdt_disable(); + + /* if the timer is not disbaled reload and notify that we are still + * going down + */ + if (state != 0) { + wdt_enable(); + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "reset in %lu seconds\n", iop_watchdog_timeout()); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static const struct file_operations iop_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = iop_wdt_write, + .ioctl = iop_wdt_ioctl, + .open = iop_wdt_open, + .release = iop_wdt_release, +}; + +static struct miscdevice iop_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &iop_wdt_fops, +}; + +static int __init iop_wdt_init(void) +{ + int ret; + + ret = misc_register(&iop_wdt_miscdev); + if (ret == 0) + printk("iop watchdog timer: timeout %lu sec\n", + iop_watchdog_timeout()); + + /* check if the reset was caused by the watchdog timer */ + boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; + + /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset + * NOTE: An IB Reset will Reset both cores in the IOP342 + */ + write_wdtsr(IOP13XX_WDTCR_IB_RESET); + + return ret; +} + +static void __exit iop_wdt_exit(void) +{ + misc_deregister(&iop_wdt_miscdev); +} + +module_init(iop_wdt_init); +module_exit(iop_wdt_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_AUTHOR("Curt E Bruns "); +MODULE_DESCRIPTION("iop watchdog timer driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/ixp2000_wdt.c b/drivers/watchdog/ixp2000_wdt.c new file mode 100644 index 0000000..dc7548d --- /dev/null +++ b/drivers/watchdog/ixp2000_wdt.c @@ -0,0 +1,219 @@ +/* + * drivers/char/watchdog/ixp2000_wdt.c + * + * Watchdog driver for Intel IXP2000 network processors + * + * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek. + * The original version carries these notices: + * + * Author: Deepak Saxena + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int nowayout = WATCHDOG_NOWAYOUT; +static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */ +static unsigned long wdt_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static unsigned long wdt_tick_rate; + +static void +wdt_enable(void) +{ + ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE); + ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE); + ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate); + ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE); +} + +static void +wdt_disable(void) +{ + ixp2000_reg_write(IXP2000_T4_CTL, 0); +} + +static void +wdt_keepalive(void) +{ + ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate); +} + +static int +ixp2000_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +static ssize_t +ixp2000_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_keepalive(); + } + + return len; +} + + +static struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "IXP2000 Watchdog", +}; + +static int +ixp2000_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > 60) { + ret = -EINVAL; + break; + } + + heartbeat = time; + wdt_keepalive(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + } + + return ret; +} + +static int +ixp2000_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { + wdt_disable(); + } else { + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "timer will not stop\n"); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + + +static const struct file_operations ixp2000_wdt_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = ixp2000_wdt_write, + .ioctl = ixp2000_wdt_ioctl, + .open = ixp2000_wdt_open, + .release = ixp2000_wdt_release, +}; + +static struct miscdevice ixp2000_wdt_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ixp2000_wdt_fops, +}; + +static int __init ixp2000_wdt_init(void) +{ + if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { + printk(KERN_INFO "Unable to use IXP2000 watchdog due to IXP2800 erratum #25.\n"); + return -EIO; + } + + wdt_tick_rate = (*IXP2000_T1_CLD * HZ) / 256; + + return misc_register(&ixp2000_wdt_miscdev); +} + +static void __exit ixp2000_wdt_exit(void) +{ + misc_deregister(&ixp2000_wdt_miscdev); +} + +module_init(ixp2000_wdt_init); +module_exit(ixp2000_wdt_exit); + +MODULE_AUTHOR("Deepak Saxena "); +MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c new file mode 100644 index 0000000..5864bb8 --- /dev/null +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -0,0 +1,225 @@ +/* + * drivers/char/watchdog/ixp4xx_wdt.c + * + * Watchdog driver for Intel IXP4xx network processors + * + * Author: Deepak Saxena + * + * Copyright 2004 (c) MontaVista, Software, Inc. + * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = 60; /* (secs) Default is 1 minute */ +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL) + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static void +wdt_enable(void) +{ + *IXP4XX_OSWK = IXP4XX_WDT_KEY; + *IXP4XX_OSWE = 0; + *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat; + *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE; + *IXP4XX_OSWK = 0; +} + +static void +wdt_disable(void) +{ + *IXP4XX_OSWK = IXP4XX_WDT_KEY; + *IXP4XX_OSWE = 0; + *IXP4XX_OSWK = 0; +} + +static int +ixp4xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +static ssize_t +ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "IXP4xx Watchdog", +}; + + +static int +ixp4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int *)arg); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > 60) { + ret = -EINVAL; + break; + } + + heartbeat = time; + wdt_enable(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + } + return ret; +} + +static int +ixp4xx_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { + wdt_disable(); + } else { + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "timer will not stop\n"); + } + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + + +static const struct file_operations ixp4xx_wdt_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = ixp4xx_wdt_write, + .ioctl = ixp4xx_wdt_ioctl, + .open = ixp4xx_wdt_open, + .release = ixp4xx_wdt_release, +}; + +static struct miscdevice ixp4xx_wdt_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ixp4xx_wdt_fops, +}; + +static int __init ixp4xx_wdt_init(void) +{ + int ret; + unsigned long processor_id; + + asm("mrc p15, 0, %0, cr0, cr0, 0;" : "=r"(processor_id) :); + if (!(processor_id & 0xf) && !cpu_is_ixp46x()) { + printk("IXP4XXX Watchdog: Rev. A0 IXP42x CPU detected - " + "watchdog disabled\n"); + + return -ENODEV; + } + + ret = misc_register(&ixp4xx_wdt_miscdev); + if (ret == 0) + printk("IXP4xx Watchdog Timer: heartbeat %d sec\n", heartbeat); + + boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ? + WDIOF_CARDRESET : 0; + + return ret; +} + +static void __exit ixp4xx_wdt_exit(void) +{ + misc_deregister(&ixp4xx_wdt_miscdev); +} + + +module_init(ixp4xx_wdt_init); +module_exit(ixp4xx_wdt_exit); + +MODULE_AUTHOR("Deepak Saxena "); +MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c new file mode 100644 index 0000000..7150fb9 --- /dev/null +++ b/drivers/watchdog/ks8695_wdt.c @@ -0,0 +1,308 @@ +/* + * Watchdog driver for Kendin/Micrel KS8695. + * + * (C) 2007 Andrew Victor + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define WDT_DEFAULT_TIME 5 /* seconds */ +#define WDT_MAX_TIME 171 /* seconds */ + +static int wdt_time = WDT_DEFAULT_TIME; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_time, int, 0); +MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +#endif + + +static unsigned long ks8695wdt_busy; + +/* ......................................................................... */ + +/* + * Disable the watchdog. + */ +static void inline ks8695_wdt_stop(void) +{ + unsigned long tmcon; + + /* disable timer0 */ + tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); + __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); +} + +/* + * Enable and reset the watchdog. + */ +static void inline ks8695_wdt_start(void) +{ + unsigned long tmcon; + unsigned long tval = wdt_time * CLOCK_TICK_RATE; + + /* disable timer0 */ + tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); + __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); + + /* program timer0 */ + __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC); + + /* re-enable timer0 */ + tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); + __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); +} + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static void inline ks8695_wdt_reload(void) +{ + unsigned long tmcon; + + /* disable, then re-enable timer0 */ + tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); + __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); + __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); +} + +/* + * Change the watchdog time interval. + */ +static int ks8695_wdt_settimeout(int new_time) +{ + /* + * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz + * + * Since WDV is a 16-bit counter, the maximum period is + * 65536 / 0.256 = 256 seconds. + */ + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; + + /* Set new watchdog time. It will be used when ks8695_wdt_start() is called. */ + wdt_time = new_time; + return 0; +} + +/* ......................................................................... */ + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int ks8695_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &ks8695wdt_busy)) + return -EBUSY; + + ks8695_wdt_start(); + return nonseekable_open(inode, file); +} + +/* + * Close the watchdog device. + * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also + * disabled. + */ +static int ks8695_wdt_close(struct inode *inode, struct file *file) +{ + if (!nowayout) + ks8695_wdt_stop(); /* Disable the watchdog when file is closed */ + + clear_bit(0, &ks8695wdt_busy); + return 0; +} + +static struct watchdog_info ks8695_wdt_info = { + .identity = "ks8695 watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +/* + * Handle commands from user-space. + */ +static int ks8695_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_value; + + switch(cmd) { + case WDIOC_KEEPALIVE: + ks8695_wdt_reload(); /* pat the watchdog */ + return 0; + + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ks8695_wdt_info, sizeof(ks8695_wdt_info)) ? -EFAULT : 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + if (ks8695_wdt_settimeout(new_value)) + return -EINVAL; + + /* Enable new time value */ + ks8695_wdt_start(); + + /* Return current value */ + return put_user(wdt_time, p); + + case WDIOC_GETTIMEOUT: + return put_user(wdt_time, p); + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + + if (new_value & WDIOS_DISABLECARD) + ks8695_wdt_stop(); + if (new_value & WDIOS_ENABLECARD) + ks8695_wdt_start(); + return 0; + + default: + return -ENOTTY; + } +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t ks8695_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + ks8695_wdt_reload(); /* pat the watchdog */ + return len; +} + +/* ......................................................................... */ + +static const struct file_operations ks8695wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = ks8695_wdt_ioctl, + .open = ks8695_wdt_open, + .release = ks8695_wdt_close, + .write = ks8695_wdt_write, +}; + +static struct miscdevice ks8695wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ks8695wdt_fops, +}; + +static int __init ks8695wdt_probe(struct platform_device *pdev) +{ + int res; + + if (ks8695wdt_miscdev.parent) + return -EBUSY; + ks8695wdt_miscdev.parent = &pdev->dev; + + res = misc_register(&ks8695wdt_miscdev); + if (res) + return res; + + printk("KS8695 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : ""); + return 0; +} + +static int __exit ks8695wdt_remove(struct platform_device *pdev) +{ + int res; + + res = misc_deregister(&ks8695wdt_miscdev); + if (!res) + ks8695wdt_miscdev.parent = NULL; + + return res; +} + +static void ks8695wdt_shutdown(struct platform_device *pdev) +{ + ks8695_wdt_stop(); +} + +#ifdef CONFIG_PM + +static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message) +{ + ks8695_wdt_stop(); + return 0; +} + +static int ks8695wdt_resume(struct platform_device *pdev) +{ + if (ks8695wdt_busy) + ks8695_wdt_start(); + return 0; +} + +#else +#define ks8695wdt_suspend NULL +#define ks8695wdt_resume NULL +#endif + +static struct platform_driver ks8695wdt_driver = { + .probe = ks8695wdt_probe, + .remove = __exit_p(ks8695wdt_remove), + .shutdown = ks8695wdt_shutdown, + .suspend = ks8695wdt_suspend, + .resume = ks8695wdt_resume, + .driver = { + .name = "ks8695_wdt", + .owner = THIS_MODULE, + }, +}; + +static int __init ks8695_wdt_init(void) +{ + /* Check that the heartbeat value is within range; if not reset to the default */ + if (ks8695_wdt_settimeout(wdt_time)) { + ks8695_wdt_settimeout(WDT_DEFAULT_TIME); + pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i, using %d\n", wdt_time, WDT_MAX_TIME); + } + + return platform_driver_register(&ks8695wdt_driver); +} + +static void __exit ks8695_wdt_exit(void) +{ + platform_driver_unregister(&ks8695wdt_driver); +} + +module_init(ks8695_wdt_init); +module_exit(ks8695_wdt_exit); + +MODULE_AUTHOR("Andrew Victor"); +MODULE_DESCRIPTION("Watchdog driver for KS8695"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c new file mode 100644 index 0000000..6d35bb1 --- /dev/null +++ b/drivers/watchdog/machzwd.c @@ -0,0 +1,489 @@ +/* + * MachZ ZF-Logic Watchdog Timer driver for Linux + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The author does 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. + * + * Author: Fernando Fuganti + * + * Based on sbc60xxwdt.c by Jakob Oestergaard + * + * + * We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the + * following periods: + * wd#1 - 2 seconds; + * wd#2 - 7.2 ms; + * After the expiration of wd#1, it can generate a NMI, SCI, SMI, or + * a system RESET and it starts wd#2 that unconditionaly will RESET + * the system when the counter reaches zero. + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* ports */ +#define ZF_IOBASE 0x218 +#define INDEX 0x218 +#define DATA_B 0x219 +#define DATA_W 0x21A +#define DATA_D 0x21A + +/* indexes */ /* size */ +#define ZFL_VERSION 0x02 /* 16 */ +#define CONTROL 0x10 /* 16 */ +#define STATUS 0x12 /* 8 */ +#define COUNTER_1 0x0C /* 16 */ +#define COUNTER_2 0x0E /* 8 */ +#define PULSE_LEN 0x0F /* 8 */ + +/* controls */ +#define ENABLE_WD1 0x0001 +#define ENABLE_WD2 0x0002 +#define RESET_WD1 0x0010 +#define RESET_WD2 0x0020 +#define GEN_SCI 0x0100 +#define GEN_NMI 0x0200 +#define GEN_SMI 0x0400 +#define GEN_RESET 0x0800 + + +/* utilities */ + +#define WD1 0 +#define WD2 1 + +#define zf_writew(port, data) { outb(port, INDEX); outw(data, DATA_W); } +#define zf_writeb(port, data) { outb(port, INDEX); outb(data, DATA_B); } +#define zf_get_ZFL_version() zf_readw(ZFL_VERSION) + + +static unsigned short zf_readw(unsigned char port) +{ + outb(port, INDEX); + return inw(DATA_W); +} + + +MODULE_AUTHOR("Fernando Fuganti "); +MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +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) ")"); + +#define PFX "machzwd" + +static struct watchdog_info zf_info = { + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "ZF-Logic watchdog", +}; + + +/* + * action refers to action taken when watchdog resets + * 0 = GEN_RESET + * 1 = GEN_SMI + * 2 = GEN_NMI + * 3 = GEN_SCI + * defaults to GEN_RESET (0) + */ +static int action = 0; +module_param(action, int, 0); +MODULE_PARM_DESC(action, "after watchdog resets, generate: 0 = RESET(*) 1 = SMI 2 = NMI 3 = SCI"); + +static void zf_ping(unsigned long data); + +static int zf_action = GEN_RESET; +static unsigned long zf_is_open; +static char zf_expect_close; +static spinlock_t zf_lock; +static spinlock_t zf_port_lock; +static DEFINE_TIMER(zf_timer, zf_ping, 0, 0); +static unsigned long next_heartbeat = 0; + + +/* timeout for user land heart beat (10 seconds) */ +#define ZF_USER_TIMEO (HZ*10) + +/* timeout for hardware watchdog (~500ms) */ +#define ZF_HW_TIMEO (HZ/2) + +/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */ +#define ZF_CTIMEOUT 0xffff + +#ifndef ZF_DEBUG +# define dprintk(format, args...) +#else +# define dprintk(format, args...) printk(KERN_DEBUG PFX ":%s:%d: " format, __FUNCTION__, __LINE__ , ## args) +#endif + + +static inline void zf_set_status(unsigned char new) +{ + zf_writeb(STATUS, new); +} + + +/* CONTROL register functions */ + +static inline unsigned short zf_get_control(void) +{ + return zf_readw(CONTROL); +} + +static inline void zf_set_control(unsigned short new) +{ + zf_writew(CONTROL, new); +} + + +/* WD#? counter functions */ +/* + * Just set counter value + */ + +static inline void zf_set_timer(unsigned short new, unsigned char n) +{ + switch(n){ + case WD1: + zf_writew(COUNTER_1, new); + case WD2: + zf_writeb(COUNTER_2, new > 0xff ? 0xff : new); + default: + return; + } +} + +/* + * stop hardware timer + */ +static void zf_timer_off(void) +{ + unsigned int ctrl_reg = 0; + unsigned long flags; + + /* stop internal ping */ + del_timer_sync(&zf_timer); + + spin_lock_irqsave(&zf_port_lock, flags); + /* stop watchdog timer */ + ctrl_reg = zf_get_control(); + ctrl_reg |= (ENABLE_WD1|ENABLE_WD2); /* disable wd1 and wd2 */ + ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2); + zf_set_control(ctrl_reg); + spin_unlock_irqrestore(&zf_port_lock, flags); + + printk(KERN_INFO PFX ": Watchdog timer is now disabled\n"); +} + + +/* + * start hardware timer + */ +static void zf_timer_on(void) +{ + unsigned int ctrl_reg = 0; + unsigned long flags; + + spin_lock_irqsave(&zf_port_lock, flags); + + zf_writeb(PULSE_LEN, 0xff); + + zf_set_timer(ZF_CTIMEOUT, WD1); + + /* user land ping */ + next_heartbeat = jiffies + ZF_USER_TIMEO; + + /* start the timer for internal ping */ + mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); + + /* start watchdog timer */ + ctrl_reg = zf_get_control(); + ctrl_reg |= (ENABLE_WD1|zf_action); + zf_set_control(ctrl_reg); + spin_unlock_irqrestore(&zf_port_lock, flags); + + printk(KERN_INFO PFX ": Watchdog timer is now enabled\n"); +} + + +static void zf_ping(unsigned long data) +{ + unsigned int ctrl_reg = 0; + unsigned long flags; + + zf_writeb(COUNTER_2, 0xff); + + if(time_before(jiffies, next_heartbeat)){ + + dprintk("time_before: %ld\n", next_heartbeat - jiffies); + + /* + * reset event is activated by transition from 0 to 1 on + * RESET_WD1 bit and we assume that it is already zero... + */ + + spin_lock_irqsave(&zf_port_lock, flags); + ctrl_reg = zf_get_control(); + ctrl_reg |= RESET_WD1; + zf_set_control(ctrl_reg); + + /* ...and nothing changes until here */ + ctrl_reg &= ~(RESET_WD1); + zf_set_control(ctrl_reg); + spin_unlock_irqrestore(&zf_port_lock, flags); + + mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO); + }else{ + printk(KERN_CRIT PFX ": I will reset your machine\n"); + } +} + +static ssize_t zf_write(struct file *file, const char __user *buf, size_t count, + loff_t *ppos) +{ + /* See if we got the magic character */ + if(count){ + + /* + * no need to check for close confirmation + * no way to disable watchdog ;) + */ + if (!nowayout) { + size_t ofs; + + /* + * note: just in case someone wrote the magic character + * five months ago... + */ + zf_expect_close = 0; + + /* now scan */ + for (ofs = 0; ofs != count; ofs++){ + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if (c == 'V'){ + zf_expect_close = 42; + dprintk("zf_expect_close = 42\n"); + } + } + } + + /* + * Well, anyhow someone wrote to us, + * we should return that favour + */ + next_heartbeat = jiffies + ZF_USER_TIMEO; + dprintk("user ping at %ld\n", jiffies); + + } + + return count; +} + +static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &zf_info, sizeof(zf_info))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + zf_ping(0); + break; + + default: + return -ENOTTY; + } + + return 0; +} + +static int zf_open(struct inode *inode, struct file *file) +{ + spin_lock(&zf_lock); + if(test_and_set_bit(0, &zf_is_open)) { + spin_unlock(&zf_lock); + return -EBUSY; + } + + if (nowayout) + __module_get(THIS_MODULE); + + spin_unlock(&zf_lock); + + zf_timer_on(); + + return nonseekable_open(inode, file); +} + +static int zf_close(struct inode *inode, struct file *file) +{ + if(zf_expect_close == 42){ + zf_timer_off(); + } else { + del_timer(&zf_timer); + printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n"); + } + + spin_lock(&zf_lock); + clear_bit(0, &zf_is_open); + spin_unlock(&zf_lock); + + zf_expect_close = 0; + + return 0; +} + +/* + * Notifier for system down + */ + +static int zf_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code == SYS_DOWN || code == SYS_HALT){ + zf_timer_off(); + } + + return NOTIFY_DONE; +} + + + + +static const struct file_operations zf_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = zf_write, + .ioctl = zf_ioctl, + .open = zf_open, + .release = zf_close, +}; + +static struct miscdevice zf_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &zf_fops, +}; + + +/* + * The device needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ +static struct notifier_block zf_notifier = { + .notifier_call = zf_notify_sys, +}; + +static void __init zf_show_action(int act) +{ + char *str[] = { "RESET", "SMI", "NMI", "SCI" }; + + printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]); +} + +static int __init zf_init(void) +{ + int ret; + + printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n"); + + ret = zf_get_ZFL_version(); + if ((!ret) || (ret == 0xffff)) { + printk(KERN_WARNING PFX ": no ZF-Logic found\n"); + return -ENODEV; + } + + if((action <= 3) && (action >= 0)){ + zf_action = zf_action>>action; + } else + action = 0; + + zf_show_action(action); + + spin_lock_init(&zf_lock); + spin_lock_init(&zf_port_lock); + + if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){ + printk(KERN_ERR "cannot reserve I/O ports at %d\n", + ZF_IOBASE); + ret = -EBUSY; + goto no_region; + } + + ret = register_reboot_notifier(&zf_notifier); + if(ret){ + printk(KERN_ERR "can't register reboot notifier (err=%d)\n", + ret); + goto no_reboot; + } + + ret = misc_register(&zf_miscdev); + if (ret){ + printk(KERN_ERR "can't misc_register on minor=%d\n", + WATCHDOG_MINOR); + goto no_misc; + } + + zf_set_status(0); + zf_set_control(0); + + return 0; + +no_misc: + unregister_reboot_notifier(&zf_notifier); +no_reboot: + release_region(ZF_IOBASE, 3); +no_region: + return ret; +} + + +static void __exit zf_exit(void) +{ + zf_timer_off(); + + misc_deregister(&zf_miscdev); + unregister_reboot_notifier(&zf_notifier); + release_region(ZF_IOBASE, 3); +} + +module_init(zf_init); +module_exit(zf_exit); diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c new file mode 100644 index 0000000..1adf1d5 --- /dev/null +++ b/drivers/watchdog/mixcomwd.c @@ -0,0 +1,330 @@ +/* + * MixCom Watchdog: A Simple Hardware Watchdog Device + * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis + * + * Author: Gergely Madarasz + * + * Copyright (c) 1999 ITConsult-Pro Co. + * + * 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. + * + * Version 0.1 (99/04/15): + * - first version + * + * Version 0.2 (99/06/16): + * - added kernel timer watchdog ping after close + * since the hardware does not support watchdog shutdown + * + * Version 0.3 (99/06/21): + * - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls + * + * Version 0.3.1 (99/06/22): + * - allow module removal while internal timer is active, + * print warning about probable reset + * + * Version 0.4 (99/11/15): + * - support for one more type board + * + * Version 0.5 (2001/12/14) Matt Domsch + * - added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * + * Version 0.6 (2002/04/12): Rob Radez + * - make mixcomwd_opened unsigned, + * removed lock_kernel/unlock_kernel from mixcomwd_release, + * modified ioctl a bit to conform to API + * + */ + +#define VERSION "0.6" +#define WATCHDOG_NAME "mixcomwd" +#define PFX WATCHDOG_NAME ": " + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * We have two types of cards that can be probed: + * 1) The Mixcom cards: these cards can be found at addresses + * 0x180, 0x280, 0x380 with an additional offset of 0xc10. + * (Or 0xd90, 0xe90, 0xf90). + * 2) The FlashCOM cards: these cards can be set up at + * 0x300 -> 0x378, in 0x8 jumps with an offset of 0x04. + * (Or 0x304 -> 0x37c in 0x8 jumps). + * Each card has it's own ID. + */ +#define MIXCOM_ID 0x11 +#define FLASHCOM_ID 0x18 +static struct { + int ioport; + int id; +} mixcomwd_io_info[] __devinitdata = { + /* The Mixcom cards */ + {0x0d90, MIXCOM_ID}, + {0x0e90, MIXCOM_ID}, + {0x0f90, MIXCOM_ID}, + /* The FlashCOM cards */ + {0x0304, FLASHCOM_ID}, + {0x030c, FLASHCOM_ID}, + {0x0314, FLASHCOM_ID}, + {0x031c, FLASHCOM_ID}, + {0x0324, FLASHCOM_ID}, + {0x032c, FLASHCOM_ID}, + {0x0334, FLASHCOM_ID}, + {0x033c, FLASHCOM_ID}, + {0x0344, FLASHCOM_ID}, + {0x034c, FLASHCOM_ID}, + {0x0354, FLASHCOM_ID}, + {0x035c, FLASHCOM_ID}, + {0x0364, FLASHCOM_ID}, + {0x036c, FLASHCOM_ID}, + {0x0374, FLASHCOM_ID}, + {0x037c, FLASHCOM_ID}, + /* The end of the list */ + {0x0000, 0}, +}; + +static void mixcomwd_timerfun(unsigned long d); + +static unsigned long mixcomwd_opened; /* long req'd for setbit --RR */ + +static int watchdog_port; +static int mixcomwd_timer_alive; +static DEFINE_TIMER(mixcomwd_timer, mixcomwd_timerfun, 0, 0); +static char expect_close; + +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 mixcomwd_ping(void) +{ + outb_p(55,watchdog_port); + return; +} + +static void mixcomwd_timerfun(unsigned long d) +{ + mixcomwd_ping(); + + mod_timer(&mixcomwd_timer, jiffies + 5 * HZ); +} + +/* + * Allow only one person to hold it open + */ + +static int mixcomwd_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(0,&mixcomwd_opened)) { + return -EBUSY; + } + mixcomwd_ping(); + + if (nowayout) { + /* + * fops_get() code via open() has already done + * a try_module_get() so it is safe to do the + * __module_get(). + */ + __module_get(THIS_MODULE); + } else { + if(mixcomwd_timer_alive) { + del_timer(&mixcomwd_timer); + mixcomwd_timer_alive=0; + } + } + return nonseekable_open(inode, file); +} + +static int mixcomwd_release(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + if(mixcomwd_timer_alive) { + printk(KERN_ERR PFX "release called while internal timer alive"); + return -EBUSY; + } + mixcomwd_timer_alive=1; + mod_timer(&mixcomwd_timer, jiffies + 5 * HZ); + } else { + printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n"); + } + + clear_bit(0,&mixcomwd_opened); + expect_close=0; + return 0; +} + + +static ssize_t mixcomwd_write(struct file *file, const char __user *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 = 42; + } + } + mixcomwd_ping(); + } + return len; +} + +static int mixcomwd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int status; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "MixCOM watchdog", + }; + + switch(cmd) + { + case WDIOC_GETSTATUS: + status=mixcomwd_opened; + if (!nowayout) { + status|=mixcomwd_timer_alive; + } + if (copy_to_user(p, &status, sizeof(int))) { + return -EFAULT; + } + break; + case WDIOC_GETBOOTSTATUS: + if (copy_to_user(p, &status, sizeof(int))) { + return -EFAULT; + } + break; + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof(ident))) { + return -EFAULT; + } + break; + case WDIOC_KEEPALIVE: + mixcomwd_ping(); + break; + default: + return -ENOTTY; + } + return 0; +} + +static const struct file_operations mixcomwd_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mixcomwd_write, + .ioctl = mixcomwd_ioctl, + .open = mixcomwd_open, + .release = mixcomwd_release, +}; + +static struct miscdevice mixcomwd_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mixcomwd_fops, +}; + +static int __init checkcard(int port, int card_id) +{ + int id; + + if (!request_region(port, 1, "MixCOM watchdog")) { + return 0; + } + + id=inb_p(port); + if (card_id==MIXCOM_ID) + id &= 0x3f; + + if (id!=card_id) { + release_region(port, 1); + return 0; + } + return 1; +} + +static int __init mixcomwd_init(void) +{ + int i; + int ret; + int found=0; + + for (i = 0; !found && mixcomwd_io_info[i].ioport != 0; i++) { + if (checkcard(mixcomwd_io_info[i].ioport, + mixcomwd_io_info[i].id)) { + found = 1; + watchdog_port = mixcomwd_io_info[i].ioport; + } + } + + if (!found) { + printk(KERN_ERR PFX "No card detected, or port not available.\n"); + return -ENODEV; + } + + ret = misc_register(&mixcomwd_miscdev); + if (ret) + { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto error_misc_register_watchdog; + } + + printk(KERN_INFO "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n", + VERSION, watchdog_port); + + return 0; + +error_misc_register_watchdog: + release_region(watchdog_port, 1); + watchdog_port = 0x0000; + return ret; +} + +static void __exit mixcomwd_exit(void) +{ + if (!nowayout) { + if(mixcomwd_timer_alive) { + printk(KERN_WARNING PFX "I quit now, hardware will" + " probably reboot!\n"); + del_timer_sync(&mixcomwd_timer); + mixcomwd_timer_alive=0; + } + } + misc_deregister(&mixcomwd_miscdev); + release_region(watchdog_port,1); +} + +module_init(mixcomwd_init); +module_exit(mixcomwd_exit); + +MODULE_AUTHOR("Gergely Madarasz "); +MODULE_DESCRIPTION("MixCom Watchdog driver"); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/mpc5200_wdt.c b/drivers/watchdog/mpc5200_wdt.c new file mode 100644 index 0000000..9cfb975 --- /dev/null +++ b/drivers/watchdog/mpc5200_wdt.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define GPT_MODE_WDT (1<<15) +#define GPT_MODE_CE (1<<12) +#define GPT_MODE_MS_TIMER (0x4) + + +struct mpc5200_wdt { + unsigned count; /* timer ticks before watchdog kicks in */ + long ipb_freq; + struct miscdevice miscdev; + struct resource mem; + struct mpc52xx_gpt __iomem *regs; + spinlock_t io_lock; +}; + +/* is_active stores wether or not the /dev/watchdog device is opened */ +static unsigned long is_active; + +/* misc devices don't provide a way, to get back to 'dev' or 'miscdev' from + * file operations, which sucks. But there can be max 1 watchdog anyway, so... + */ +static struct mpc5200_wdt *wdt_global; + + +/* helper to calculate timeout in timer counts */ +static void mpc5200_wdt_set_timeout(struct mpc5200_wdt *wdt, int timeout) +{ + /* use biggest prescaler of 64k */ + wdt->count = (wdt->ipb_freq + 0xffff) / 0x10000 * timeout; + + if (wdt->count > 0xffff) + wdt->count = 0xffff; +} +/* return timeout in seconds (calculated from timer count) */ +static int mpc5200_wdt_get_timeout(struct mpc5200_wdt *wdt) +{ + return wdt->count * 0x10000 / wdt->ipb_freq; +} + + +/* watchdog operations */ +static int mpc5200_wdt_start(struct mpc5200_wdt *wdt) +{ + spin_lock(&wdt->io_lock); + /* disable */ + out_be32(&wdt->regs->mode, 0); + /* set timeout, with maximum prescaler */ + out_be32(&wdt->regs->count, 0x0 | wdt->count); + /* enable watchdog */ + out_be32(&wdt->regs->mode, GPT_MODE_CE | GPT_MODE_WDT | GPT_MODE_MS_TIMER); + spin_unlock(&wdt->io_lock); + + return 0; +} +static int mpc5200_wdt_ping(struct mpc5200_wdt *wdt) +{ + spin_lock(&wdt->io_lock); + /* writing A5 to OCPW resets the watchdog */ + out_be32(&wdt->regs->mode, 0xA5000000 | (0xffffff & in_be32(&wdt->regs->mode))); + spin_unlock(&wdt->io_lock); + return 0; +} +static int mpc5200_wdt_stop(struct mpc5200_wdt *wdt) +{ + spin_lock(&wdt->io_lock); + /* disable */ + out_be32(&wdt->regs->mode, 0); + spin_unlock(&wdt->io_lock); + return 0; +} + + +/* file operations */ +static ssize_t mpc5200_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + struct mpc5200_wdt *wdt = file->private_data; + mpc5200_wdt_ping(wdt); + return 0; +} +static struct watchdog_info mpc5200_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "mpc5200 watchdog on GPT0", +}; +static int mpc5200_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mpc5200_wdt *wdt = file->private_data; + int __user *data = (int __user *)arg; + int timeout; + int ret = 0; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(data, &mpc5200_wdt_info, + sizeof(mpc5200_wdt_info)); + if (ret) + ret = -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, data); + break; + + case WDIOC_KEEPALIVE: + mpc5200_wdt_ping(wdt); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(timeout, data); + if (ret) + break; + mpc5200_wdt_set_timeout(wdt, timeout); + mpc5200_wdt_start(wdt); + /* fall through and return the timeout */ + + case WDIOC_GETTIMEOUT: + timeout = mpc5200_wdt_get_timeout(wdt); + ret = put_user(timeout, data); + break; + + default: + ret = -ENOTTY; + } + return ret; +} +static int mpc5200_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; + + /* Set and activate the watchdog */ + mpc5200_wdt_set_timeout(wdt_global, 30); + mpc5200_wdt_start(wdt_global); + file->private_data = wdt_global; + return nonseekable_open(inode, file); +} +static int mpc5200_wdt_release(struct inode *inode, struct file *file) +{ +#if WATCHDOG_NOWAYOUT == 0 + struct mpc5200_wdt *wdt = file->private_data; + mpc5200_wdt_stop(wdt); + wdt->count = 0; /* == disabled */ +#endif + clear_bit(0, &is_active); + return 0; +} + +static struct file_operations mpc5200_wdt_fops = { + .owner = THIS_MODULE, + .write = mpc5200_wdt_write, + .ioctl = mpc5200_wdt_ioctl, + .open = mpc5200_wdt_open, + .release = mpc5200_wdt_release, +}; + +/* module operations */ +static int mpc5200_wdt_probe(struct of_device *op, const struct of_device_id *match) +{ + struct mpc5200_wdt *wdt; + int err; + const void *has_wdt; + int size; + + has_wdt = of_get_property(op->node, "has-wdt", NULL); + if (!has_wdt) + return -ENODEV; + + wdt = kzalloc(sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->ipb_freq = mpc52xx_find_ipb_freq(op->node); + + err = of_address_to_resource(op->node, 0, &wdt->mem); + if (err) + goto out_free; + size = wdt->mem.end - wdt->mem.start + 1; + if (!request_mem_region(wdt->mem.start, size, "mpc5200_wdt")) { + err = -ENODEV; + goto out_free; + } + wdt->regs = ioremap(wdt->mem.start, size); + if (!wdt->regs) { + err = -ENODEV; + goto out_release; + } + + dev_set_drvdata(&op->dev, wdt); + spin_lock_init(&wdt->io_lock); + + wdt->miscdev = (struct miscdevice) { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpc5200_wdt_fops, + .parent = &op->dev, + }; + wdt_global = wdt; + err = misc_register(&wdt->miscdev); + if (!err) + return 0; + + iounmap(wdt->regs); + out_release: + release_mem_region(wdt->mem.start, size); + out_free: + kfree(wdt); + return err; +} + +static int mpc5200_wdt_remove(struct of_device *op) +{ + struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); + + mpc5200_wdt_stop(wdt); + misc_deregister(&wdt->miscdev); + iounmap(wdt->regs); + release_mem_region(wdt->mem.start, wdt->mem.end - wdt->mem.start + 1); + kfree(wdt); + + return 0; +} +static int mpc5200_wdt_suspend(struct of_device *op, pm_message_t state) +{ + struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); + mpc5200_wdt_stop(wdt); + return 0; +} +static int mpc5200_wdt_resume(struct of_device *op) +{ + struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); + if (wdt->count) + mpc5200_wdt_start(wdt); + return 0; +} +static int mpc5200_wdt_shutdown(struct of_device *op) +{ + struct mpc5200_wdt *wdt = dev_get_drvdata(&op->dev); + mpc5200_wdt_stop(wdt); + return 0; +} + +static struct of_device_id mpc5200_wdt_match[] = { + { .compatible = "mpc5200-gpt", }, + {}, +}; +static struct of_platform_driver mpc5200_wdt_driver = { + .owner = THIS_MODULE, + .name = "mpc5200-gpt-wdt", + .match_table = mpc5200_wdt_match, + .probe = mpc5200_wdt_probe, + .remove = mpc5200_wdt_remove, + .suspend = mpc5200_wdt_suspend, + .resume = mpc5200_wdt_resume, + .shutdown = mpc5200_wdt_shutdown, +}; + + +static int __init mpc5200_wdt_init(void) +{ + return of_register_platform_driver(&mpc5200_wdt_driver); +} + +static void __exit mpc5200_wdt_exit(void) +{ + of_unregister_platform_driver(&mpc5200_wdt_driver); +} + +module_init(mpc5200_wdt_init); +module_exit(mpc5200_wdt_exit); + +MODULE_AUTHOR("Domen Puncer "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/mpc83xx_wdt.c b/drivers/watchdog/mpc83xx_wdt.c new file mode 100644 index 0000000..a0bf95f --- /dev/null +++ b/drivers/watchdog/mpc83xx_wdt.c @@ -0,0 +1,231 @@ +/* + * mpc83xx_wdt.c - MPC83xx watchdog userspace interface + * + * Authors: Dave Updegraff + * Kumar Gala + * Attribution: from 83xx_wst: Florian Schirmer + * ..and from sc520_wdt + * + * Note: it appears that you can only actually ENABLE or DISABLE the thing + * once after POR. Once enabled, you cannot disable, and vice versa. + * + * 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 + +struct mpc83xx_wdt { + __be32 res0; + __be32 swcrr; /* System watchdog control register */ +#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */ +#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */ +#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/ +#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */ + __be32 swcnr; /* System watchdog count register */ + u8 res1[2]; + __be16 swsrr; /* System watchdog service register */ + u8 res2[0xF0]; +}; + +static struct mpc83xx_wdt __iomem *wd_base; + +static u16 timeout = 0xffff; +module_param(timeout, ushort, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in ticks. (0swsrr, 0x556c); + out_be16(&wd_base->swsrr, 0xaa39); + spin_unlock(&wdt_spinlock); +} + +static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) + mpc83xx_wdt_keepalive(); + return count; +} + +static int mpc83xx_wdt_open(struct inode *inode, struct file *file) +{ + u32 tmp = SWCRR_SWEN; + if (test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + + /* Once we start the watchdog we can't stop it */ + __module_get(THIS_MODULE); + + /* Good, fire up the show */ + if (prescale) + tmp |= SWCRR_SWPR; + if (reset) + tmp |= SWCRR_SWRI; + + tmp |= timeout << 16; + + out_be32(&wd_base->swcrr, tmp); + + return nonseekable_open(inode, file); +} + +static int mpc83xx_wdt_release(struct inode *inode, struct file *file) +{ + printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); + mpc83xx_wdt_keepalive(); + clear_bit(0, &wdt_is_open); + return 0; +} + +static int mpc83xx_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = "MPC83xx", + }; + + 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: + mpc83xx_wdt_keepalive(); + return 0; + case WDIOC_GETTIMEOUT: + return put_user(timeout_sec, p); + default: + return -ENOTTY; + } +} + +static const struct file_operations mpc83xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mpc83xx_wdt_write, + .ioctl = mpc83xx_wdt_ioctl, + .open = mpc83xx_wdt_open, + .release = mpc83xx_wdt_release, +}; + +static struct miscdevice mpc83xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpc83xx_wdt_fops, +}; + +static int __devinit mpc83xx_wdt_probe(struct platform_device *dev) +{ + struct resource *r; + int ret; + unsigned int *freq = dev->dev.platform_data; + + /* get a pointer to the register memory */ + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + + if (!r) { + ret = -ENODEV; + goto err_out; + } + + wd_base = ioremap(r->start, sizeof (struct mpc83xx_wdt)); + + if (wd_base == NULL) { + ret = -ENOMEM; + goto err_out; + } + + ret = misc_register(&mpc83xx_wdt_miscdev); + if (ret) { + printk(KERN_ERR "cannot register miscdev on minor=%d " + "(err=%d)\n", + WATCHDOG_MINOR, ret); + goto err_unmap; + } + + /* Calculate the timeout in seconds */ + if (prescale) + timeout_sec = (timeout * 0x10000) / (*freq); + else + timeout_sec = timeout / (*freq); + + printk(KERN_INFO "WDT driver for MPC83xx initialized. " + "mode:%s timeout=%d (%d seconds)\n", + reset ? "reset":"interrupt", timeout, timeout_sec); + + spin_lock_init(&wdt_spinlock); + + return 0; + +err_unmap: + iounmap(wd_base); +err_out: + return ret; +} + +static int __devexit mpc83xx_wdt_remove(struct platform_device *dev) +{ + misc_deregister(&mpc83xx_wdt_miscdev); + iounmap(wd_base); + + return 0; +} + +static struct platform_driver mpc83xx_wdt_driver = { + .probe = mpc83xx_wdt_probe, + .remove = __devexit_p(mpc83xx_wdt_remove), + .driver = { + .name = "mpc83xx_wdt", + }, +}; + +static int __init mpc83xx_wdt_init(void) +{ + return platform_driver_register(&mpc83xx_wdt_driver); +} + +static void __exit mpc83xx_wdt_exit(void) +{ + platform_driver_unregister(&mpc83xx_wdt_driver); +} + +module_init(mpc83xx_wdt_init); +module_exit(mpc83xx_wdt_exit); + +MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); +MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/mpc8xx_wdt.c b/drivers/watchdog/mpc8xx_wdt.c new file mode 100644 index 0000000..85b5734 --- /dev/null +++ b/drivers/watchdog/mpc8xx_wdt.c @@ -0,0 +1,169 @@ +/* + * mpc8xx_wdt.c - MPC8xx watchdog userspace interface + * + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned long wdt_opened; +static int wdt_status; + +static void mpc8xx_wdt_handler_disable(void) +{ + volatile uint __iomem *piscr; + piscr = (uint *)&((immap_t*)IMAP_ADDR)->im_sit.sit_piscr; + + if (!m8xx_has_internal_rtc) + m8xx_wdt_stop_timer(); + else + out_be32(piscr, in_be32(piscr) & ~(PISCR_PIE | PISCR_PTE)); + + printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); +} + +static void mpc8xx_wdt_handler_enable(void) +{ + volatile uint __iomem *piscr; + piscr = (uint *)&((immap_t*)IMAP_ADDR)->im_sit.sit_piscr; + + if (!m8xx_has_internal_rtc) + m8xx_wdt_install_timer(); + else + out_be32(piscr, in_be32(piscr) | PISCR_PIE | PISCR_PTE); + + printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); +} + +static int mpc8xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &wdt_opened)) + return -EBUSY; + + m8xx_wdt_reset(); + mpc8xx_wdt_handler_disable(); + + return nonseekable_open(inode, file); +} + +static int mpc8xx_wdt_release(struct inode *inode, struct file *file) +{ + m8xx_wdt_reset(); + +#if !defined(CONFIG_WATCHDOG_NOWAYOUT) + mpc8xx_wdt_handler_enable(); +#endif + + clear_bit(0, &wdt_opened); + + return 0; +} + +static ssize_t mpc8xx_wdt_write(struct file *file, const char *data, size_t len, + loff_t * ppos) +{ + if (len) + m8xx_wdt_reset(); + + return len; +} + +static int mpc8xx_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int timeout; + static struct watchdog_info info = { + .options = WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "MPC8xx watchdog", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(wdt_status, (int *)arg)) + return -EFAULT; + wdt_status &= ~WDIOF_KEEPALIVEPING; + break; + + case WDIOC_GETTEMP: + return -EOPNOTSUPP; + + case WDIOC_SETOPTIONS: + return -EOPNOTSUPP; + + case WDIOC_KEEPALIVE: + m8xx_wdt_reset(); + wdt_status |= WDIOF_KEEPALIVEPING; + break; + + case WDIOC_SETTIMEOUT: + return -EOPNOTSUPP; + + case WDIOC_GETTIMEOUT: + timeout = m8xx_wdt_get_timeout(); + if (put_user(timeout, (int *)arg)) + return -EFAULT; + break; + + default: + return -ENOTTY; + } + + return 0; +} + +static const struct file_operations mpc8xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mpc8xx_wdt_write, + .ioctl = mpc8xx_wdt_ioctl, + .open = mpc8xx_wdt_open, + .release = mpc8xx_wdt_release, +}; + +static struct miscdevice mpc8xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpc8xx_wdt_fops, +}; + +static int __init mpc8xx_wdt_init(void) +{ + return misc_register(&mpc8xx_wdt_miscdev); +} + +static void __exit mpc8xx_wdt_exit(void) +{ + misc_deregister(&mpc8xx_wdt_miscdev); + + m8xx_wdt_reset(); + mpc8xx_wdt_handler_enable(); +} + +module_init(mpc8xx_wdt_init); +module_exit(mpc8xx_wdt_exit); + +MODULE_AUTHOR("Florian Schirmer "); +MODULE_DESCRIPTION("MPC8xx watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c new file mode 100644 index 0000000..0d2b277 --- /dev/null +++ b/drivers/watchdog/mpcore_wdt.c @@ -0,0 +1,435 @@ +/* + * Watchdog driver for the mpcore watchdog timer + * + * (c) Copyright 2004 ARM Limited + * + * Based on the SoftDog driver: + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct mpcore_wdt { + unsigned long timer_alive; + struct device *dev; + void __iomem *base; + int irq; + unsigned int perturb; + char expect_close; +}; + +static struct platform_device *mpcore_wdt_dev; + +extern unsigned int mpcore_timer_rate; + +#define TIMER_MARGIN 60 +static int mpcore_margin = TIMER_MARGIN; +module_param(mpcore_margin, int, 0); +MODULE_PARM_DESC(mpcore_margin, "MPcore timer margin in seconds. (0base + TWD_WDOG_INTSTAT)) { + dev_printk(KERN_CRIT, wdt->dev, "Triggered - Reboot ignored.\n"); + + /* Clear the interrupt on the watchdog */ + writel(1, wdt->base + TWD_WDOG_INTSTAT); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * mpcore_wdt_keepalive - reload the timer + * + * Note that the spec says a DIFFERENT value must be written to the reload + * register each time. The "perturb" variable deals with this by adding 1 + * to the count every other time the function is called. + */ +static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt) +{ + unsigned int count; + + /* Assume prescale is set to 256 */ + count = (mpcore_timer_rate / 256) * mpcore_margin; + + /* Reload the counter */ + writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD); + + wdt->perturb = wdt->perturb ? 0 : 1; +} + +static void mpcore_wdt_stop(struct mpcore_wdt *wdt) +{ + writel(0x12345678, wdt->base + TWD_WDOG_DISABLE); + writel(0x87654321, wdt->base + TWD_WDOG_DISABLE); + writel(0x0, wdt->base + TWD_WDOG_CONTROL); +} + +static void mpcore_wdt_start(struct mpcore_wdt *wdt) +{ + dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n"); + + /* This loads the count register but does NOT start the count yet */ + mpcore_wdt_keepalive(wdt); + + if (mpcore_noboot) { + /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */ + writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL); + } else { + /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */ + writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL); + } +} + +static int mpcore_wdt_set_heartbeat(int t) +{ + if (t < 0x0001 || t > 0xFFFF) + return -EINVAL; + + mpcore_margin = t; + return 0; +} + +/* + * /dev/watchdog handling + */ +static int mpcore_wdt_open(struct inode *inode, struct file *file) +{ + struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev); + + if (test_and_set_bit(0, &wdt->timer_alive)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + file->private_data = wdt; + + /* + * Activate timer + */ + mpcore_wdt_start(wdt); + + return nonseekable_open(inode, file); +} + +static int mpcore_wdt_release(struct inode *inode, struct file *file) +{ + struct mpcore_wdt *wdt = file->private_data; + + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (wdt->expect_close == 42) { + mpcore_wdt_stop(wdt); + } else { + dev_printk(KERN_CRIT, wdt->dev, "unexpected close, not stopping watchdog!\n"); + mpcore_wdt_keepalive(wdt); + } + clear_bit(0, &wdt->timer_alive); + wdt->expect_close = 0; + return 0; +} + +static ssize_t mpcore_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + struct mpcore_wdt *wdt = file->private_data; + + /* + * Refresh the timer. + */ + if (len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + wdt->expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + wdt->expect_close = 42; + } + } + mpcore_wdt_keepalive(wdt); + } + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "MPcore Watchdog", +}; + +static int mpcore_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mpcore_wdt *wdt = file->private_data; + int ret; + union { + struct watchdog_info ident; + int i; + } uarg; + + if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg)) + return -ENOTTY; + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd)); + if (ret) + return -EFAULT; + } + + switch (cmd) { + case WDIOC_GETSUPPORT: + uarg.ident = ident; + ret = 0; + break; + + case WDIOC_SETOPTIONS: + ret = -EINVAL; + if (uarg.i & WDIOS_DISABLECARD) { + mpcore_wdt_stop(wdt); + ret = 0; + } + if (uarg.i & WDIOS_ENABLECARD) { + mpcore_wdt_start(wdt); + ret = 0; + } + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + uarg.i = 0; + ret = 0; + break; + + case WDIOC_KEEPALIVE: + mpcore_wdt_keepalive(wdt); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = mpcore_wdt_set_heartbeat(uarg.i); + if (ret) + break; + + mpcore_wdt_keepalive(wdt); + /* Fall */ + case WDIOC_GETTIMEOUT: + uarg.i = mpcore_margin; + ret = 0; + break; + + default: + return -ENOTTY; + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd)); + if (ret) + ret = -EFAULT; + } + return ret; +} + +/* + * System shutdown handler. Turn off the watchdog if we're + * restarting or halting the system. + */ +static void mpcore_wdt_shutdown(struct platform_device *dev) +{ + struct mpcore_wdt *wdt = platform_get_drvdata(dev); + + if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT) + mpcore_wdt_stop(wdt); +} + +/* + * Kernel Interfaces + */ +static const struct file_operations mpcore_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mpcore_wdt_write, + .ioctl = mpcore_wdt_ioctl, + .open = mpcore_wdt_open, + .release = mpcore_wdt_release, +}; + +static struct miscdevice mpcore_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpcore_wdt_fops, +}; + +static int __devinit mpcore_wdt_probe(struct platform_device *dev) +{ + struct mpcore_wdt *wdt; + struct resource *res; + int ret; + + /* We only accept one device, and it must have an id of -1 */ + if (dev->id != -1) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto err_out; + } + + wdt = kzalloc(sizeof(struct mpcore_wdt), GFP_KERNEL); + if (!wdt) { + ret = -ENOMEM; + goto err_out; + } + + wdt->dev = &dev->dev; + wdt->irq = platform_get_irq(dev, 0); + if (wdt->irq < 0) { + ret = -ENXIO; + goto err_free; + } + wdt->base = ioremap(res->start, res->end - res->start + 1); + if (!wdt->base) { + ret = -ENOMEM; + goto err_free; + } + + mpcore_wdt_miscdev.parent = &dev->dev; + ret = misc_register(&mpcore_wdt_miscdev); + if (ret) { + dev_printk(KERN_ERR, _dev, "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto err_misc; + } + + ret = request_irq(wdt->irq, mpcore_wdt_fire, IRQF_DISABLED, "mpcore_wdt", wdt); + if (ret) { + dev_printk(KERN_ERR, _dev, "cannot register IRQ%d for watchdog\n", wdt->irq); + goto err_irq; + } + + mpcore_wdt_stop(wdt); + platform_set_drvdata(&dev->dev, wdt); + mpcore_wdt_dev = dev; + + return 0; + + err_irq: + misc_deregister(&mpcore_wdt_miscdev); + err_misc: + iounmap(wdt->base); + err_free: + kfree(wdt); + err_out: + return ret; +} + +static int __devexit mpcore_wdt_remove(struct platform_device *dev) +{ + struct mpcore_wdt *wdt = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + misc_deregister(&mpcore_wdt_miscdev); + + mpcore_wdt_dev = NULL; + + free_irq(wdt->irq, wdt); + iounmap(wdt->base); + kfree(wdt); + return 0; +} + +static struct platform_driver mpcore_wdt_driver = { + .probe = mpcore_wdt_probe, + .remove = __devexit_p(mpcore_wdt_remove), + .shutdown = mpcore_wdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "mpcore_wdt", + }, +}; + +static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n"; + +static int __init mpcore_wdt_init(void) +{ + /* + * Check that the margin value is within it's range; + * if not reset to the default + */ + if (mpcore_wdt_set_heartbeat(mpcore_margin)) { + mpcore_wdt_set_heartbeat(TIMER_MARGIN); + printk(KERN_INFO "mpcore_margin value must be 0, All Rights Reserved. + * http://www.4g-systems.biz + * + * (C) Copyright 2007 OpenWrt.org, Florian Fainelli + * + * 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 Michael Stickel nor 4G Systems admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 2005 4G Systems + * + * Release 0.01. + * Author: Michael Stickel michael.stickel@4g-systems.biz + * + * Release 0.02. + * Author: Florian Fainelli florian@openwrt.org + * use the Linux watchdog/timer APIs + * + * The Watchdog is configured to reset the MTX-1 + * if it is not triggered for 100 seconds. + * It should not be triggered more often than 1.6 seconds. + * + * A timer triggers the watchdog every 5 seconds, until + * it is opened for the first time. After the first open + * it MUST be triggered every 2..95 seconds. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MTX1_WDT_INTERVAL (5 * HZ) + +static int ticks = 100 * HZ; + +static struct { + struct completion stop; + volatile int running; + struct timer_list timer; + volatile int queue; + int default_ticks; + unsigned long inuse; +} mtx1_wdt_device; + +static void mtx1_wdt_trigger(unsigned long unused) +{ + u32 tmp; + + if (mtx1_wdt_device.running) + ticks--; + /* + * toggle GPIO2_15 + */ + tmp = au_readl(GPIO2_DIR); + tmp = (tmp & ~(1<<15)) | ((~tmp) & (1<<15)); + au_writel (tmp, GPIO2_DIR); + + if (mtx1_wdt_device.queue && ticks) + mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); + else { + complete(&mtx1_wdt_device.stop); + } +} + +static void mtx1_wdt_reset(void) +{ + ticks = mtx1_wdt_device.default_ticks; +} + + +static void mtx1_wdt_start(void) +{ + if (!mtx1_wdt_device.queue) { + mtx1_wdt_device.queue = 1; + au_writel (au_readl(GPIO2_DIR) | (u32)(1<<15), GPIO2_DIR); + mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); + } + mtx1_wdt_device.running++; +} + +static int mtx1_wdt_stop(void) +{ + if (mtx1_wdt_device.queue) { + mtx1_wdt_device.queue = 0; + au_writel (au_readl(GPIO2_DIR) & ~((u32)(1<<15)), GPIO2_DIR); + } + + ticks = mtx1_wdt_device.default_ticks; + + return 0; +} + +/* Filesystem functions */ + +static int mtx1_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &mtx1_wdt_device.inuse)) + return -EBUSY; + + return nonseekable_open(inode, file); +} + + +static int mtx1_wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &mtx1_wdt_device.inuse); + return 0; +} + +static int mtx1_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 = "MTX-1 WDT", + }; + + switch(cmd) { + case WDIOC_KEEPALIVE: + mtx1_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: + mtx1_wdt_start(); + break; + case WDIOS_DISABLECARD: + return mtx1_wdt_stop(); + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + + +static ssize_t mtx1_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + if (!count) + return -EIO; + + mtx1_wdt_reset(); + return count; +} + +static struct file_operations mtx1_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = mtx1_wdt_ioctl, + .open = mtx1_wdt_open, + .write = mtx1_wdt_write, + .release = mtx1_wdt_release +}; + + +static struct miscdevice mtx1_wdt_misc = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mtx1_wdt_fops +}; + + +static int __init mtx1_wdt_init(void) +{ + int ret; + + if ((ret = misc_register(&mtx1_wdt_misc)) < 0) { + printk(KERN_ERR " mtx-1_wdt : failed to register\n"); + return ret; + } + + init_completion(&mtx1_wdt_device.stop); + mtx1_wdt_device.queue = 0; + + clear_bit(0, &mtx1_wdt_device.inuse); + + setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L); + + mtx1_wdt_device.default_ticks = ticks; + + mtx1_wdt_start(); + + printk(KERN_INFO "MTX-1 Watchdog driver\n"); + + return 0; +} + +static void __exit mtx1_wdt_exit(void) +{ + if (mtx1_wdt_device.queue) { + mtx1_wdt_device.queue = 0; + wait_for_completion(&mtx1_wdt_device.stop); + } + misc_deregister(&mtx1_wdt_misc); +} + +module_init(mtx1_wdt_init); +module_exit(mtx1_wdt_exit); + +MODULE_AUTHOR("Michael Stickel, Florian Fainelli"); +MODULE_DESCRIPTION("Driver for the MTX-1 watchdog"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c new file mode 100644 index 0000000..0365c31 --- /dev/null +++ b/drivers/watchdog/mv64x60_wdt.c @@ -0,0 +1,326 @@ +/* + * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface + * + * Author: James Chapman + * + * Platform-specific setup code should configure the dog to generate + * interrupt or reset as required. This code only enables/disables + * and services the watchdog. + * + * Derived from mpc8xx_wdt.c, with the following copyright. + * + * 2002 (c) Florian Schirmer This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MV64x60_WDT_WDC_OFFSET 0 + +/* + * The watchdog configuration register contains a pair of 2-bit fields, + * 1. a reload field, bits 27-26, which triggers a reload of + * the countdown register, and + * 2. an enable field, bits 25-24, which toggles between + * enabling and disabling the watchdog timer. + * Bit 31 is a read-only field which indicates whether the + * watchdog timer is currently enabled. + * + * The low 24 bits contain the timer reload value. + */ +#define MV64x60_WDC_ENABLE_SHIFT 24 +#define MV64x60_WDC_SERVICE_SHIFT 26 +#define MV64x60_WDC_ENABLED_SHIFT 31 + +#define MV64x60_WDC_ENABLED_TRUE 1 +#define MV64x60_WDC_ENABLED_FALSE 0 + +/* Flags bits */ +#define MV64x60_WDOG_FLAG_OPENED 0 + +static unsigned long wdt_flags; +static int wdt_status; +static void __iomem *mv64x60_wdt_regs; +static int mv64x60_wdt_timeout; +static int mv64x60_wdt_count; +static unsigned int bus_clk; +static char expect_close; +static DEFINE_SPINLOCK(mv64x60_wdt_spinlock); + +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 mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift) +{ + u32 data; + u32 enabled; + int ret = 0; + + spin_lock(&mv64x60_wdt_spinlock); + data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); + enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1; + + /* only toggle the requested field if enabled state matches predicate */ + if ((enabled ^ enabled_predicate) == 0) { + /* We write a 1, then a 2 -- to the appropriate field */ + data = (1 << field_shift) | mv64x60_wdt_count; + writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); + + data = (2 << field_shift) | mv64x60_wdt_count; + writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET); + ret = 1; + } + spin_unlock(&mv64x60_wdt_spinlock); + + return ret; +} + +static void mv64x60_wdt_service(void) +{ + mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE, + MV64x60_WDC_SERVICE_SHIFT); +} + +static void mv64x60_wdt_handler_enable(void) +{ + if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE, + MV64x60_WDC_ENABLE_SHIFT)) { + mv64x60_wdt_service(); + printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n"); + } +} + +static void mv64x60_wdt_handler_disable(void) +{ + if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE, + MV64x60_WDC_ENABLE_SHIFT)) + printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n"); +} + +static void mv64x60_wdt_set_timeout(unsigned int timeout) +{ + /* maximum bus cycle count is 0xFFFFFFFF */ + if (timeout > 0xFFFFFFFF / bus_clk) + timeout = 0xFFFFFFFF / bus_clk; + + mv64x60_wdt_count = timeout * bus_clk >> 8; + mv64x60_wdt_timeout = timeout; +} + +static int mv64x60_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + mv64x60_wdt_handler_enable(); + + return nonseekable_open(inode, file); +} + +static int mv64x60_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close == 42) + mv64x60_wdt_handler_disable(); + else { + printk(KERN_CRIT + "mv64x60_wdt: unexpected close, not stopping timer!\n"); + mv64x60_wdt_service(); + } + expect_close = 0; + + clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags); + + return 0; +} + +static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t * ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + if(get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + mv64x60_wdt_service(); + } + + return len; +} + +static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int timeout; + int options; + void __user *argp = (void __user *)arg; + static struct watchdog_info info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "MV64x60 watchdog", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(wdt_status, (int __user *)argp)) + return -EFAULT; + wdt_status &= ~WDIOF_KEEPALIVEPING; + break; + + case WDIOC_GETTEMP: + return -EOPNOTSUPP; + + case WDIOC_SETOPTIONS: + if (get_user(options, (int __user *)argp)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) + mv64x60_wdt_handler_disable(); + + if (options & WDIOS_ENABLECARD) + mv64x60_wdt_handler_enable(); + break; + + case WDIOC_KEEPALIVE: + mv64x60_wdt_service(); + wdt_status |= WDIOF_KEEPALIVEPING; + break; + + case WDIOC_SETTIMEOUT: + if (get_user(timeout, (int __user *)argp)) + return -EFAULT; + mv64x60_wdt_set_timeout(timeout); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + if (put_user(mv64x60_wdt_timeout, (int __user *)argp)) + return -EFAULT; + break; + + default: + return -ENOTTY; + } + + return 0; +} + +static const struct file_operations mv64x60_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mv64x60_wdt_write, + .ioctl = mv64x60_wdt_ioctl, + .open = mv64x60_wdt_open, + .release = mv64x60_wdt_release, +}; + +static struct miscdevice mv64x60_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mv64x60_wdt_fops, +}; + +static int __devinit mv64x60_wdt_probe(struct platform_device *dev) +{ + struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data; + struct resource *r; + int timeout = 10; + + bus_clk = 133; /* in MHz */ + if (pdata) { + timeout = pdata->timeout; + bus_clk = pdata->bus_clk; + } + + /* Since bus_clk is truncated MHz, actual frequency could be + * up to 1MHz higher. Round up, since it's better to time out + * too late than too soon. + */ + bus_clk++; + bus_clk *= 1000000; /* convert to Hz */ + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1); + if (mv64x60_wdt_regs == NULL) + return -ENOMEM; + + mv64x60_wdt_set_timeout(timeout); + + mv64x60_wdt_handler_disable(); /* in case timer was already running */ + + return misc_register(&mv64x60_wdt_miscdev); +} + +static int __devexit mv64x60_wdt_remove(struct platform_device *dev) +{ + misc_deregister(&mv64x60_wdt_miscdev); + + mv64x60_wdt_handler_disable(); + + iounmap(mv64x60_wdt_regs); + + return 0; +} + +static struct platform_driver mv64x60_wdt_driver = { + .probe = mv64x60_wdt_probe, + .remove = __devexit_p(mv64x60_wdt_remove), + .driver = { + .owner = THIS_MODULE, + .name = MV64x60_WDT_NAME, + }, +}; + +static int __init mv64x60_wdt_init(void) +{ + printk(KERN_INFO "MV64x60 watchdog driver\n"); + + return platform_driver_register(&mv64x60_wdt_driver); +} + +static void __exit mv64x60_wdt_exit(void) +{ + platform_driver_unregister(&mv64x60_wdt_driver); +} + +module_init(mv64x60_wdt_init); +module_exit(mv64x60_wdt_exit); + +MODULE_AUTHOR("James Chapman "); +MODULE_DESCRIPTION("MV64x60 watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c new file mode 100644 index 0000000..719b066 --- /dev/null +++ b/drivers/watchdog/omap_wdt.c @@ -0,0 +1,389 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.c + * + * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog + * + * Author: MontaVista Software, Inc. + * or + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * History: + * + * 20030527: George G. Davis + * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * Copyright (c) 2004 Texas Instruments. + * 1. Modified to support OMAP1610 32-KHz watchdog timer + * 2. Ported to 2.6 kernel + * + * Copyright (c) 2005 David Brownell + * Use the driver model and standard identifiers; handle bigger timeouts. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "omap_wdt.h" + +static unsigned timer_margin; +module_param(timer_margin, uint, 0); +MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); + +static int omap_wdt_users; +static struct clk *armwdt_ck = NULL; +static struct clk *mpu_wdt_ick = NULL; +static struct clk *mpu_wdt_fck = NULL; + +static unsigned int wdt_trgr_pattern = 0x1234; + +static void omap_wdt_ping(void) +{ + /* wait for posted write to complete */ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + cpu_relax(); + wdt_trgr_pattern = ~wdt_trgr_pattern; + omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); + /* wait for posted write to complete */ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + cpu_relax(); + /* reloaded WCRR from WLDR */ +} + +static void omap_wdt_enable(void) +{ + /* Sequence to enable the watchdog */ + omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + cpu_relax(); + omap_writel(0x4444, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + cpu_relax(); +} + +static void omap_wdt_disable(void) +{ + /* sequence required to disable watchdog */ + omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + cpu_relax(); + omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + cpu_relax(); +} + +static void omap_wdt_adjust_timeout(unsigned new_timeout) +{ + if (new_timeout < TIMER_MARGIN_MIN) + new_timeout = TIMER_MARGIN_DEFAULT; + if (new_timeout > TIMER_MARGIN_MAX) + new_timeout = TIMER_MARGIN_MAX; + timer_margin = new_timeout; +} + +static void omap_wdt_set_timeout(void) +{ + u32 pre_margin = GET_WLDR_VAL(timer_margin); + + /* just count up at 32 KHz */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + cpu_relax(); + omap_writel(pre_margin, OMAP_WATCHDOG_LDR); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + cpu_relax(); +} + +/* + * Allow only one task to hold it open + */ + +static int omap_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) + return -EBUSY; + + if (cpu_is_omap16xx()) + clk_enable(armwdt_ck); /* Enable the clock */ + + if (cpu_is_omap24xx()) { + clk_enable(mpu_wdt_ick); /* Enable the interface clock */ + clk_enable(mpu_wdt_fck); /* Enable the functional clock */ + } + + /* initialize prescaler */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + cpu_relax(); + omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + cpu_relax(); + + omap_wdt_set_timeout(); + omap_wdt_enable(); + return nonseekable_open(inode, file); +} + +static int omap_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer unless NOWAYOUT is defined. + */ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + omap_wdt_disable(); + + if (cpu_is_omap16xx()) { + clk_disable(armwdt_ck); /* Disable the clock */ + clk_put(armwdt_ck); + armwdt_ck = NULL; + } + + if (cpu_is_omap24xx()) { + clk_disable(mpu_wdt_ick); /* Disable the clock */ + clk_disable(mpu_wdt_fck); /* Disable the clock */ + clk_put(mpu_wdt_ick); + clk_put(mpu_wdt_fck); + mpu_wdt_ick = NULL; + mpu_wdt_fck = NULL; + } +#else + printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); +#endif + omap_wdt_users = 0; + return 0; +} + +static ssize_t +omap_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* Refresh LOAD_TIME. */ + if (len) + omap_wdt_ping(); + return len; +} + +static int +omap_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "OMAP Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int __user *)arg); + case WDIOC_GETBOOTSTATUS: + if (cpu_is_omap16xx()) + return put_user(omap_readw(ARM_SYSST), + (int __user *)arg); + if (cpu_is_omap24xx()) + return put_user(omap_prcm_get_reset_sources(), + (int __user *)arg); + case WDIOC_KEEPALIVE: + omap_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + omap_wdt_adjust_timeout(new_margin); + + omap_wdt_disable(); + omap_wdt_set_timeout(); + omap_wdt_enable(); + + omap_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int __user *)arg); + } +} + +static const struct file_operations omap_wdt_fops = { + .owner = THIS_MODULE, + .write = omap_wdt_write, + .ioctl = omap_wdt_ioctl, + .open = omap_wdt_open, + .release = omap_wdt_release, +}; + +static struct miscdevice omap_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &omap_wdt_fops +}; + +static int __init omap_wdt_probe(struct platform_device *pdev) +{ + struct resource *res, *mem; + int ret; + + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + mem = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (mem == NULL) + return -EBUSY; + + platform_set_drvdata(pdev, mem); + + omap_wdt_users = 0; + + if (cpu_is_omap16xx()) { + armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); + if (IS_ERR(armwdt_ck)) { + ret = PTR_ERR(armwdt_ck); + armwdt_ck = NULL; + goto fail; + } + } + + if (cpu_is_omap24xx()) { + mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); + if (IS_ERR(mpu_wdt_ick)) { + ret = PTR_ERR(mpu_wdt_ick); + mpu_wdt_ick = NULL; + goto fail; + } + mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); + if (IS_ERR(mpu_wdt_fck)) { + ret = PTR_ERR(mpu_wdt_fck); + mpu_wdt_fck = NULL; + goto fail; + } + } + + omap_wdt_disable(); + omap_wdt_adjust_timeout(timer_margin); + + omap_wdt_miscdev.parent = &pdev->dev; + ret = misc_register(&omap_wdt_miscdev); + if (ret) + goto fail; + + pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); + + /* autogate OCP interface clock */ + omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); + return 0; + +fail: + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + release_resource(mem); + return ret; +} + +static void omap_wdt_shutdown(struct platform_device *pdev) +{ + omap_wdt_disable(); +} + +static int omap_wdt_remove(struct platform_device *pdev) +{ + struct resource *mem = platform_get_drvdata(pdev); + misc_deregister(&omap_wdt_miscdev); + release_resource(mem); + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + return 0; +} + +#ifdef CONFIG_PM + +/* REVISIT ... not clear this is the best way to handle system suspend; and + * it's very inappropriate for selective device suspend (e.g. suspending this + * through sysfs rather than by stopping the watchdog daemon). Also, this + * may not play well enough with NOWAYOUT... + */ + +static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (omap_wdt_users) + omap_wdt_disable(); + return 0; +} + +static int omap_wdt_resume(struct platform_device *pdev) +{ + if (omap_wdt_users) { + omap_wdt_enable(); + omap_wdt_ping(); + } + return 0; +} + +#else +#define omap_wdt_suspend NULL +#define omap_wdt_resume NULL +#endif + +static struct platform_driver omap_wdt_driver = { + .probe = omap_wdt_probe, + .remove = omap_wdt_remove, + .shutdown = omap_wdt_shutdown, + .suspend = omap_wdt_suspend, + .resume = omap_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "omap_wdt", + }, +}; + +static int __init omap_wdt_init(void) +{ + return platform_driver_register(&omap_wdt_driver); +} + +static void __exit omap_wdt_exit(void) +{ + platform_driver_unregister(&omap_wdt_driver); +} + +module_init(omap_wdt_init); +module_exit(omap_wdt_exit); + +MODULE_AUTHOR("George G. Davis"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h new file mode 100644 index 0000000..52a532a5 --- /dev/null +++ b/drivers/watchdog/omap_wdt.h @@ -0,0 +1,64 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.h + * + * BRIEF MODULE DESCRIPTION + * OMAP Watchdog timer register definitions + * + * Copyright (C) 2004 Texas Instruments. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + */ + +#ifndef _OMAP_WATCHDOG_H +#define _OMAP_WATCHDOG_H + +#define OMAP1610_WATCHDOG_BASE 0xfffeb000 +#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ + +#ifdef CONFIG_ARCH_OMAP24XX +#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE +#else +#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE +#define RM_RSTST_WKUP 0 +#endif + +#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) +#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) +#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) +#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) +#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) +#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) +#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) +#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) +#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) + +/* Using the prescaler, the OMAP watchdog could go for many + * months before firing. These limits work without scaling, + * with the 60 second default assumed by most tools and docs. + */ +#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ +#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ +#define TIMER_MARGIN_MIN 1 + +#define PTV 0 /* prescale */ +#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1< + * and Marcus Junker, + * + * 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 Sven Anders, 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. + * + * Release 1.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* #define DEBUG 1 */ + +#define DEFAULT_TIMEOUT 1 /* 1 minute */ +#define MAX_TIMEOUT 255 + +#define VERSION "1.1" +#define MODNAME "pc87413 WDT" +#define PFX MODNAME ": " +#define DPFX MODNAME " - DEBUG: " + +#define WDT_INDEX_IO_PORT (io+0) /* I/O port base (index register) */ +#define WDT_DATA_IO_PORT (WDT_INDEX_IO_PORT+1) +#define SWC_LDN 0x04 +#define SIOCFG2 0x22 /* Serial IO register */ +#define WDCTL 0x10 /* Watchdog-Timer-Controll-Register */ +#define WDTO 0x11 /* Watchdog timeout register */ +#define WDCFG 0x12 /* Watchdog config register */ + +static int io = 0x2E; /* Address used on Portwell Boards */ + +static int timeout = DEFAULT_TIMEOUT; /* timeout value */ +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 ----------------------------------------*/ + +/* Select pins for Watchdog output */ + +static inline void pc87413_select_wdt_out (void) +{ + unsigned int cr_data = 0; + + /* Step 1: Select multiple pin,pin55,as WDT output */ + + outb_p(SIOCFG2, WDT_INDEX_IO_PORT); + + cr_data = inb (WDT_DATA_IO_PORT); + + cr_data |= 0x80; /* Set Bit7 to 1*/ + outb_p(SIOCFG2, WDT_INDEX_IO_PORT); + + outb_p(cr_data, WDT_DATA_IO_PORT); + +#ifdef DEBUG + printk(KERN_INFO DPFX "Select multiple pin,pin55,as WDT output:" + " Bit7 to 1: %d\n", cr_data); +#endif +} + +/* Enable SWC functions */ + +static inline void pc87413_enable_swc(void) +{ + unsigned int cr_data=0; + + /* Step 2: Enable SWC functions */ + + outb_p(0x07, WDT_INDEX_IO_PORT); /* Point SWC_LDN (LDN=4) */ + outb_p(SWC_LDN, WDT_DATA_IO_PORT); + + outb_p(0x30, WDT_INDEX_IO_PORT); /* Read Index 0x30 First */ + cr_data = inb(WDT_DATA_IO_PORT); + cr_data |= 0x01; /* Set Bit0 to 1 */ + outb_p(0x30, WDT_INDEX_IO_PORT); + outb_p(cr_data, WDT_DATA_IO_PORT); /* Index0x30_bit0P1 */ + +#ifdef DEBUG + printk(KERN_INFO DPFX "pc87413 - Enable SWC functions\n"); +#endif +} + +/* Read SWC I/O base address */ + +static inline unsigned int pc87413_get_swc_base(void) +{ + unsigned int swc_base_addr = 0; + unsigned char addr_l, addr_h = 0; + + /* Step 3: Read SWC I/O Base Address */ + + outb_p(0x60, WDT_INDEX_IO_PORT); /* Read Index 0x60 */ + addr_h = inb(WDT_DATA_IO_PORT); + + outb_p(0x61, WDT_INDEX_IO_PORT); /* Read Index 0x61 */ + + addr_l = inb(WDT_DATA_IO_PORT); + + swc_base_addr = (addr_h << 8) + addr_l; + +#ifdef DEBUG + printk(KERN_INFO DPFX "Read SWC I/O Base Address: low %d, high %d," + " res %d\n", addr_l, addr_h, swc_base_addr); +#endif + + return swc_base_addr; +} + +/* Select Bank 3 of SWC */ + +static inline void pc87413_swc_bank3(unsigned int swc_base_addr) +{ + /* Step 4: Select Bank3 of SWC */ + + outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); + +#ifdef DEBUG + printk(KERN_INFO DPFX "Select Bank3 of SWC\n"); +#endif +} + +/* Set watchdog timeout to x minutes */ + +static inline void pc87413_programm_wdto(unsigned int swc_base_addr, + char pc87413_time) +{ + /* Step 5: Programm WDTO, Twd. */ + + outb_p(pc87413_time, swc_base_addr + WDTO); + +#ifdef DEBUG + printk(KERN_INFO DPFX "Set WDTO to %d minutes\n", pc87413_time); +#endif +} + +/* Enable WDEN */ + +static inline void pc87413_enable_wden(unsigned int swc_base_addr) +{ + /* Step 6: Enable WDEN */ + + outb_p(inb (swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); + +#ifdef DEBUG + printk(KERN_INFO DPFX "Enable WDEN\n"); +#endif +} + +/* Enable SW_WD_TREN */ +static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) +{ + /* Enable SW_WD_TREN */ + + outb_p(inb (swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); + +#ifdef DEBUG + printk(KERN_INFO DPFX "Enable SW_WD_TREN\n"); +#endif +} + +/* Disable SW_WD_TREN */ + +static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) +{ + /* Disable SW_WD_TREN */ + + outb_p(inb (swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); + +#ifdef DEBUG + printk(KERN_INFO DPFX "pc87413 - Disable SW_WD_TREN\n"); +#endif +} + +/* Enable SW_WD_TRG */ + +static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) +{ + /* Enable SW_WD_TRG */ + + outb_p(inb (swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); + +#ifdef DEBUG + printk(KERN_INFO DPFX "pc87413 - Enable SW_WD_TRG\n"); +#endif +} + +/* Disable SW_WD_TRG */ + +static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) +{ + /* Disable SW_WD_TRG */ + + outb_p(inb (swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); + +#ifdef DEBUG + printk(KERN_INFO DPFX "Disable SW_WD_TRG\n"); +#endif +} + +/* -- Higher level functions ------------------------------------*/ + +/* Enable the watchdog */ + +static void pc87413_enable(void) +{ + unsigned int swc_base_addr; + + spin_lock(&io_lock); + + pc87413_select_wdt_out(); + pc87413_enable_swc(); + swc_base_addr = pc87413_get_swc_base(); + pc87413_swc_bank3(swc_base_addr); + pc87413_programm_wdto(swc_base_addr, timeout); + pc87413_enable_wden(swc_base_addr); + pc87413_enable_sw_wd_tren(swc_base_addr); + pc87413_enable_sw_wd_trg(swc_base_addr); + + spin_unlock(&io_lock); +} + +/* Disable the watchdog */ + +static void pc87413_disable(void) +{ + unsigned int swc_base_addr; + + spin_lock(&io_lock); + + pc87413_select_wdt_out(); + pc87413_enable_swc(); + swc_base_addr = pc87413_get_swc_base(); + pc87413_swc_bank3(swc_base_addr); + pc87413_disable_sw_wd_tren(swc_base_addr); + pc87413_disable_sw_wd_trg(swc_base_addr); + pc87413_programm_wdto(swc_base_addr, 0); + + spin_unlock(&io_lock); +} + +/* Refresh the watchdog */ + +static void pc87413_refresh(void) +{ + unsigned int swc_base_addr; + + spin_lock(&io_lock); + + pc87413_select_wdt_out(); + pc87413_enable_swc(); + swc_base_addr = pc87413_get_swc_base(); + pc87413_swc_bank3(swc_base_addr); + pc87413_disable_sw_wd_tren(swc_base_addr); + pc87413_disable_sw_wd_trg(swc_base_addr); + pc87413_programm_wdto(swc_base_addr, timeout); + pc87413_enable_wden(swc_base_addr); + pc87413_enable_sw_wd_tren(swc_base_addr); + pc87413_enable_sw_wd_trg(swc_base_addr); + + spin_unlock(&io_lock); +} + +/* -- File operations -------------------------------------------*/ + +/** + * pc87413_open: + * @inode: inode of device + * @file: file handle to device + * + */ + +static int pc87413_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 */ + pc87413_refresh(); + + printk(KERN_INFO MODNAME "Watchdog enabled. Timeout set to" + " %d minute(s).\n", timeout); + + return nonseekable_open(inode, file); +} + +/** + * pc87413_release: + * @inode: inode to board + * @file: file handle to board + * + * The watchdog has a configurable API. There is a religious dispute + * between people who want their watchdog to be able to shut down and + * those who want to be sure if the watchdog manager dies the machine + * reboots. In the former case we disable the counters, in the latter + * case you have to open it again very soon. + */ + +static int pc87413_release(struct inode *inode, struct file *file) +{ + /* Shut off the timer. */ + + if (expect_close == 42) { + pc87413_disable(); + printk(KERN_INFO MODNAME "Watchdog disabled," + " sleeping again...\n"); + } else { + printk(KERN_CRIT MODNAME "Unexpected close, not stopping" + " watchdog!\n"); + pc87413_refresh(); + } + + clear_bit(0, &timer_enabled); + expect_close = 0; + + return 0; +} + +/** + * pc87413_status: + * + * return, if the watchdog is enabled (timeout is set...) + */ + + +static int pc87413_status(void) +{ + return 0; /* currently not supported */ +} + +/** + * pc87413_write: + * @file: file handle to the watchdog + * @data: data buffer to write + * @len: length in bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t pc87413_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 */ + pc87413_refresh(); + } + return len; +} + +/** + * pc87413_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. We only actually usefully support + * querying capabilities and current status. + */ + +static int pc87413_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 = 1, + .identity = "PC87413(HF/F) 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(pc87413_status(), uarg.i); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, uarg.i); + + case WDIOC_KEEPALIVE: + pc87413_refresh(); +#ifdef DEBUG + printk(KERN_INFO DPFX "keepalive\n"); +#endif + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + + // the API states this is given in secs + new_timeout /= 60; + + if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) + return -EINVAL; + + timeout = new_timeout; + pc87413_refresh(); + + // fall through and return the new timeout... + + case WDIOC_GETTIMEOUT: + + new_timeout = 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) { + pc87413_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + pc87413_enable(); + retval = 0; + } + + return retval; + } + } +} + +/* -- Notifier funtions -----------------------------------------*/ + +/** + * notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ + +static int pc87413_notify_sys(struct notifier_block *this, + unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + { + /* Turn the card off */ + pc87413_disable(); + } + return NOTIFY_DONE; +} + +/* -- Module's structures ---------------------------------------*/ + +static const struct file_operations pc87413_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pc87413_write, + .ioctl = pc87413_ioctl, + .open = pc87413_open, + .release = pc87413_release, +}; + +static struct notifier_block pc87413_notifier = +{ + .notifier_call = pc87413_notify_sys, +}; + +static struct miscdevice pc87413_miscdev= +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pc87413_fops +}; + +/* -- Module init functions -------------------------------------*/ + +/** + * pc87413_init: module's "constructor" + * + * Set up the WDT watchdog board. All we have to do is grab the + * resources we require and bitch if anyone beat us to them. + * The open() function will actually kick the board off. + */ + +static int __init pc87413_init(void) +{ + int ret; + + spin_lock_init(&io_lock); + + printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", WDT_INDEX_IO_PORT); + + /* request_region(io, 2, "pc87413"); */ + + ret = register_reboot_notifier(&pc87413_notifier); + if (ret != 0) { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + ret); + } + + ret = misc_register(&pc87413_miscdev); + + if (ret != 0) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&pc87413_notifier); + return ret; + } + + printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); + + pc87413_enable(); + + return 0; +} + +/** + * pc87413_exit: module's "destructor" + * + * Unload the watchdog. You cannot do this with any file handles open. + * If your watchdog is set to continue ticking on close and you unload + * it, well it keeps ticking. We won't get the interrupt but the board + * will not touch PC memory so all is fine. You just have to load a new + * module in 60 seconds or reboot. + */ + +static void __exit pc87413_exit(void) +{ + /* Stop the timer before we leave */ + if (!nowayout) + { + pc87413_disable(); + printk(KERN_INFO MODNAME "Watchdog disabled.\n"); + } + + misc_deregister(&pc87413_miscdev); + unregister_reboot_notifier(&pc87413_notifier); + /* release_region(io,2); */ + + printk(MODNAME " watchdog component driver removed.\n"); +} + +module_init(pc87413_init); +module_exit(pc87413_exit); + +MODULE_AUTHOR("Sven Anders , Marcus Junker ,"); +MODULE_DESCRIPTION("PC87413 WDT driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, MODNAME " I/O port (default: " __MODULE_STRING(io) ")."); + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes (default=" __MODULE_STRING(timeout) ")."); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c new file mode 100644 index 0000000..7b41434 --- /dev/null +++ b/drivers/watchdog/pcwd.c @@ -0,0 +1,1013 @@ +/* + * PC Watchdog Driver + * by Ken Hollis (khollis@bitgate.com) + * + * Permission granted from Simon Machell (smachell@berkprod.com) + * Written for the Linux Kernel, and GPLed by Ken Hollis + * + * 960107 Added request_region routines, modulized the whole thing. + * 960108 Fixed end-of-file pointer (Thanks to Dan Hollis), added + * WD_TIMEOUT define. + * 960216 Added eof marker on the file, and changed verbose messages. + * 960716 Made functional and cosmetic changes to the source for + * inclusion in Linux 2.0.x kernels, thanks to Alan Cox. + * 960717 Removed read/seek routines, replaced with ioctl. Also, added + * check_region command due to Alan's suggestion. + * 960821 Made changes to compile in newer 2.0.x kernels. Added + * "cold reboot sense" entry. + * 960825 Made a few changes to code, deleted some defines and made + * typedefs to replace them. Made heartbeat reset only available + * via ioctl, and removed the write routine. + * 960828 Added new items for PC Watchdog Rev.C card. + * 960829 Changed around all of the IOCTLs, added new features, + * added watchdog disable/re-enable routines. Added firmware + * version reporting. Added read routine for temperature. + * Removed some extra defines, added an autodetect Revision + * routine. + * 961006 Revised some documentation, fixed some cosmetic bugs. Made + * drivers to panic the system if it's overheating at bootup. + * 961118 Changed some verbiage on some of the output, tidied up + * code bits, and added compatibility to 2.1.x. + * 970912 Enabled board on open and disable on close. + * 971107 Took account of recent VFS changes (broke read). + * 971210 Disable board on initialisation in case board already ticking. + * 971222 Changed open/close for temperature handling + * Michael Meskes . + * 980112 Used minor numbers from include/linux/miscdevice.h + * 990403 Clear reset status after reading control status register in + * pcwd_showprevstate(). [Marc Boucher ] + * 990605 Made changes to code to support Firmware 1.22a, added + * fairly useless proc entry. + * 990610 removed said useless proc code for the merge + * 000403 Removed last traces of proc code. + * 011214 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * Added timeout module option to override default + */ + +/* + * A bells and whistles driver is available from http://www.pcwd.de/ + * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ + */ + +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For mdelay function */ +#include /* For timer related operations */ +#include /* For jiffies stuff */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For kernel_power_off() */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For isa devices */ +#include /* For io-port access */ +#include /* For spin_lock/spin_unlock/... */ + +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ + +/* Module and version information */ +#define WATCHDOG_VERSION "1.20" +#define WATCHDOG_DATE "18 Feb 2007" +#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" +#define WATCHDOG_NAME "pcwd" +#define PFX WATCHDOG_NAME ": " +#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " (" WATCHDOG_DATE ")\n" +#define WD_VER WATCHDOG_VERSION " (" WATCHDOG_DATE ")" + +/* + * It should be noted that PCWD_REVISION_B was removed because A and B + * are essentially the same types of card, with the exception that B + * has temperature reporting. Since I didn't receive a Rev.B card, + * the Rev.B card is not supported. (It's a good thing too, as they + * are no longer in production.) + */ +#define PCWD_REVISION_A 1 +#define PCWD_REVISION_C 2 + +/* + * These are the auto-probe addresses available. + * + * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350. + * Revision A has an address range of 2 addresses, while Revision C has 4. + */ +#define PCWD_ISA_NR_CARDS 3 +static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; + +/* + * These are the defines that describe the control status bits for the + * PCI-PC Watchdog card. +*/ +/* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */ +#define WD_WDRST 0x01 /* Previously reset state */ +#define WD_T110 0x02 /* Temperature overheat sense */ +#define WD_HRTBT 0x04 /* Heartbeat sense */ +#define WD_RLY2 0x08 /* External relay triggered */ +#define WD_SRLY2 0x80 /* Software external relay triggered */ +/* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */ +#define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ +#define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ +#define WD_REVC_TTRP 0x04 /* Temperature Trip status */ +#define WD_REVC_RL2A 0x08 /* Relay 2 activated by on-board processor */ +#define WD_REVC_RL1A 0x10 /* Relay 1 active */ +#define WD_REVC_R2DS 0x40 /* Relay 2 disable */ +#define WD_REVC_RLY2 0x80 /* Relay 2 activated? */ +/* Port 2 : Control Status #2 */ +#define WD_WDIS 0x10 /* Watchdog Disabled */ +#define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */ +#define WD_SSEL 0x40 /* Watchdog Switch Select (1:SW1 <-> 0:SW2) */ +#define WD_WCMD 0x80 /* Watchdog Command Mode */ + +/* max. time we give an ISA watchdog card to process a command */ +/* 500ms for each 4 bit response (according to spec.) */ +#define ISA_COMMAND_TIMEOUT 1000 + +/* Watchdog's internal commands */ +#define CMD_ISA_IDLE 0x00 +#define CMD_ISA_VERSION_INTEGER 0x01 +#define CMD_ISA_VERSION_TENTH 0x02 +#define CMD_ISA_VERSION_HUNDRETH 0x03 +#define CMD_ISA_VERSION_MINOR 0x04 +#define CMD_ISA_SWITCH_SETTINGS 0x05 +#define CMD_ISA_RESET_PC 0x06 +#define CMD_ISA_ARM_0 0x07 +#define CMD_ISA_ARM_30 0x08 +#define CMD_ISA_ARM_60 0x09 +#define CMD_ISA_DELAY_TIME_2SECS 0x0A +#define CMD_ISA_DELAY_TIME_4SECS 0x0B +#define CMD_ISA_DELAY_TIME_8SECS 0x0C +#define CMD_ISA_RESET_RELAYS 0x0D + +/* Watchdog's Dip Switch heartbeat values */ +static const int heartbeat_tbl [] = { + 20, /* OFF-OFF-OFF = 20 Sec */ + 40, /* OFF-OFF-ON = 40 Sec */ + 60, /* OFF-ON-OFF = 1 Min */ + 300, /* OFF-ON-ON = 5 Min */ + 600, /* ON-OFF-OFF = 10 Min */ + 1800, /* ON-OFF-ON = 30 Min */ + 3600, /* ON-ON-OFF = 1 Hour */ + 7200, /* ON-ON-ON = 2 hour */ +}; + +/* + * We are using an kernel timer to do the pinging of the watchdog + * every ~500ms. We try to set the internal heartbeat of the + * watchdog to 2 ms. + */ + +#define WDT_INTERVAL (HZ/2+1) + +/* We can only use 1 card due to the /dev/watchdog restriction */ +static int cards_found; + +/* internal variables */ +static atomic_t open_allowed = ATOMIC_INIT(1); +static char expect_close; +static int temp_panic; +static struct { /* this is private data for each ISA-PC watchdog card */ + char fw_ver_str[6]; /* The cards firmware version */ + int revision; /* The card's revision */ + int supports_temp; /* Wether or not the card has a temperature device */ + int command_mode; /* Wether or not the card is in command mode */ + int boot_status; /* The card's boot status */ + int io_addr; /* The cards I/O address */ + spinlock_t io_lock; /* the lock for io operations */ + struct timer_list timer; /* The timer that pings the watchdog */ + unsigned long next_heartbeat; /* the next_heartbeat for the timer */ +} pcwd_private; + +/* module parameters */ +#define QUIET 0 /* Default */ +#define VERBOSE 1 /* Verbose */ +#define DEBUG 2 /* print fancy stuff too */ +static int debug = QUIET; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); + +#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ +static int heartbeat = WATCHDOG_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<=heartbeat<=7200 or 0=delay-time from dip-switches, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); + +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) ")"); + +/* + * Internal functions + */ + +static int send_isa_command(int cmd) +{ + int i; + int control_status; + int port0, last_port0; /* Double read for stabilising */ + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n", + cmd); + + /* The WCMD bit must be 1 and the command is only 4 bits in size */ + control_status = (cmd & 0x0F) | WD_WCMD; + outb_p(control_status, pcwd_private.io_addr + 2); + udelay(ISA_COMMAND_TIMEOUT); + + port0 = inb_p(pcwd_private.io_addr); + for (i = 0; i < 25; ++i) { + last_port0 = port0; + port0 = inb_p(pcwd_private.io_addr); + + if (port0 == last_port0) + break; /* Data is stable */ + + udelay (250); + } + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n", + cmd, port0, last_port0); + + return port0; +} + +static int set_command_mode(void) +{ + int i, found=0, count=0; + + /* Set the card into command mode */ + spin_lock(&pcwd_private.io_lock); + while ((!found) && (count < 3)) { + i = send_isa_command(CMD_ISA_IDLE); + + if (i == 0x00) + found = 1; + else if (i == 0xF3) { + /* Card does not like what we've done to it */ + outb_p(0x00, pcwd_private.io_addr + 2); + udelay(1200); /* Spec says wait 1ms */ + outb_p(0x00, pcwd_private.io_addr + 2); + udelay(ISA_COMMAND_TIMEOUT); + } + count++; + } + spin_unlock(&pcwd_private.io_lock); + pcwd_private.command_mode = found; + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "command_mode=%d\n", + pcwd_private.command_mode); + + return(found); +} + +static void unset_command_mode(void) +{ + /* Set the card into normal mode */ + spin_lock(&pcwd_private.io_lock); + outb_p(0x00, pcwd_private.io_addr + 2); + udelay(ISA_COMMAND_TIMEOUT); + spin_unlock(&pcwd_private.io_lock); + + pcwd_private.command_mode = 0; + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "command_mode=%d\n", + pcwd_private.command_mode); +} + +static inline void pcwd_check_temperature_support(void) +{ + if (inb(pcwd_private.io_addr) != 0xF0) + pcwd_private.supports_temp = 1; +} + +static inline void pcwd_get_firmware(void) +{ + int one, ten, hund, minor; + + strcpy(pcwd_private.fw_ver_str, "ERROR"); + + if (set_command_mode()) { + one = send_isa_command(CMD_ISA_VERSION_INTEGER); + ten = send_isa_command(CMD_ISA_VERSION_TENTH); + hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); + minor = send_isa_command(CMD_ISA_VERSION_MINOR); + sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c", one, ten, hund, minor); + } + unset_command_mode(); + + return; +} + +static inline int pcwd_get_option_switches(void) +{ + int option_switches=0; + + if (set_command_mode()) { + /* Get switch settings */ + option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS); + } + + unset_command_mode(); + return(option_switches); +} + +static void pcwd_show_card_info(void) +{ + int option_switches; + + /* Get some extra info from the hardware (in command/debug/diag mode) */ + if (pcwd_private.revision == PCWD_REVISION_A) + printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr); + else if (pcwd_private.revision == PCWD_REVISION_C) { + pcwd_get_firmware(); + printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", + pcwd_private.io_addr, pcwd_private.fw_ver_str); + option_switches = pcwd_get_option_switches(); + printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", + option_switches, + ((option_switches & 0x10) ? "ON" : "OFF"), + ((option_switches & 0x08) ? "ON" : "OFF")); + + /* Reprogram internal heartbeat to 2 seconds */ + if (set_command_mode()) { + send_isa_command(CMD_ISA_DELAY_TIME_2SECS); + unset_command_mode(); + } + } + + if (pcwd_private.supports_temp) + printk(KERN_INFO PFX "Temperature Option Detected\n"); + + if (pcwd_private.boot_status & WDIOF_CARDRESET) + printk(KERN_INFO PFX "Previous reboot was caused by the card\n"); + + if (pcwd_private.boot_status & WDIOF_OVERHEAT) { + printk(KERN_EMERG PFX "Card senses a CPU Overheat. Panicking!\n"); + printk(KERN_EMERG PFX "CPU Overheat\n"); + } + + if (pcwd_private.boot_status == 0) + printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); +} + +static void pcwd_timer_ping(unsigned long data) +{ + int wdrst_stat; + + /* If we got a heartbeat pulse within the WDT_INTERVAL + * we agree to ping the WDT */ + if(time_before(jiffies, pcwd_private.next_heartbeat)) { + /* Ping the watchdog */ + spin_lock(&pcwd_private.io_lock); + if (pcwd_private.revision == PCWD_REVISION_A) { + /* Rev A cards are reset by setting the WD_WDRST bit in register 1 */ + wdrst_stat = inb_p(pcwd_private.io_addr); + wdrst_stat &= 0x0F; + wdrst_stat |= WD_WDRST; + + outb_p(wdrst_stat, pcwd_private.io_addr + 1); + } else { + /* Re-trigger watchdog by writing to port 0 */ + outb_p(0x00, pcwd_private.io_addr); + } + + /* Re-set the timer interval */ + mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL); + + spin_unlock(&pcwd_private.io_lock); + } else { + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); + } +} + +static int pcwd_start(void) +{ + int stat_reg; + + pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); + + /* Start the timer */ + mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL); + + /* Enable the port */ + if (pcwd_private.revision == PCWD_REVISION_C) { + spin_lock(&pcwd_private.io_lock); + outb_p(0x00, pcwd_private.io_addr + 3); + udelay(ISA_COMMAND_TIMEOUT); + stat_reg = inb_p(pcwd_private.io_addr + 2); + spin_unlock(&pcwd_private.io_lock); + if (stat_reg & WD_WDIS) { + printk(KERN_INFO PFX "Could not start watchdog\n"); + return -EIO; + } + } + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog started\n"); + + return 0; +} + +static int pcwd_stop(void) +{ + int stat_reg; + + /* Stop the timer */ + del_timer(&pcwd_private.timer); + + /* Disable the board */ + if (pcwd_private.revision == PCWD_REVISION_C) { + spin_lock(&pcwd_private.io_lock); + outb_p(0xA5, pcwd_private.io_addr + 3); + udelay(ISA_COMMAND_TIMEOUT); + outb_p(0xA5, pcwd_private.io_addr + 3); + udelay(ISA_COMMAND_TIMEOUT); + stat_reg = inb_p(pcwd_private.io_addr + 2); + spin_unlock(&pcwd_private.io_lock); + if ((stat_reg & WD_WDIS) == 0) { + printk(KERN_INFO PFX "Could not stop watchdog\n"); + return -EIO; + } + } + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog stopped\n"); + + return 0; +} + +static int pcwd_keepalive(void) +{ + /* user land ping */ + pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); + + return 0; +} + +static int pcwd_set_heartbeat(int t) +{ + if ((t < 2) || (t > 7200)) /* arbitrary upper limit */ + return -EINVAL; + + heartbeat = t; + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "New heartbeat: %d\n", + heartbeat); + + return 0; +} + +static int pcwd_get_status(int *status) +{ + int control_status; + + *status=0; + spin_lock(&pcwd_private.io_lock); + if (pcwd_private.revision == PCWD_REVISION_A) + /* Rev A cards return status information from + * the base register, which is used for the + * temperature in other cards. */ + control_status = inb(pcwd_private.io_addr); + else { + /* Rev C cards return card status in the base + * address + 1 register. And use different bits + * to indicate a card initiated reset, and an + * over-temperature condition. And the reboot + * status can be reset. */ + control_status = inb(pcwd_private.io_addr + 1); + } + spin_unlock(&pcwd_private.io_lock); + + if (pcwd_private.revision == PCWD_REVISION_A) { + if (control_status & WD_WDRST) + *status |= WDIOF_CARDRESET; + + if (control_status & WD_T110) { + *status |= WDIOF_OVERHEAT; + if (temp_panic) { + printk(KERN_INFO PFX "Temperature overheat trip!\n"); + kernel_power_off(); + /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ + } + } + } else { + if (control_status & WD_REVC_WTRP) + *status |= WDIOF_CARDRESET; + + if (control_status & WD_REVC_TTRP) { + *status |= WDIOF_OVERHEAT; + if (temp_panic) { + printk(KERN_INFO PFX "Temperature overheat trip!\n"); + kernel_power_off(); + /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ + } + } + } + + return 0; +} + +static int pcwd_clear_status(void) +{ + int control_status; + + if (pcwd_private.revision == PCWD_REVISION_C) { + spin_lock(&pcwd_private.io_lock); + + if (debug >= VERBOSE) + printk(KERN_INFO PFX "clearing watchdog trip status\n"); + + control_status = inb_p(pcwd_private.io_addr + 1); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); + printk(KERN_DEBUG PFX "sending: 0x%02x\n", + (control_status & WD_REVC_R2DS)); + } + + /* clear reset status & Keep Relay 2 disable state as it is */ + outb_p((control_status & WD_REVC_R2DS), pcwd_private.io_addr + 1); + + spin_unlock(&pcwd_private.io_lock); + } + return 0; +} + +static int pcwd_get_temperature(int *temperature) +{ + /* check that port 0 gives temperature info and no command results */ + if (pcwd_private.command_mode) + return -1; + + *temperature = 0; + if (!pcwd_private.supports_temp) + return -ENODEV; + + /* + * Convert celsius to fahrenheit, since this was + * the decided 'standard' for this return value. + */ + spin_lock(&pcwd_private.io_lock); + *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32; + spin_unlock(&pcwd_private.io_lock); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "temperature is: %d F\n", + *temperature); + } + + return 0; +} + +/* + * /dev/watchdog handling + */ + +static int pcwd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rv; + int status; + int temperature; + int new_heartbeat; + int __user *argp = (int __user *)arg; + static struct watchdog_info ident = { + .options = WDIOF_OVERHEAT | + WDIOF_CARDRESET | + WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "PCWD", + }; + + switch(cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + if(copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + pcwd_get_status(&status); + return put_user(status, argp); + + case WDIOC_GETBOOTSTATUS: + return put_user(pcwd_private.boot_status, argp); + + case WDIOC_GETTEMP: + if (pcwd_get_temperature(&temperature)) + return -EFAULT; + + return put_user(temperature, argp); + + case WDIOC_SETOPTIONS: + if (pcwd_private.revision == PCWD_REVISION_C) + { + if(copy_from_user(&rv, argp, sizeof(int))) + return -EFAULT; + + if (rv & WDIOS_DISABLECARD) + { + return pcwd_stop(); + } + + if (rv & WDIOS_ENABLECARD) + { + return pcwd_start(); + } + + if (rv & WDIOS_TEMPPANIC) + { + temp_panic = 1; + } + } + return -EINVAL; + + case WDIOC_KEEPALIVE: + pcwd_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, argp)) + return -EFAULT; + + if (pcwd_set_heartbeat(new_heartbeat)) + return -EINVAL; + + pcwd_keepalive(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, argp); + } + + return 0; +} + +static ssize_t pcwd_write(struct file *file, const char __user *buf, 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, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + pcwd_keepalive(); + } + return len; +} + +static int pcwd_open(struct inode *inode, struct file *file) +{ + if (!atomic_dec_and_test(&open_allowed) ) { + if (debug >= VERBOSE) + printk(KERN_ERR PFX "Attempt to open already opened device.\n"); + atomic_inc( &open_allowed ); + return -EBUSY; + } + + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate */ + pcwd_start(); + pcwd_keepalive(); + return nonseekable_open(inode, file); +} + +static int pcwd_close(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + pcwd_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + pcwd_keepalive(); + } + expect_close = 0; + atomic_inc( &open_allowed ); + return 0; +} + +/* + * /dev/temperature handling + */ + +static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + int temperature; + + if (pcwd_get_temperature(&temperature)) + return -EFAULT; + + if (copy_to_user(buf, &temperature, 1)) + return -EFAULT; + + return 1; +} + +static int pcwd_temp_open(struct inode *inode, struct file *file) +{ + if (!pcwd_private.supports_temp) + return -ENODEV; + + return nonseekable_open(inode, file); +} + +static int pcwd_temp_close(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations pcwd_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pcwd_write, + .ioctl = pcwd_ioctl, + .open = pcwd_open, + .release = pcwd_close, +}; + +static struct miscdevice pcwd_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pcwd_fops, +}; + +static const struct file_operations pcwd_temp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = pcwd_temp_read, + .open = pcwd_temp_open, + .release = pcwd_temp_close, +}; + +static struct miscdevice temp_miscdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &pcwd_temp_fops, +}; + +/* + * Init & exit routines + */ + +static inline int get_revision(void) +{ + int r = PCWD_REVISION_C; + + spin_lock(&pcwd_private.io_lock); + /* REV A cards use only 2 io ports; test + * presumes a floating bus reads as 0xff. */ + if ((inb(pcwd_private.io_addr + 2) == 0xFF) || + (inb(pcwd_private.io_addr + 3) == 0xFF)) + r=PCWD_REVISION_A; + spin_unlock(&pcwd_private.io_lock); + + return r; +} + +/* + * The ISA cards have a heartbeat bit in one of the registers, which + * register is card dependent. The heartbeat bit is monitored, and if + * found, is considered proof that a Berkshire card has been found. + * The initial rate is once per second at board start up, then twice + * per second for normal operation. + */ +static int __devinit pcwd_isa_match(struct device *dev, unsigned int id) +{ + int base_addr=pcwd_ioports[id]; + int port0, last_port0; /* Reg 0, in case it's REV A */ + int port1, last_port1; /* Register 1 for REV C cards */ + int i; + int retval; + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n", + id); + + if (!request_region (base_addr, 4, "PCWD")) { + printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr); + return 0; + } + + retval = 0; + + port0 = inb_p(base_addr); /* For REV A boards */ + port1 = inb_p(base_addr + 1); /* For REV C boards */ + if (port0 != 0xff || port1 != 0xff) { + /* Not an 'ff' from a floating bus, so must be a card! */ + for (i = 0; i < 4; ++i) { + + msleep(500); + + last_port0 = port0; + last_port1 = port1; + + port0 = inb_p(base_addr); + port1 = inb_p(base_addr + 1); + + /* Has either hearbeat bit changed? */ + if ((port0 ^ last_port0) & WD_HRTBT || + (port1 ^ last_port1) & WD_REVC_HRBT) { + retval = 1; + break; + } + } + } + release_region (base_addr, 4); + + return retval; +} + +static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id) +{ + int ret; + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n", + id); + + cards_found++; + if (cards_found == 1) + printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); + + if (cards_found > 1) { + printk(KERN_ERR PFX "This driver only supports 1 device\n"); + return -ENODEV; + } + + if (pcwd_ioports[id] == 0x0000) { + printk(KERN_ERR PFX "No I/O-Address for card detected\n"); + return -ENODEV; + } + pcwd_private.io_addr = pcwd_ioports[id]; + + spin_lock_init(&pcwd_private.io_lock); + + /* Check card's revision */ + pcwd_private.revision = get_revision(); + + if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + pcwd_private.io_addr); + ret=-EIO; + goto error_request_region; + } + + /* Initial variables */ + pcwd_private.supports_temp = 0; + temp_panic = 0; + pcwd_private.boot_status = 0x0000; + + /* get the boot_status */ + pcwd_get_status(&pcwd_private.boot_status); + + /* clear the "card caused reboot" flag */ + pcwd_clear_status(); + + setup_timer(&pcwd_private.timer, pcwd_timer_ping, 0); + + /* Disable the board */ + pcwd_stop(); + + /* Check whether or not the card supports the temperature device */ + pcwd_check_temperature_support(); + + /* Show info about the card itself */ + pcwd_show_card_info(); + + /* If heartbeat = 0 then we use the heartbeat from the dip-switches */ + if (heartbeat == 0) + heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)]; + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (pcwd_set_heartbeat(heartbeat)) { + pcwd_set_heartbeat(WATCHDOG_HEARTBEAT); + printk(KERN_INFO PFX "heartbeat value must be 2<=heartbeat<=7200, using %d\n", + WATCHDOG_HEARTBEAT); + } + + if (pcwd_private.supports_temp) { + ret = misc_register(&temp_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + TEMP_MINOR, ret); + goto error_misc_register_temp; + } + } + + ret = misc_register(&pcwd_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto error_misc_register_watchdog; + } + + printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", + heartbeat, nowayout); + + return 0; + +error_misc_register_watchdog: + if (pcwd_private.supports_temp) + misc_deregister(&temp_miscdev); +error_misc_register_temp: + release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); +error_request_region: + pcwd_private.io_addr = 0x0000; + cards_found--; + return ret; +} + +static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id) +{ + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n", + id); + + if (!pcwd_private.io_addr) + return 1; + + /* Disable the board */ + if (!nowayout) + pcwd_stop(); + + /* Deregister */ + misc_deregister(&pcwd_miscdev); + if (pcwd_private.supports_temp) + misc_deregister(&temp_miscdev); + release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); + pcwd_private.io_addr = 0x0000; + cards_found--; + + return 0; +} + +static void pcwd_isa_shutdown(struct device *dev, unsigned int id) +{ + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n", + id); + + pcwd_stop(); +} + +static struct isa_driver pcwd_isa_driver = { + .match = pcwd_isa_match, + .probe = pcwd_isa_probe, + .remove = __devexit_p(pcwd_isa_remove), + .shutdown = pcwd_isa_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = WATCHDOG_NAME, + }, +}; + +static int __init pcwd_init_module(void) +{ + return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS); +} + +static void __exit pcwd_cleanup_module(void) +{ + isa_unregister_driver(&pcwd_isa_driver); + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); +} + +module_init(pcwd_init_module); +module_exit(pcwd_cleanup_module); + +MODULE_AUTHOR("Ken Hollis , Wim Van Sebroeck "); +MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); +MODULE_VERSION(WATCHDOG_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS_MISCDEV(TEMP_MINOR); diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c new file mode 100644 index 0000000..61a89e9 --- /dev/null +++ b/drivers/watchdog/pcwd_pci.c @@ -0,0 +1,832 @@ +/* + * Berkshire PCI-PC Watchdog Card Driver + * + * (c) Copyright 2003-2007 Wim Van Sebroeck . + * + * Based on source code of the following authors: + * Ken Hollis , + * Lindsay Harris , + * Alan Cox , + * Matt Domsch , + * Rob Radez + * + * 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. + */ + +/* + * A bells and whistles driver is available from: + * http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/ + * + * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ + */ + +/* + * Includes, defines, variables, module parameters, ... + */ + +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For mdelay function */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For notifier support */ +#include /* For reboot_notifier stuff */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For pci functions */ +#include /* For io-port access */ +#include /* For spin_lock/spin_unlock/... */ + +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ + +/* Module and version information */ +#define WATCHDOG_VERSION "1.03" +#define WATCHDOG_DATE "21 Jan 2007" +#define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" +#define WATCHDOG_NAME "pcwd_pci" +#define PFX WATCHDOG_NAME ": " +#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " (" WATCHDOG_DATE ")\n" + +/* Stuff for the PCI ID's */ +#ifndef PCI_VENDOR_ID_QUICKLOGIC +#define PCI_VENDOR_ID_QUICKLOGIC 0x11e3 +#endif + +#ifndef PCI_DEVICE_ID_WATCHDOG_PCIPCWD +#define PCI_DEVICE_ID_WATCHDOG_PCIPCWD 0x5030 +#endif + +/* + * These are the defines that describe the control status bits for the + * PCI-PC Watchdog card. + */ +/* Port 1 : Control Status #1 */ +#define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ +#define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ +#define WD_PCI_TTRP 0x04 /* Temperature Trip status */ +#define WD_PCI_RL2A 0x08 /* Relay 2 Active */ +#define WD_PCI_RL1A 0x10 /* Relay 1 Active */ +#define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip/reset */ +#define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */ +/* Port 2 : Control Status #2 */ +#define WD_PCI_WDIS 0x10 /* Watchdog Disable */ +#define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */ +#define WD_PCI_WRSP 0x40 /* Watchdog wrote response */ +#define WD_PCI_PCMD 0x80 /* PC has sent command */ + +/* according to documentation max. time to process a command for the pci + * watchdog card is 100 ms, so we give it 150 ms to do it's job */ +#define PCI_COMMAND_TIMEOUT 150 + +/* Watchdog's internal commands */ +#define CMD_GET_STATUS 0x04 +#define CMD_GET_FIRMWARE_VERSION 0x08 +#define CMD_READ_WATCHDOG_TIMEOUT 0x18 +#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 +#define CMD_GET_CLEAR_RESET_COUNT 0x84 + +/* Watchdog's Dip Switch heartbeat values */ +static const int heartbeat_tbl [] = { + 5, /* OFF-OFF-OFF = 5 Sec */ + 10, /* OFF-OFF-ON = 10 Sec */ + 30, /* OFF-ON-OFF = 30 Sec */ + 60, /* OFF-ON-ON = 1 Min */ + 300, /* ON-OFF-OFF = 5 Min */ + 600, /* ON-OFF-ON = 10 Min */ + 1800, /* ON-ON-OFF = 30 Min */ + 3600, /* ON-ON-ON = 1 hour */ +}; + +/* We can only use 1 card due to the /dev/watchdog restriction */ +static int cards_found; + +/* internal variables */ +static int temp_panic; +static unsigned long is_active; +static char expect_release; +static struct { /* this is private data for each PCI-PC watchdog card */ + int supports_temp; /* Wether or not the card has a temperature device */ + int boot_status; /* The card's boot status */ + unsigned long io_addr; /* The cards I/O address */ + spinlock_t io_lock; /* the lock for io operations */ + struct pci_dev *pdev; /* the PCI-device */ +} pcipcwd_private; + +/* module parameters */ +#define QUIET 0 /* Default */ +#define VERBOSE 1 /* Verbose */ +#define DEBUG 2 /* print fancy stuff too */ +static int debug = QUIET; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); + +#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ +static int heartbeat = WATCHDOG_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0= DEBUG) + printk(KERN_DEBUG PFX "sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x\n", + cmd, *msb, *lsb); + + spin_lock(&pcipcwd_private.io_lock); + /* If a command requires data it should be written first. + * Data for commands with 8 bits of data should be written to port 4. + * Commands with 16 bits of data, should be written as LSB to port 4 + * and MSB to port 5. + * After the required data has been written then write the command to + * port 6. */ + outb_p(*lsb, pcipcwd_private.io_addr + 4); + outb_p(*msb, pcipcwd_private.io_addr + 5); + outb_p(cmd, pcipcwd_private.io_addr + 6); + + /* wait till the pci card processed the command, signaled by + * the WRSP bit in port 2 and give it a max. timeout of + * PCI_COMMAND_TIMEOUT to process */ + got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; + for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response); count++) { + mdelay(1); + got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; + } + + if (debug >= DEBUG) { + if (got_response) { + printk(KERN_DEBUG PFX "time to process command was: %d ms\n", + count); + } else { + printk(KERN_DEBUG PFX "card did not respond on command!\n"); + } + } + + if (got_response) { + /* read back response */ + *lsb = inb_p(pcipcwd_private.io_addr + 4); + *msb = inb_p(pcipcwd_private.io_addr + 5); + + /* clear WRSP bit */ + inb_p(pcipcwd_private.io_addr + 6); + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n", + cmd, *msb, *lsb); + } + + spin_unlock(&pcipcwd_private.io_lock); + + return got_response; +} + +static inline void pcipcwd_check_temperature_support(void) +{ + if (inb_p(pcipcwd_private.io_addr) != 0xF0) + pcipcwd_private.supports_temp = 1; +} + +static int pcipcwd_get_option_switches(void) +{ + int option_switches; + + option_switches = inb_p(pcipcwd_private.io_addr + 3); + return option_switches; +} + +static void pcipcwd_show_card_info(void) +{ + int got_fw_rev, fw_rev_major, fw_rev_minor; + char fw_ver_str[20]; /* The cards firmware version */ + int option_switches; + + got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); + if (got_fw_rev) { + sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); + } else { + sprintf(fw_ver_str, ""); + } + + /* Get switch settings */ + option_switches = pcipcwd_get_option_switches(); + + printk(KERN_INFO PFX "Found card at port 0x%04x (Firmware: %s) %s temp option\n", + (int) pcipcwd_private.io_addr, fw_ver_str, + (pcipcwd_private.supports_temp ? "with" : "without")); + + printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", + option_switches, + ((option_switches & 0x10) ? "ON" : "OFF"), + ((option_switches & 0x08) ? "ON" : "OFF")); + + if (pcipcwd_private.boot_status & WDIOF_CARDRESET) + printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card\n"); + + if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) + printk(KERN_INFO PFX "Card sensed a CPU Overheat\n"); + + if (pcipcwd_private.boot_status == 0) + printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); +} + +static int pcipcwd_start(void) +{ + int stat_reg; + + spin_lock(&pcipcwd_private.io_lock); + outb_p(0x00, pcipcwd_private.io_addr + 3); + udelay(1000); + + stat_reg = inb_p(pcipcwd_private.io_addr + 2); + spin_unlock(&pcipcwd_private.io_lock); + + if (stat_reg & WD_PCI_WDIS) { + printk(KERN_ERR PFX "Card timer not enabled\n"); + return -1; + } + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog started\n"); + + return 0; +} + +static int pcipcwd_stop(void) +{ + int stat_reg; + + spin_lock(&pcipcwd_private.io_lock); + outb_p(0xA5, pcipcwd_private.io_addr + 3); + udelay(1000); + + outb_p(0xA5, pcipcwd_private.io_addr + 3); + udelay(1000); + + stat_reg = inb_p(pcipcwd_private.io_addr + 2); + spin_unlock(&pcipcwd_private.io_lock); + + if (!(stat_reg & WD_PCI_WDIS)) { + printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); + return -1; + } + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog stopped\n"); + + return 0; +} + +static int pcipcwd_keepalive(void) +{ + /* Re-trigger watchdog by writing to port 0 */ + spin_lock(&pcipcwd_private.io_lock); + outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */ + spin_unlock(&pcipcwd_private.io_lock); + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); + + return 0; +} + +static int pcipcwd_set_heartbeat(int t) +{ + int t_msb = t / 256; + int t_lsb = t % 256; + + if ((t < 0x0001) || (t > 0xFFFF)) + return -EINVAL; + + /* Write new heartbeat to watchdog */ + send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb); + + heartbeat = t; + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "New heartbeat: %d\n", + heartbeat); + + return 0; +} + +static int pcipcwd_get_status(int *status) +{ + int control_status; + + *status=0; + control_status = inb_p(pcipcwd_private.io_addr + 1); + if (control_status & WD_PCI_WTRP) + *status |= WDIOF_CARDRESET; + if (control_status & WD_PCI_TTRP) { + *status |= WDIOF_OVERHEAT; + if (temp_panic) + panic(PFX "Temperature overheat trip!\n"); + } + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "Control Status #1: 0x%02x\n", + control_status); + + return 0; +} + +static int pcipcwd_clear_status(void) +{ + int control_status; + int msb; + int reset_counter; + + if (debug >= VERBOSE) + printk(KERN_INFO PFX "clearing watchdog trip status & LED\n"); + + control_status = inb_p(pcipcwd_private.io_addr + 1); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); + printk(KERN_DEBUG PFX "sending: 0x%02x\n", + (control_status & WD_PCI_R2DS) | WD_PCI_WTRP); + } + + /* clear trip status & LED and keep mode of relay 2 */ + outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP, pcipcwd_private.io_addr + 1); + + /* clear reset counter */ + msb=0; + reset_counter=0xff; + send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "reset count was: 0x%02x\n", + reset_counter); + } + + return 0; +} + +static int pcipcwd_get_temperature(int *temperature) +{ + *temperature = 0; + if (!pcipcwd_private.supports_temp) + return -ENODEV; + + spin_lock(&pcipcwd_private.io_lock); + *temperature = inb_p(pcipcwd_private.io_addr); + spin_unlock(&pcipcwd_private.io_lock); + + /* + * Convert celsius to fahrenheit, since this was + * the decided 'standard' for this return value. + */ + *temperature = (*temperature * 9 / 5) + 32; + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "temperature is: %d F\n", + *temperature); + } + + return 0; +} + +static int pcipcwd_get_timeleft(int *time_left) +{ + int msb; + int lsb; + + /* Read the time that's left before rebooting */ + /* Note: if the board is not yet armed then we will read 0xFFFF */ + send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); + + *time_left = (msb << 8) + lsb; + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Time left before next reboot: %d\n", + *time_left); + + return 0; +} + +/* + * /dev/watchdog handling + */ + +static ssize_t pcipcwd_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 */ + pcipcwd_keepalive(); + } + return len; +} + +static int pcipcwd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_OVERHEAT | + WDIOF_CARDRESET | + WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = WATCHDOG_DRIVER_NAME, + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, + sizeof (ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + { + int status; + + pcipcwd_get_status(&status); + + return put_user(status, p); + } + + case WDIOC_GETBOOTSTATUS: + return put_user(pcipcwd_private.boot_status, p); + + case WDIOC_GETTEMP: + { + int temperature; + + if (pcipcwd_get_temperature(&temperature)) + return -EFAULT; + + return put_user(temperature, p); + } + + case WDIOC_KEEPALIVE: + pcipcwd_keepalive(); + return 0; + + case WDIOC_SETOPTIONS: + { + int new_options, retval = -EINVAL; + + if (get_user (new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + if (pcipcwd_stop()) + return -EIO; + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + if (pcipcwd_start()) + return -EIO; + retval = 0; + } + + if (new_options & WDIOS_TEMPPANIC) { + temp_panic = 1; + retval = 0; + } + + return retval; + } + + case WDIOC_SETTIMEOUT: + { + int new_heartbeat; + + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (pcipcwd_set_heartbeat(new_heartbeat)) + return -EINVAL; + + pcipcwd_keepalive(); + /* Fall */ + } + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + + case WDIOC_GETTIMELEFT: + { + int time_left; + + if (pcipcwd_get_timeleft(&time_left)) + return -EFAULT; + + return put_user(time_left, p); + } + + default: + return -ENOTTY; + } +} + +static int pcipcwd_open(struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &is_active)) { + if (debug >= VERBOSE) + printk(KERN_ERR PFX "Attempt to open already opened device.\n"); + return -EBUSY; + } + + /* Activate */ + pcipcwd_start(); + pcipcwd_keepalive(); + return nonseekable_open(inode, file); +} + +static int pcipcwd_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + */ + if (expect_release == 42) { + pcipcwd_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + pcipcwd_keepalive(); + } + expect_release = 0; + clear_bit(0, &is_active); + return 0; +} + +/* + * /dev/temperature handling + */ + +static ssize_t pcipcwd_temp_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + int temperature; + + if (pcipcwd_get_temperature(&temperature)) + return -EFAULT; + + if (copy_to_user (data, &temperature, 1)) + return -EFAULT; + + return 1; +} + +static int pcipcwd_temp_open(struct inode *inode, struct file *file) +{ + if (!pcipcwd_private.supports_temp) + return -ENODEV; + + return nonseekable_open(inode, file); +} + +static int pcipcwd_temp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * Notify system + */ + +static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) { + /* Turn the WDT off */ + pcipcwd_stop(); + } + + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations pcipcwd_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pcipcwd_write, + .ioctl = pcipcwd_ioctl, + .open = pcipcwd_open, + .release = pcipcwd_release, +}; + +static struct miscdevice pcipcwd_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pcipcwd_fops, +}; + +static const struct file_operations pcipcwd_temp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = pcipcwd_temp_read, + .open = pcipcwd_temp_open, + .release = pcipcwd_temp_release, +}; + +static struct miscdevice pcipcwd_temp_miscdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &pcipcwd_temp_fops, +}; + +static struct notifier_block pcipcwd_notifier = { + .notifier_call = pcipcwd_notify_sys, +}; + +/* + * Init & exit routines + */ + +static int __devinit pcipcwd_card_init(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret = -EIO; + + cards_found++; + if (cards_found == 1) + printk(KERN_INFO PFX DRIVER_VERSION); + + if (cards_found > 1) { + printk(KERN_ERR PFX "This driver only supports 1 device\n"); + return -ENODEV; + } + + if (pci_enable_device(pdev)) { + printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); + return -ENODEV; + } + + if (pci_resource_start(pdev, 0) == 0x0000) { + printk(KERN_ERR PFX "No I/O-Address for card detected\n"); + ret = -ENODEV; + goto err_out_disable_device; + } + + pcipcwd_private.pdev = pdev; + pcipcwd_private.io_addr = pci_resource_start(pdev, 0); + + if (pci_request_regions(pdev, WATCHDOG_NAME)) { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + (int) pcipcwd_private.io_addr); + ret = -EIO; + goto err_out_disable_device; + } + + /* get the boot_status */ + pcipcwd_get_status(&pcipcwd_private.boot_status); + + /* clear the "card caused reboot" flag */ + pcipcwd_clear_status(); + + /* disable card */ + pcipcwd_stop(); + + /* Check whether or not the card supports the temperature device */ + pcipcwd_check_temperature_support(); + + /* Show info about the card itself */ + pcipcwd_show_card_info(); + + /* If heartbeat = 0 then we use the heartbeat from the dip-switches */ + if (heartbeat == 0) + heartbeat = heartbeat_tbl[(pcipcwd_get_option_switches() & 0x07)]; + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (pcipcwd_set_heartbeat(heartbeat)) { + pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT); + printk(KERN_INFO PFX "heartbeat value must be 0"); +MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS_MISCDEV(TEMP_MINOR); diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c new file mode 100644 index 0000000..0f3fd6c --- /dev/null +++ b/drivers/watchdog/pcwd_usb.c @@ -0,0 +1,824 @@ +/* + * Berkshire USB-PC Watchdog Card Driver + * + * (c) Copyright 2004-2007 Wim Van Sebroeck . + * + * Based on source code of the following authors: + * Ken Hollis , + * Alan Cox , + * Matt Domsch , + * Rob Radez , + * Greg Kroah-Hartman + * + * 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. + * + * Thanks also to Simon Machell at Berkshire Products Inc. for + * providing the test hardware. More info is available at + * http://www.berkprod.com/ or http://www.pcwatchdog.com/ + */ + +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For mdelay function */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For notifier support */ +#include /* For reboot_notifier stuff */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For USB functions */ +#include /* For kmalloc, ... */ +#include /* For mutex locking */ +#include /* For HID_REQ_SET_REPORT & HID_DT_REPORT */ + +#include /* For copy_to_user/put_user/... */ + + +#ifdef CONFIG_USB_DEBUG + static int debug = 1; +#else + static int debug; +#endif + +/* Use our own dbg macro */ +#undef dbg +#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG PFX format "\n" , ## arg); } while (0) + + +/* Module and Version Information */ +#define DRIVER_VERSION "1.02" +#define DRIVER_DATE "21 Jan 2007" +#define DRIVER_AUTHOR "Wim Van Sebroeck " +#define DRIVER_DESC "Berkshire USB-PC Watchdog driver" +#define DRIVER_LICENSE "GPL" +#define DRIVER_NAME "pcwd_usb" +#define PFX DRIVER_NAME ": " + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS_MISCDEV(TEMP_MINOR); + +/* Module Parameters */ +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + +#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ +static int heartbeat = WATCHDOG_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0context; + unsigned char *data = usb_pcwd->intr_buffer; + int retval; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto resubmit; + } + + dbg("received following data cmd=0x%02x msb=0x%02x lsb=0x%02x", + data[0], data[1], data[2]); + + usb_pcwd->cmd_command = data[0]; + usb_pcwd->cmd_data_msb = data[1]; + usb_pcwd->cmd_data_lsb = data[2]; + + /* notify anyone waiting that the cmd has finished */ + atomic_set (&usb_pcwd->cmd_received, 1); + +resubmit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + printk(KERN_ERR PFX "can't resubmit intr, usb_submit_urb failed with result %d\n", + retval); +} + +static int usb_pcwd_send_command(struct usb_pcwd_private *usb_pcwd, unsigned char cmd, + unsigned char *msb, unsigned char *lsb) +{ + int got_response, count; + unsigned char buf[6]; + + /* We will not send any commands if the USB PCWD device does not exist */ + if ((!usb_pcwd) || (!usb_pcwd->exists)) + return -1; + + /* The USB PC Watchdog uses a 6 byte report format. The board currently uses + * only 3 of the six bytes of the report. */ + buf[0] = cmd; /* Byte 0 = CMD */ + buf[1] = *msb; /* Byte 1 = Data MSB */ + buf[2] = *lsb; /* Byte 2 = Data LSB */ + buf[3] = buf[4] = buf[5] = 0; /* All other bytes not used */ + + dbg("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x", + buf[0], buf[1], buf[2]); + + atomic_set (&usb_pcwd->cmd_received, 0); + + if (usb_control_msg(usb_pcwd->udev, usb_sndctrlpipe(usb_pcwd->udev, 0), + HID_REQ_SET_REPORT, HID_DT_REPORT, + 0x0200, usb_pcwd->interface_number, buf, sizeof(buf), + USB_COMMAND_TIMEOUT) != sizeof(buf)) { + dbg("usb_pcwd_send_command: error in usb_control_msg for cmd 0x%x 0x%x 0x%x\n", cmd, *msb, *lsb); + } + /* wait till the usb card processed the command, + * with a max. timeout of USB_COMMAND_TIMEOUT */ + got_response = 0; + for (count = 0; (count < USB_COMMAND_TIMEOUT) && (!got_response); count++) { + mdelay(1); + if (atomic_read (&usb_pcwd->cmd_received)) + got_response = 1; + } + + if ((got_response) && (cmd == usb_pcwd->cmd_command)) { + /* read back response */ + *msb = usb_pcwd->cmd_data_msb; + *lsb = usb_pcwd->cmd_data_lsb; + } + + return got_response; +} + +static int usb_pcwd_start(struct usb_pcwd_private *usb_pcwd) +{ + unsigned char msb = 0x00; + unsigned char lsb = 0x00; + int retval; + + /* Enable Watchdog */ + retval = usb_pcwd_send_command(usb_pcwd, CMD_ENABLE_WATCHDOG, &msb, &lsb); + + if ((retval == 0) || (lsb == 0)) { + printk(KERN_ERR PFX "Card did not acknowledge enable attempt\n"); + return -1; + } + + return 0; +} + +static int usb_pcwd_stop(struct usb_pcwd_private *usb_pcwd) +{ + unsigned char msb = 0xA5; + unsigned char lsb = 0xC3; + int retval; + + /* Disable Watchdog */ + retval = usb_pcwd_send_command(usb_pcwd, CMD_DISABLE_WATCHDOG, &msb, &lsb); + + if ((retval == 0) || (lsb != 0)) { + printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); + return -1; + } + + return 0; +} + +static int usb_pcwd_keepalive(struct usb_pcwd_private *usb_pcwd) +{ + unsigned char dummy; + + /* Re-trigger Watchdog */ + usb_pcwd_send_command(usb_pcwd, CMD_TRIGGER, &dummy, &dummy); + + return 0; +} + +static int usb_pcwd_set_heartbeat(struct usb_pcwd_private *usb_pcwd, int t) +{ + unsigned char msb = t / 256; + unsigned char lsb = t % 256; + + if ((t < 0x0001) || (t > 0xFFFF)) + return -EINVAL; + + /* Write new heartbeat to watchdog */ + usb_pcwd_send_command(usb_pcwd, CMD_WRITE_WATCHDOG_TIMEOUT, &msb, &lsb); + + heartbeat = t; + return 0; +} + +static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temperature) +{ + unsigned char msb, lsb; + + usb_pcwd_send_command(usb_pcwd, CMD_READ_TEMP, &msb, &lsb); + + /* + * Convert celsius to fahrenheit, since this was + * the decided 'standard' for this return value. + */ + *temperature = (lsb * 9 / 5) + 32; + + return 0; +} + +static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd, int *time_left) +{ + unsigned char msb, lsb; + + /* Read the time that's left before rebooting */ + /* Note: if the board is not yet armed then we will read 0xFFFF */ + usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); + + *time_left = (msb << 8) + lsb; + + return 0; +} + +/* + * /dev/watchdog handling + */ + +static ssize_t usb_pcwd_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 */ + usb_pcwd_keepalive(usb_pcwd_device); + } + return len; +} + +static int usb_pcwd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = DRIVER_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_GETTEMP: + { + int temperature; + + if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature)) + return -EFAULT; + + return put_user(temperature, p); + } + + case WDIOC_KEEPALIVE: + usb_pcwd_keepalive(usb_pcwd_device); + return 0; + + case WDIOC_SETOPTIONS: + { + int new_options, retval = -EINVAL; + + if (get_user (new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + usb_pcwd_stop(usb_pcwd_device); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + usb_pcwd_start(usb_pcwd_device); + retval = 0; + } + + return retval; + } + + case WDIOC_SETTIMEOUT: + { + int new_heartbeat; + + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (usb_pcwd_set_heartbeat(usb_pcwd_device, new_heartbeat)) + return -EINVAL; + + usb_pcwd_keepalive(usb_pcwd_device); + /* Fall */ + } + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + + case WDIOC_GETTIMELEFT: + { + int time_left; + + if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left)) + return -EFAULT; + + return put_user(time_left, p); + } + + default: + return -ENOTTY; + } +} + +static int usb_pcwd_open(struct inode *inode, struct file *file) +{ + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &is_active)) + return -EBUSY; + + /* Activate */ + usb_pcwd_start(usb_pcwd_device); + usb_pcwd_keepalive(usb_pcwd_device); + return nonseekable_open(inode, file); +} + +static int usb_pcwd_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + */ + if (expect_release == 42) { + usb_pcwd_stop(usb_pcwd_device); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + usb_pcwd_keepalive(usb_pcwd_device); + } + expect_release = 0; + clear_bit(0, &is_active); + return 0; +} + +/* + * /dev/temperature handling + */ + +static ssize_t usb_pcwd_temperature_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + int temperature; + + if (usb_pcwd_get_temperature(usb_pcwd_device, &temperature)) + return -EFAULT; + + if (copy_to_user(data, &temperature, 1)) + return -EFAULT; + + return 1; +} + +static int usb_pcwd_temperature_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int usb_pcwd_temperature_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * Notify system + */ + +static int usb_pcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) { + /* Turn the WDT off */ + usb_pcwd_stop(usb_pcwd_device); + } + + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations usb_pcwd_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = usb_pcwd_write, + .ioctl = usb_pcwd_ioctl, + .open = usb_pcwd_open, + .release = usb_pcwd_release, +}; + +static struct miscdevice usb_pcwd_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &usb_pcwd_fops, +}; + +static const struct file_operations usb_pcwd_temperature_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = usb_pcwd_temperature_read, + .open = usb_pcwd_temperature_open, + .release = usb_pcwd_temperature_release, +}; + +static struct miscdevice usb_pcwd_temperature_miscdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &usb_pcwd_temperature_fops, +}; + +static struct notifier_block usb_pcwd_notifier = { + .notifier_call = usb_pcwd_notify_sys, +}; + +/** + * usb_pcwd_delete + */ +static inline void usb_pcwd_delete (struct usb_pcwd_private *usb_pcwd) +{ + usb_free_urb(usb_pcwd->intr_urb); + if (usb_pcwd->intr_buffer != NULL) + usb_buffer_free(usb_pcwd->udev, usb_pcwd->intr_size, + usb_pcwd->intr_buffer, usb_pcwd->intr_dma); + kfree (usb_pcwd); +} + +/** + * usb_pcwd_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int usb_pcwd_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct usb_pcwd_private *usb_pcwd = NULL; + int pipe, maxp; + int retval = -ENOMEM; + int got_fw_rev; + unsigned char fw_rev_major, fw_rev_minor; + char fw_ver_str[20]; + unsigned char option_switches, dummy; + + cards_found++; + if (cards_found > 1) { + printk(KERN_ERR PFX "This driver only supports 1 device\n"); + return -ENODEV; + } + + /* get the active interface descriptor */ + iface_desc = interface->cur_altsetting; + + /* check out that we have a HID device */ + if (!(iface_desc->desc.bInterfaceClass == USB_CLASS_HID)) { + printk(KERN_ERR PFX "The device isn't a Human Interface Device\n"); + return -ENODEV; + } + + /* check out the endpoint: it has to be Interrupt & IN */ + endpoint = &iface_desc->endpoint[0].desc; + + if (!((endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT))) { + /* we didn't find a Interrupt endpoint with direction IN */ + printk(KERN_ERR PFX "Couldn't find an INTR & IN endpoint\n"); + return -ENODEV; + } + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + /* allocate memory for our device and initialize it */ + usb_pcwd = kzalloc (sizeof(struct usb_pcwd_private), GFP_KERNEL); + if (usb_pcwd == NULL) { + printk(KERN_ERR PFX "Out of memory\n"); + goto error; + } + + usb_pcwd_device = usb_pcwd; + + mutex_init(&usb_pcwd->mtx); + usb_pcwd->udev = udev; + usb_pcwd->interface = interface; + usb_pcwd->interface_number = iface_desc->desc.bInterfaceNumber; + usb_pcwd->intr_size = (le16_to_cpu(endpoint->wMaxPacketSize) > 8 ? le16_to_cpu(endpoint->wMaxPacketSize) : 8); + + /* set up the memory buffer's */ + if (!(usb_pcwd->intr_buffer = usb_buffer_alloc(udev, usb_pcwd->intr_size, GFP_ATOMIC, &usb_pcwd->intr_dma))) { + printk(KERN_ERR PFX "Out of memory\n"); + goto error; + } + + /* allocate the urb's */ + usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!usb_pcwd->intr_urb) { + printk(KERN_ERR PFX "Out of memory\n"); + goto error; + } + + /* initialise the intr urb's */ + usb_fill_int_urb(usb_pcwd->intr_urb, udev, pipe, + usb_pcwd->intr_buffer, usb_pcwd->intr_size, + usb_pcwd_intr_done, usb_pcwd, endpoint->bInterval); + usb_pcwd->intr_urb->transfer_dma = usb_pcwd->intr_dma; + usb_pcwd->intr_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* register our interrupt URB with the USB system */ + if (usb_submit_urb(usb_pcwd->intr_urb, GFP_KERNEL)) { + printk(KERN_ERR PFX "Problem registering interrupt URB\n"); + retval = -EIO; /* failure */ + goto error; + } + + /* The device exists and can be communicated with */ + usb_pcwd->exists = 1; + + /* disable card */ + usb_pcwd_stop(usb_pcwd); + + /* Get the Firmware Version */ + got_fw_rev = usb_pcwd_send_command(usb_pcwd, CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); + if (got_fw_rev) { + sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); + } else { + sprintf(fw_ver_str, ""); + } + + printk(KERN_INFO PFX "Found card (Firmware: %s) with temp option\n", + fw_ver_str); + + /* Get switch settings */ + usb_pcwd_send_command(usb_pcwd, CMD_GET_DIP_SWITCH_SETTINGS, &dummy, &option_switches); + + printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", + option_switches, + ((option_switches & 0x10) ? "ON" : "OFF"), + ((option_switches & 0x08) ? "ON" : "OFF")); + + /* If heartbeat = 0 then we use the heartbeat from the dip-switches */ + if (heartbeat == 0) + heartbeat = heartbeat_tbl[(option_switches & 0x07)]; + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (usb_pcwd_set_heartbeat(usb_pcwd, heartbeat)) { + usb_pcwd_set_heartbeat(usb_pcwd, WATCHDOG_HEARTBEAT); + printk(KERN_INFO PFX "heartbeat value must be 0udev. + */ +static void usb_pcwd_disconnect(struct usb_interface *interface) +{ + struct usb_pcwd_private *usb_pcwd; + + /* prevent races with open() */ + mutex_lock(&disconnect_mutex); + + usb_pcwd = usb_get_intfdata (interface); + usb_set_intfdata (interface, NULL); + + mutex_lock(&usb_pcwd->mtx); + + /* Stop the timer before we leave */ + if (!nowayout) + usb_pcwd_stop(usb_pcwd); + + /* We should now stop communicating with the USB PCWD device */ + usb_pcwd->exists = 0; + + /* Deregister */ + misc_deregister(&usb_pcwd_miscdev); + misc_deregister(&usb_pcwd_temperature_miscdev); + unregister_reboot_notifier(&usb_pcwd_notifier); + + mutex_unlock(&usb_pcwd->mtx); + + /* Delete the USB PCWD device */ + usb_pcwd_delete(usb_pcwd); + + cards_found--; + + mutex_unlock(&disconnect_mutex); + + printk(KERN_INFO PFX "USB PC Watchdog disconnected\n"); +} + + + +/** + * usb_pcwd_init + */ +static int __init usb_pcwd_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&usb_pcwd_driver); + if (result) { + printk(KERN_ERR PFX "usb_register failed. Error number %d\n", + result); + return result; + } + + printk(KERN_INFO PFX DRIVER_DESC " v" DRIVER_VERSION " (" DRIVER_DATE ")\n"); + return 0; +} + + +/** + * usb_pcwd_exit + */ +static void __exit usb_pcwd_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&usb_pcwd_driver); +} + + +module_init (usb_pcwd_init); +module_exit (usb_pcwd_exit); diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c new file mode 100644 index 0000000..22f8873 --- /dev/null +++ b/drivers/watchdog/pnx4008_wdt.c @@ -0,0 +1,358 @@ +/* + * drivers/char/watchdog/pnx4008_wdt.c + * + * Watchdog driver for PNX4008 board + * + * Authors: Dmitry Chigirev , + * Vitaly Wool + * Based on sa1100 driver, + * Copyright (C) 2000 Oleg Drokin + * + * 2005-2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MODULE_NAME "PNX4008-WDT: " + +/* WatchDog Timer - Chapter 23 Page 207 */ + +#define DEFAULT_HEARTBEAT 19 +#define MAX_HEARTBEAT 60 + +/* Watchdog timer register set definition */ +#define WDTIM_INT(p) ((p) + 0x0) +#define WDTIM_CTRL(p) ((p) + 0x4) +#define WDTIM_COUNTER(p) ((p) + 0x8) +#define WDTIM_MCTRL(p) ((p) + 0xC) +#define WDTIM_MATCH0(p) ((p) + 0x10) +#define WDTIM_EMR(p) ((p) + 0x14) +#define WDTIM_PULSE(p) ((p) + 0x18) +#define WDTIM_RES(p) ((p) + 0x1C) + +/* WDTIM_INT bit definitions */ +#define MATCH_INT 1 + +/* WDTIM_CTRL bit definitions */ +#define COUNT_ENAB 1 +#define RESET_COUNT (1<<1) +#define DEBUG_EN (1<<2) + +/* WDTIM_MCTRL bit definitions */ +#define MR0_INT 1 +#undef RESET_COUNT0 +#define RESET_COUNT0 (1<<2) +#define STOP_COUNT0 (1<<2) +#define M_RES1 (1<<3) +#define M_RES2 (1<<4) +#define RESFRC1 (1<<5) +#define RESFRC2 (1<<6) + +/* WDTIM_EMR bit definitions */ +#define EXT_MATCH0 1 +#define MATCH_OUTPUT_HIGH (2<<4) /*a MATCH_CTRL setting */ + +/* WDTIM_RES bit definitions */ +#define WDOG_RESET 1 /* read only */ + +#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */ + +static int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = DEFAULT_HEARTBEAT; + +static spinlock_t io_lock; +static unsigned long wdt_status; +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 +#define WDT_REGION_INITED 2 +#define WDT_DEVICE_INITED 3 + +static unsigned long boot_status; + +static struct resource *wdt_mem; +static void __iomem *wdt_base; +struct clk *wdt_clk; + +static void wdt_enable(void) +{ + spin_lock(&io_lock); + + if (wdt_clk) + clk_set_rate(wdt_clk, 1); + + /* stop counter, initiate counter reset */ + __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base)); + /*wait for reset to complete. 100% guarantee event */ + while (__raw_readl(WDTIM_COUNTER(wdt_base))) + cpu_relax(); + /* internal and external reset, stop after that */ + __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0, + WDTIM_MCTRL(wdt_base)); + /* configure match output */ + __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base)); + /* clear interrupt, just in case */ + __raw_writel(MATCH_INT, WDTIM_INT(wdt_base)); + /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */ + __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base)); + __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base)); + /*enable counter, stop when debugger active */ + __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base)); + + spin_unlock(&io_lock); +} + +static void wdt_disable(void) +{ + spin_lock(&io_lock); + + __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */ + if (wdt_clk) + clk_set_rate(wdt_clk, 0); + + spin_unlock(&io_lock); +} + +static int pnx4008_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_enable(); + + return nonseekable_open(inode, file); +} + +static ssize_t +pnx4008_wdt_write(struct file *file, const char *data, size_t len, + loff_t * ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "PNX4008 Watchdog", +}; + +static int +pnx4008_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int *)arg); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > MAX_HEARTBEAT) { + ret = -EINVAL; + break; + } + + heartbeat = time; + wdt_enable(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + } + return ret; +} + +static int pnx4008_wdt_release(struct inode *inode, struct file *file) +{ + if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n"); + + wdt_disable(); + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static const struct file_operations pnx4008_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pnx4008_wdt_write, + .ioctl = pnx4008_wdt_ioctl, + .open = pnx4008_wdt_open, + .release = pnx4008_wdt_release, +}; + +static struct miscdevice pnx4008_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pnx4008_wdt_fops, +}; + +static int pnx4008_wdt_probe(struct platform_device *pdev) +{ + int ret = 0, size; + struct resource *res; + + spin_lock_init(&io_lock); + + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; + + printk(KERN_INFO MODULE_NAME + "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk(KERN_INFO MODULE_NAME + "failed to get memory region resouce\n"); + return -ENOENT; + } + + size = res->end - res->start + 1; + wdt_mem = request_mem_region(res->start, size, pdev->name); + + if (wdt_mem == NULL) { + printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); + return -ENOENT; + } + wdt_base = (void __iomem *)IO_ADDRESS(res->start); + + wdt_clk = clk_get(&pdev->dev, "wdt_ck"); + if (IS_ERR(wdt_clk)) { + ret = PTR_ERR(wdt_clk); + release_resource(wdt_mem); + kfree(wdt_mem); + goto out; + } else + clk_set_rate(wdt_clk, 1); + + ret = misc_register(&pnx4008_wdt_miscdev); + if (ret < 0) { + printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); + release_resource(wdt_mem); + kfree(wdt_mem); + clk_set_rate(wdt_clk, 0); + } else { + boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ? + WDIOF_CARDRESET : 0; + wdt_disable(); /*disable for now */ + set_bit(WDT_DEVICE_INITED, &wdt_status); + } + +out: + return ret; +} + +static int pnx4008_wdt_remove(struct platform_device *pdev) +{ + misc_deregister(&pnx4008_wdt_miscdev); + if (wdt_clk) { + clk_set_rate(wdt_clk, 0); + clk_put(wdt_clk); + wdt_clk = NULL; + } + if (wdt_mem) { + release_resource(wdt_mem); + kfree(wdt_mem); + wdt_mem = NULL; + } + return 0; +} + +static struct platform_driver platform_wdt_driver = { + .driver = { + .name = "watchdog", + }, + .probe = pnx4008_wdt_probe, + .remove = pnx4008_wdt_remove, +}; + +static int __init pnx4008_wdt_init(void) +{ + return platform_driver_register(&platform_wdt_driver); +} + +static void __exit pnx4008_wdt_exit(void) +{ + return platform_driver_unregister(&platform_wdt_driver); +} + +module_init(pnx4008_wdt_init); +module_exit(pnx4008_wdt_exit); + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("PNX4008 Watchdog Driver"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(MAX_HEARTBEAT) ", default " + __MODULE_STRING(DEFAULT_HEARTBEAT)); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Set to 1 to keep watchdog running after device release"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/rm9k_wdt.c b/drivers/watchdog/rm9k_wdt.c new file mode 100644 index 0000000..5c921e4 --- /dev/null +++ b/drivers/watchdog/rm9k_wdt.c @@ -0,0 +1,420 @@ +/* + * Watchdog implementation for GPI h/w found on PMC-Sierra RM9xxx + * chips. + * + * Copyright (C) 2004 by Basler Vision Technologies AG + * Author: Thomas Koeller + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define CLOCK 125000000 +#define MAX_TIMEOUT_SECONDS 32 +#define CPCCR 0x0080 +#define CPGIG1SR 0x0044 +#define CPGIG1ER 0x0054 + + +/* Function prototypes */ +static irqreturn_t wdt_gpi_irqhdl(int, void *); +static void wdt_gpi_start(void); +static void wdt_gpi_stop(void); +static void wdt_gpi_set_timeout(unsigned int); +static int wdt_gpi_open(struct inode *, struct file *); +static int wdt_gpi_release(struct inode *, struct file *); +static ssize_t wdt_gpi_write(struct file *, const char __user *, size_t, loff_t *); +static long wdt_gpi_ioctl(struct file *, unsigned int, unsigned long); +static int wdt_gpi_notify(struct notifier_block *, unsigned long, void *); +static const struct resource *wdt_gpi_get_resource(struct platform_device *, const char *, unsigned int); +static int __init wdt_gpi_probe(struct device *); +static int __exit wdt_gpi_remove(struct device *); + + +static const char wdt_gpi_name[] = "wdt_gpi"; +static atomic_t opencnt; +static int expect_close; +static int locked; + + +/* These are set from device resources */ +static void __iomem * wd_regs; +static unsigned int wd_irq, wd_ctr; + + +/* Module arguments */ +static int timeout = MAX_TIMEOUT_SECONDS; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); + +static unsigned long resetaddr = 0xbffdc200; +module_param(resetaddr, ulong, 0444); +MODULE_PARM_DESC(resetaddr, "Address to write to to force a reset"); + +static unsigned long flagaddr = 0xbffdc104; +module_param(flagaddr, ulong, 0444); +MODULE_PARM_DESC(flagaddr, "Address to write to boot flags to"); + +static int powercycle; +module_param(powercycle, bool, 0444); +MODULE_PARM_DESC(powercycle, "Cycle power if watchdog expires"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0444); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be disabled once started"); + + +/* Kernel interfaces */ +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = wdt_gpi_open, + .release = wdt_gpi_release, + .write = wdt_gpi_write, + .unlocked_ioctl = wdt_gpi_ioctl, +}; + +static struct miscdevice miscdev = { + .minor = WATCHDOG_MINOR, + .name = wdt_gpi_name, + .fops = &fops, +}; + +static struct notifier_block wdt_gpi_shutdown = { + .notifier_call = wdt_gpi_notify, +}; + + +/* Interrupt handler */ +static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt) +{ + if (!unlikely(__raw_readl(wd_regs + 0x0008) & 0x1)) + return IRQ_NONE; + __raw_writel(0x1, wd_regs + 0x0008); + + + printk(KERN_CRIT "%s: watchdog expired - resetting system\n", + wdt_gpi_name); + + *(volatile char *) flagaddr |= 0x01; + *(volatile char *) resetaddr = powercycle ? 0x01 : 0x2; + iob(); + while (1) + cpu_relax(); +} + + +/* Watchdog functions */ +static void wdt_gpi_start(void) +{ + u32 reg; + + lock_titan_regs(); + reg = titan_readl(CPGIG1ER); + titan_writel(reg | (0x100 << wd_ctr), CPGIG1ER); + iob(); + unlock_titan_regs(); +} + +static void wdt_gpi_stop(void) +{ + u32 reg; + + lock_titan_regs(); + reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4)); + titan_writel(reg, CPCCR); + reg = titan_readl(CPGIG1ER); + titan_writel(reg & ~(0x100 << wd_ctr), CPGIG1ER); + iob(); + unlock_titan_regs(); +} + +static void wdt_gpi_set_timeout(unsigned int to) +{ + u32 reg; + const u32 wdval = (to * CLOCK) & ~0x0000000f; + + lock_titan_regs(); + reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4)); + titan_writel(reg, CPCCR); + wmb(); + __raw_writel(wdval, wd_regs + 0x0000); + wmb(); + titan_writel(reg | (0x2 << (wd_ctr * 4)), CPCCR); + wmb(); + titan_writel(reg | (0x5 << (wd_ctr * 4)), CPCCR); + iob(); + unlock_titan_regs(); +} + + +/* /dev/watchdog operations */ +static int wdt_gpi_open(struct inode *inode, struct file *file) +{ + int res; + + if (unlikely(atomic_dec_if_positive(&opencnt) < 0)) + return -EBUSY; + + expect_close = 0; + if (locked) { + module_put(THIS_MODULE); + free_irq(wd_irq, &miscdev); + locked = 0; + } + + res = request_irq(wd_irq, wdt_gpi_irqhdl, IRQF_SHARED | IRQF_DISABLED, + wdt_gpi_name, &miscdev); + if (unlikely(res)) + return res; + + wdt_gpi_set_timeout(timeout); + wdt_gpi_start(); + + printk(KERN_INFO "%s: watchdog started, timeout = %u seconds\n", + wdt_gpi_name, timeout); + return nonseekable_open(inode, file); +} + +static int wdt_gpi_release(struct inode *inode, struct file *file) +{ + if (nowayout) { + printk(KERN_INFO "%s: no way out - watchdog left running\n", + wdt_gpi_name); + __module_get(THIS_MODULE); + locked = 1; + } else { + if (expect_close) { + wdt_gpi_stop(); + free_irq(wd_irq, &miscdev); + printk(KERN_INFO "%s: watchdog stopped\n", wdt_gpi_name); + } else { + printk(KERN_CRIT "%s: unexpected close() -" + " watchdog left running\n", + wdt_gpi_name); + wdt_gpi_set_timeout(timeout); + __module_get(THIS_MODULE); + locked = 1; + } + } + + atomic_inc(&opencnt); + return 0; +} + +static ssize_t +wdt_gpi_write(struct file *f, const char __user *d, size_t s, loff_t *o) +{ + char val; + + wdt_gpi_set_timeout(timeout); + expect_close = (s > 0) && !get_user(val, d) && (val == 'V'); + return s ? 1 : 0; +} + +static long +wdt_gpi_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + long res = -ENOTTY; + const long size = _IOC_SIZE(cmd); + int stat; + void __user *argp = (void __user *)arg; + static struct watchdog_info wdinfo = { + .identity = "RM9xxx/GPI watchdog", + .firmware_version = 0, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING + }; + + if (unlikely(_IOC_TYPE(cmd) != WATCHDOG_IOCTL_BASE)) + return -ENOTTY; + + if ((_IOC_DIR(cmd) & _IOC_READ) + && !access_ok(VERIFY_WRITE, arg, size)) + return -EFAULT; + + if ((_IOC_DIR(cmd) & _IOC_WRITE) + && !access_ok(VERIFY_READ, arg, size)) + return -EFAULT; + + expect_close = 0; + + switch (cmd) { + case WDIOC_GETSUPPORT: + wdinfo.options = nowayout ? + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING : + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE; + res = __copy_to_user(argp, &wdinfo, size) ? -EFAULT : size; + break; + + case WDIOC_GETSTATUS: + break; + + case WDIOC_GETBOOTSTATUS: + stat = (*(volatile char *) flagaddr & 0x01) + ? WDIOF_CARDRESET : 0; + res = __copy_to_user(argp, &stat, size) ? + -EFAULT : size; + break; + + case WDIOC_SETOPTIONS: + break; + + case WDIOC_KEEPALIVE: + wdt_gpi_set_timeout(timeout); + res = size; + break; + + case WDIOC_SETTIMEOUT: + { + int val; + if (unlikely(__copy_from_user(&val, argp, size))) { + res = -EFAULT; + break; + } + + if (val > MAX_TIMEOUT_SECONDS) + val = MAX_TIMEOUT_SECONDS; + timeout = val; + wdt_gpi_set_timeout(val); + res = size; + printk(KERN_INFO "%s: timeout set to %u seconds\n", + wdt_gpi_name, timeout); + } + break; + + case WDIOC_GETTIMEOUT: + res = __copy_to_user(argp, &timeout, size) ? + -EFAULT : size; + break; + } + + return res; +} + + +/* Shutdown notifier */ +static int +wdt_gpi_notify(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + wdt_gpi_stop(); + + return NOTIFY_DONE; +} + + +/* Init & exit procedures */ +static const struct resource * +wdt_gpi_get_resource(struct platform_device *pdv, const char *name, + unsigned int type) +{ + char buf[80]; + if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf) + return NULL; + return platform_get_resource_byname(pdv, type, buf); +} + +/* No hotplugging on the platform bus - use __init */ +static int __init wdt_gpi_probe(struct device *dev) +{ + int res; + struct platform_device * const pdv = to_platform_device(dev); + const struct resource + * const rr = wdt_gpi_get_resource(pdv, WDT_RESOURCE_REGS, + IORESOURCE_MEM), + * const ri = wdt_gpi_get_resource(pdv, WDT_RESOURCE_IRQ, + IORESOURCE_IRQ), + * const rc = wdt_gpi_get_resource(pdv, WDT_RESOURCE_COUNTER, + 0); + + if (unlikely(!rr || !ri || !rc)) + return -ENXIO; + + wd_regs = ioremap_nocache(rr->start, rr->end + 1 - rr->start); + if (unlikely(!wd_regs)) + return -ENOMEM; + wd_irq = ri->start; + wd_ctr = rc->start; + res = misc_register(&miscdev); + if (res) + iounmap(wd_regs); + else + register_reboot_notifier(&wdt_gpi_shutdown); + return res; +} + +static int __exit wdt_gpi_remove(struct device *dev) +{ + int res; + + unregister_reboot_notifier(&wdt_gpi_shutdown); + res = misc_deregister(&miscdev); + iounmap(wd_regs); + wd_regs = NULL; + return res; +} + + +/* Device driver init & exit */ +static struct device_driver wdt_gpi_driver = { + .name = (char *) wdt_gpi_name, + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .probe = wdt_gpi_probe, + .remove = __exit_p(wdt_gpi_remove), + .shutdown = NULL, + .suspend = NULL, + .resume = NULL, +}; + +static int __init wdt_gpi_init_module(void) +{ + atomic_set(&opencnt, 1); + if (timeout > MAX_TIMEOUT_SECONDS) + timeout = MAX_TIMEOUT_SECONDS; + return driver_register(&wdt_gpi_driver); +} + +static void __exit wdt_gpi_cleanup_module(void) +{ + driver_unregister(&wdt_gpi_driver); +} + +module_init(wdt_gpi_init_module); +module_exit(wdt_gpi_cleanup_module); + +MODULE_AUTHOR("Thomas Koeller "); +MODULE_DESCRIPTION("Basler eXcite watchdog driver for gpi devices"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c new file mode 100644 index 0000000..5d1c15f --- /dev/null +++ b/drivers/watchdog/s3c2410_wdt.c @@ -0,0 +1,563 @@ +/* linux/drivers/char/watchdog/s3c2410_wdt.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * S3C2410 Watchdog Timer Support + * + * Based on, softdog.c by Alan Cox, + * (c) Copyright 1996 Alan Cox + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Changelog: + * 05-Oct-2004 BJD Added semaphore init to stop crashes on open + * Fixed tmr_count / wdt_count confusion + * Added configurable debug + * + * 11-Jan-2005 BJD Fixed divide-by-2 in timeout code + * + * 25-Jan-2005 DA Added suspend/resume support + * Replaced reboot notifier with .shutdown method + * + * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#undef S3C_VA_WATCHDOG +#define S3C_VA_WATCHDOG (0) + +#include + +#define PFX "s3c2410-wdt: " + +#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) +#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) + +static int nowayout = WATCHDOG_NOWAYOUT; +static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME; +static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; +static int soft_noboot = 0; +static int debug = 0; + +module_param(tmr_margin, int, 0); +module_param(tmr_atboot, int, 0); +module_param(nowayout, int, 0); +module_param(soft_noboot, int, 0); +module_param(debug, int, 0); + +MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default=" __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); + +MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default=" __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT)); + +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default depends on ONLY_TESTING)"); + +MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)"); + + +typedef enum close_state { + CLOSE_STATE_NOT, + CLOSE_STATE_ALLOW=0x4021 +} close_state_t; + +static DECLARE_MUTEX(open_lock); + +static struct device *wdt_dev; /* platform device attached to */ +static struct resource *wdt_mem; +static struct resource *wdt_irq; +static struct clk *wdt_clock; +static void __iomem *wdt_base; +static unsigned int wdt_count; +static close_state_t allow_close; + +/* watchdog control routines */ + +#define DBG(msg...) do { \ + if (debug) \ + printk(KERN_INFO msg); \ + } while(0) + +/* functions */ + +static int s3c2410wdt_keepalive(void) +{ + writel(wdt_count, wdt_base + S3C2410_WTCNT); + return 0; +} + +static int s3c2410wdt_stop(void) +{ + unsigned long wtcon; + + wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); + writel(wtcon, wdt_base + S3C2410_WTCON); + + return 0; +} + +static int s3c2410wdt_start(void) +{ + unsigned long wtcon; + + s3c2410wdt_stop(); + + wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128; + + if (soft_noboot) { + wtcon |= S3C2410_WTCON_INTEN; + wtcon &= ~S3C2410_WTCON_RSTEN; + } else { + wtcon &= ~S3C2410_WTCON_INTEN; + wtcon |= S3C2410_WTCON_RSTEN; + } + + DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n", + __FUNCTION__, wdt_count, wtcon); + + writel(wdt_count, wdt_base + S3C2410_WTDAT); + writel(wdt_count, wdt_base + S3C2410_WTCNT); + writel(wtcon, wdt_base + S3C2410_WTCON); + + return 0; +} + +static int s3c2410wdt_set_heartbeat(int timeout) +{ + unsigned int freq = clk_get_rate(wdt_clock); + unsigned int count; + unsigned int divisor = 1; + unsigned long wtcon; + + if (timeout < 1) + return -EINVAL; + + freq /= 128; + count = timeout * freq; + + DBG("%s: count=%d, timeout=%d, freq=%d\n", + __FUNCTION__, count, timeout, freq); + + /* if the count is bigger than the watchdog register, + then work out what we need to do (and if) we can + actually make this value + */ + + if (count >= 0x10000) { + for (divisor = 1; divisor <= 0x100; divisor++) { + if ((count / divisor) < 0x10000) + break; + } + + if ((count / divisor) >= 0x10000) { + dev_err(wdt_dev, "timeout %d too big\n", timeout); + return -EINVAL; + } + } + + tmr_margin = timeout; + + DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", + __FUNCTION__, timeout, divisor, count, count/divisor); + + count /= divisor; + wdt_count = count; + + /* update the pre-scaler */ + wtcon = readl(wdt_base + S3C2410_WTCON); + wtcon &= ~S3C2410_WTCON_PRESCALE_MASK; + wtcon |= S3C2410_WTCON_PRESCALE(divisor-1); + + writel(count, wdt_base + S3C2410_WTDAT); + writel(wtcon, wdt_base + S3C2410_WTCON); + + return 0; +} + +/* + * /dev/watchdog handling + */ + +static int s3c2410wdt_open(struct inode *inode, struct file *file) +{ + if(down_trylock(&open_lock)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + allow_close = CLOSE_STATE_NOT; + + /* start the timer */ + s3c2410wdt_start(); + return nonseekable_open(inode, file); +} + +static int s3c2410wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + + if (allow_close == CLOSE_STATE_ALLOW) { + s3c2410wdt_stop(); + } else { + dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n"); + s3c2410wdt_keepalive(); + } + + allow_close = CLOSE_STATE_NOT; + up(&open_lock); + return 0; +} + +static ssize_t s3c2410wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* + * Refresh the timer. + */ + if(len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + allow_close = CLOSE_STATE_NOT; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + allow_close = CLOSE_STATE_ALLOW; + } + } + + s3c2410wdt_keepalive(); + } + return len; +} + +#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE + +static struct watchdog_info s3c2410_wdt_ident = { + .options = OPTIONS, + .firmware_version = 0, + .identity = "S3C2410 Watchdog", +}; + + +static int s3c2410wdt_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_margin; + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &s3c2410_wdt_ident, + sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + s3c2410wdt_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, p)) + return -EFAULT; + + if (s3c2410wdt_set_heartbeat(new_margin)) + return -EINVAL; + + s3c2410wdt_keepalive(); + return put_user(tmr_margin, p); + + case WDIOC_GETTIMEOUT: + return put_user(tmr_margin, p); + } +} + +/* kernel interface */ + +static const struct file_operations s3c2410wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = s3c2410wdt_write, + .ioctl = s3c2410wdt_ioctl, + .open = s3c2410wdt_open, + .release = s3c2410wdt_release, +}; + +static struct miscdevice s3c2410wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &s3c2410wdt_fops, +}; + +/* interrupt handler code */ + +static irqreturn_t s3c2410wdt_irq(int irqno, void *param) +{ + dev_info(wdt_dev, "watchdog timer expired (irq)\n"); + + s3c2410wdt_keepalive(); + return IRQ_HANDLED; +} +/* device interface */ + +static int s3c2410wdt_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device *dev; + unsigned int wtcon; + int started = 0; + int ret; + int size; + + DBG("%s: probe=%p\n", __FUNCTION__, pdev); + + dev = &pdev->dev; + wdt_dev = &pdev->dev; + + /* get the memory region for the watchdog timer */ + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(dev, "no memory resource specified\n"); + return -ENOENT; + } + + size = (res->end-res->start)+1; + wdt_mem = request_mem_region(res->start, size, pdev->name); + if (wdt_mem == NULL) { + dev_err(dev, "failed to get memory region\n"); + ret = -ENOENT; + goto err_req; + } + + wdt_base = ioremap(res->start, size); + if (wdt_base == 0) { + dev_err(dev, "failed to ioremap() region\n"); + ret = -EINVAL; + goto err_req; + } + + DBG("probe: mapped wdt_base=%p\n", wdt_base); + + wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (wdt_irq == NULL) { + dev_err(dev, "no irq resource specified\n"); + ret = -ENOENT; + goto err_map; + } + + ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev); + if (ret != 0) { + dev_err(dev, "failed to install irq (%d)\n", ret); + goto err_map; + } + + wdt_clock = clk_get(&pdev->dev, "watchdog"); + if (IS_ERR(wdt_clock)) { + dev_err(dev, "failed to find watchdog clock source\n"); + ret = PTR_ERR(wdt_clock); + goto err_irq; + } + + clk_enable(wdt_clock); + + /* see if we can actually set the requested timer margin, and if + * not, try the default value */ + + if (s3c2410wdt_set_heartbeat(tmr_margin)) { + started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); + + if (started == 0) { + dev_info(dev,"tmr_margin value out of range, default %d used\n", + CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); + } else { + dev_info(dev, "default timer value is out of range, cannot start\n"); + } + } + + ret = misc_register(&s3c2410wdt_miscdev); + if (ret) { + dev_err(dev, "cannot register miscdev on minor=%d (%d)\n", + WATCHDOG_MINOR, ret); + goto err_clk; + } + + if (tmr_atboot && started == 0) { + dev_info(dev, "starting watchdog timer\n"); + s3c2410wdt_start(); + } else if (!tmr_atboot) { + /* if we're not enabling the watchdog, then ensure it is + * disabled if it has been left running from the bootloader + * or other source */ + + s3c2410wdt_stop(); + } + + /* print out a statement of readiness */ + + wtcon = readl(wdt_base + S3C2410_WTCON); + + dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n", + (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in", + (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis", + (wtcon & S3C2410_WTCON_INTEN) ? "" : "en"); + + return 0; + + err_clk: + clk_disable(wdt_clock); + clk_put(wdt_clock); + + err_irq: + free_irq(wdt_irq->start, pdev); + + err_map: + iounmap(wdt_base); + + err_req: + release_resource(wdt_mem); + kfree(wdt_mem); + + return ret; +} + +static int s3c2410wdt_remove(struct platform_device *dev) +{ + release_resource(wdt_mem); + kfree(wdt_mem); + wdt_mem = NULL; + + free_irq(wdt_irq->start, dev); + wdt_irq = NULL; + + clk_disable(wdt_clock); + clk_put(wdt_clock); + wdt_clock = NULL; + + iounmap(wdt_base); + misc_deregister(&s3c2410wdt_miscdev); + return 0; +} + +static void s3c2410wdt_shutdown(struct platform_device *dev) +{ + s3c2410wdt_stop(); +} + +#ifdef CONFIG_PM + +static unsigned long wtcon_save; +static unsigned long wtdat_save; + +static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Save watchdog state, and turn it off. */ + wtcon_save = readl(wdt_base + S3C2410_WTCON); + wtdat_save = readl(wdt_base + S3C2410_WTDAT); + + /* Note that WTCNT doesn't need to be saved. */ + s3c2410wdt_stop(); + + return 0; +} + +static int s3c2410wdt_resume(struct platform_device *dev) +{ + /* Restore watchdog state. */ + + writel(wtdat_save, wdt_base + S3C2410_WTDAT); + writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */ + writel(wtcon_save, wdt_base + S3C2410_WTCON); + + printk(KERN_INFO PFX "watchdog %sabled\n", + (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); + + return 0; +} + +#else +#define s3c2410wdt_suspend NULL +#define s3c2410wdt_resume NULL +#endif /* CONFIG_PM */ + + +static struct platform_driver s3c2410wdt_driver = { + .probe = s3c2410wdt_probe, + .remove = s3c2410wdt_remove, + .shutdown = s3c2410wdt_shutdown, + .suspend = s3c2410wdt_suspend, + .resume = s3c2410wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "s3c2410-wdt", + }, +}; + + +static char banner[] __initdata = KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n"; + +static int __init watchdog_init(void) +{ + printk(banner); + return platform_driver_register(&s3c2410wdt_driver); +} + +static void __exit watchdog_exit(void) +{ + platform_driver_unregister(&s3c2410wdt_driver); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Ben Dooks , " + "Dimitry Andric "); +MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c new file mode 100644 index 0000000..3475f47 --- /dev/null +++ b/drivers/watchdog/sa1100_wdt.c @@ -0,0 +1,190 @@ +/* + * Watchdog driver for the SA11x0/PXA2xx + * + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * 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 Oleg Drokin nor iXcelerator.com admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 2000 Oleg Drokin + * + * 27/11/2000 Initial release + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_PXA +#include +#endif + +#include +#include +#include + +#define OSCR_FREQ CLOCK_TICK_RATE + +static unsigned long sa1100wdt_users; +static int pre_margin; +static int boot_status; + +/* + * Allow only one person to hold it open + */ +static int sa1100dog_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1,&sa1100wdt_users)) + return -EBUSY; + + /* Activate SA1100 Watchdog timer */ + OSMR3 = OSCR + pre_margin; + OSSR = OSSR_M3; + OWER = OWER_WME; + OIER |= OIER_E3; + return nonseekable_open(inode, file); +} + +/* + * The watchdog cannot be disabled. + * + * Previous comments suggested that turning off the interrupt by + * clearing OIER[E3] would prevent the watchdog timing out but this + * does not appear to be true (at least on the PXA255). + */ +static int sa1100dog_release(struct inode *inode, struct file *file) +{ + printk(KERN_CRIT "WATCHDOG: Device closed - timer will not stop\n"); + + clear_bit(1, &sa1100wdt_users); + + return 0; +} + +static ssize_t sa1100dog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) +{ + if (len) + /* Refresh OSMR3 timer. */ + OSMR3 = OSCR + pre_margin; + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = "SA1100/PXA255 Watchdog", +}; + +static int sa1100dog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = -ENOTTY; + int time; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(argp, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, p); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, p); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, p); + if (ret) + break; + + if (time <= 0 || time > 255) { + ret = -EINVAL; + break; + } + + pre_margin = OSCR_FREQ * time; + OSMR3 = OSCR + pre_margin; + /*fall through*/ + + case WDIOC_GETTIMEOUT: + ret = put_user(pre_margin / OSCR_FREQ, p); + break; + + case WDIOC_KEEPALIVE: + OSMR3 = OSCR + pre_margin; + ret = 0; + break; + } + return ret; +} + +static const struct file_operations sa1100dog_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = sa1100dog_write, + .ioctl = sa1100dog_ioctl, + .open = sa1100dog_open, + .release = sa1100dog_release, +}; + +static struct miscdevice sa1100dog_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &sa1100dog_fops, +}; + +static int margin __initdata = 60; /* (secs) Default is 1 minute */ + +static int __init sa1100dog_init(void) +{ + int ret; + + /* + * Read the reset status, and save it for later. If + * we suspend, RCSR will be cleared, and the watchdog + * reset reason will be lost. + */ + boot_status = (RCSR & RCSR_WDR) ? WDIOF_CARDRESET : 0; + pre_margin = OSCR_FREQ * margin; + + ret = misc_register(&sa1100dog_miscdev); + if (ret == 0) + printk("SA1100/PXA2xx Watchdog Timer: timer margin %d sec\n", + margin); + return ret; +} + +static void __exit sa1100dog_exit(void) +{ + misc_deregister(&sa1100dog_miscdev); +} + +module_init(sa1100dog_init); +module_exit(sa1100dog_exit); + +MODULE_AUTHOR("Oleg Drokin "); +MODULE_DESCRIPTION("SA1100/PXA2xx Watchdog"); + +module_param(margin, int, 0); +MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c new file mode 100644 index 0000000..e4f3cb6 --- /dev/null +++ b/drivers/watchdog/sbc60xxwdt.c @@ -0,0 +1,400 @@ +/* + * 60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x + * + * Based on acquirewdt.c by Alan Cox. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The author does 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 2000 Jakob Oestergaard + * + * 12/4 - 2000 [Initial revision] + * 25/4 - 2000 Added /dev/watchdog support + * 09/5 - 2001 [smj@oro.net] fixed fop_write to "return 1" on success + * 12/4 - 2002 [rob@osinvestor.com] eliminate fop_read + * fix possible wdt_is_open race + * add CONFIG_WATCHDOG_NOWAYOUT support + * remove lock_kernel/unlock_kernel pairs + * added KERN_* to printk's + * got rid of extraneous comments + * changed watchdog_info to correctly reflect what the driver offers + * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, WDIOC_SETTIMEOUT, + * WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls + * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces + * use module_param + * made timeout (the emulated heartbeat) a module_param + * made the keepalive ping an internal subroutine + * made wdt_stop and wdt_start module params + * added extra printk's for startup problems + * added MODULE_AUTHOR and MODULE_DESCRIPTION info + * + * + * This WDT driver is different from the other Linux WDT + * drivers in the following ways: + * *) The driver will ping the watchdog by itself, because this + * particular WDT has a very short timeout (one second) and it + * would be insane to count on any userspace daemon always + * getting scheduled within that time frame. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define OUR_NAME "sbc60xxwdt" +#define PFX OUR_NAME ": " + +/* + * You must set these - The driver cannot probe for the settings + */ + +static int wdt_stop = 0x45; +module_param(wdt_stop, int, 0); +MODULE_PARM_DESC(wdt_stop, "SBC60xx WDT 'stop' io port (default 0x45)"); + +static int wdt_start = 0x443; +module_param(wdt_start, int, 0); +MODULE_PARM_DESC(wdt_start, "SBC60xx WDT 'start' io port (default 0x443)"); + +/* + * The 60xx board can use watchdog timeout values from one second + * to several minutes. The default is one second, so if we reset + * the watchdog every ~250ms we should be safe. + */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + * If the daemon pulses us every 25 seconds, we can still afford + * a 5 second scheduling delay on the (high priority) daemon. That + * should be sufficient for a box under any load. + */ + +#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ +static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, 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 void wdt_timer_ping(unsigned long); +static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); +static unsigned long next_heartbeat; +static unsigned long wdt_is_open; +static char wdt_expect_close; + +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT by reading from wdt_start */ + inb_p(wdt_start); + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + } else { + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); + } +} + +/* + * Utility routines + */ + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + /* Start the timer */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); +} + +static void wdt_turnoff(void) +{ + /* Stop the timer */ + del_timer(&timer); + inb_p(wdt_stop); + printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); +} + +static void wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); +} + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) + { + if (!nowayout) + { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for(ofs = 0; ofs != count; ofs++) + { + char c; + if(get_user(c, buf+ofs)) + return -EFAULT; + if(c == 'V') + wdt_expect_close = 42; + } + } + + /* Well, anyhow someone wrote to us, we should return that favour */ + wdt_keepalive(); + } + return count; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Good, fire up the show */ + wdt_startup(); + return nonseekable_open(inode, file); +} + +static int fop_close(struct inode * inode, struct file * file) +{ + if(wdt_expect_close == 42) + wdt_turnoff(); + else { + del_timer(&timer); + printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n"); + } + clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; + return 0; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident= + { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "SBC60xx", + }; + + switch(cmd) + { + default: + return -ENOTTY; + 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: + wdt_keepalive(); + return 0; + case WDIOC_SETOPTIONS: + { + int new_options, retval = -EINVAL; + + if(get_user(new_options, p)) + return -EFAULT; + + if(new_options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(new_options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } + case WDIOC_SETTIMEOUT: + { + int new_timeout; + + if(get_user(new_timeout, p)) + return -EFAULT; + + if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ + return -EINVAL; + + timeout = new_timeout; + wdt_keepalive(); + /* Fall through */ + } + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + } +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = fop_write, + .open = fop_open, + .release = fop_close, + .ioctl = fop_ioctl, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier= +{ + .notifier_call = wdt_notify_sys, +}; + +static void __exit sbc60xxwdt_unload(void) +{ + wdt_turnoff(); + + /* Deregister */ + misc_deregister(&wdt_miscdev); + + unregister_reboot_notifier(&wdt_notifier); + if ((wdt_stop != 0x45) && (wdt_stop != wdt_start)) + release_region(wdt_stop,1); + release_region(wdt_start,1); +} + +static int __init sbc60xxwdt_init(void) +{ + int rc = -EBUSY; + + if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ + { + timeout = WATCHDOG_TIMEOUT; + printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", + timeout); + } + + if (!request_region(wdt_start, 1, "SBC 60XX WDT")) + { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_start); + rc = -EIO; + goto err_out; + } + + /* We cannot reserve 0x45 - the kernel already has! */ + if ((wdt_stop != 0x45) && (wdt_stop != wdt_start)) + { + if (!request_region(wdt_stop, 1, "SBC 60XX WDT")) + { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_stop); + rc = -EIO; + goto err_out_region1; + } + } + + rc = misc_register(&wdt_miscdev); + if (rc) + { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + wdt_miscdev.minor, rc); + goto err_out_region2; + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) + { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + rc); + goto err_out_miscdev; + } + + printk(KERN_INFO PFX "WDT driver for 60XX single board computer initialised. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out_region2: + if ((wdt_stop != 0x45) && (wdt_stop != wdt_start)) + release_region(wdt_stop,1); +err_out_region1: + release_region(wdt_start,1); +err_out: + return rc; +} + +module_init(sbc60xxwdt_init); +module_exit(sbc60xxwdt_unload); + +MODULE_AUTHOR("Jakob Oestergaard "); +MODULE_DESCRIPTION("60xx Single Board Computer Watchdog Timer driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/sbc8360.c b/drivers/watchdog/sbc8360.c new file mode 100644 index 0000000..285d852 --- /dev/null +++ b/drivers/watchdog/sbc8360.c @@ -0,0 +1,413 @@ +/* + * SBC8360 Watchdog driver + * + * (c) Copyright 2005 Webcon, Inc. + * + * Based on ib700wdt.c, which is based on advantechwdt.c which is based + * on acquirewdt.c which is based on wdt.c. + * + * (c) Copyright 2001 Charles Howes + * + * Based on advantechwdt.c which is based on acquirewdt.c which + * is based on wdt.c. + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * Based on acquirewdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * Added timeout module option to override default + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static unsigned long sbc8360_is_open; +static spinlock_t sbc8360_lock; +static char expect_close; + +#define PFX "sbc8360: " + +/* + * + * Watchdog Timer Configuration + * + * The function of the watchdog timer is to reset the system automatically + * and is defined at I/O port 0120H and 0121H. To enable the watchdog timer + * and allow the system to reset, write appropriate values from the table + * below to I/O port 0120H and 0121H. To disable the timer, write a zero + * value to I/O port 0121H for the system to stop the watchdog function. + * + * The following describes how the timer should be programmed (according to + * the vendor documentation) + * + * Enabling Watchdog: + * MOV AX,000AH (enable, phase I) + * MOV DX,0120H + * OUT DX,AX + * MOV AX,000BH (enable, phase II) + * MOV DX,0120H + * OUT DX,AX + * MOV AX,000nH (set multiplier n, from 1-4) + * MOV DX,0120H + * OUT DX,AX + * MOV AX,000mH (set base timer m, from 0-F) + * MOV DX,0121H + * OUT DX,AX + * + * Reset timer: + * MOV AX,000mH (same as set base timer, above) + * MOV DX,0121H + * OUT DX,AX + * + * Disabling Watchdog: + * MOV AX,0000H (a zero value) + * MOV DX,0120H + * OUT DX,AX + * + * Watchdog timeout configuration values: + * N + * M | 1 2 3 4 + * --|---------------------------------- + * 0 | 0.5s 5s 50s 100s + * 1 | 1s 10s 100s 200s + * 2 | 1.5s 15s 150s 300s + * 3 | 2s 20s 200s 400s + * 4 | 2.5s 25s 250s 500s + * 5 | 3s 30s 300s 600s + * 6 | 3.5s 35s 350s 700s + * 7 | 4s 40s 400s 800s + * 8 | 4.5s 45s 450s 900s + * 9 | 5s 50s 500s 1000s + * A | 5.5s 55s 550s 1100s + * B | 6s 60s 600s 1200s + * C | 6.5s 65s 650s 1300s + * D | 7s 70s 700s 1400s + * E | 7.5s 75s 750s 1500s + * F | 8s 80s 800s 1600s + * + * Another way to say the same things is: + * For N=1, Timeout = (M+1) * 0.5s + * For N=2, Timeout = (M+1) * 5s + * For N=3, Timeout = (M+1) * 50s + * For N=4, Timeout = (M+1) * 100s + * + */ + +static int wd_times[64][2] = { + {0, 1}, /* 0 = 0.5s */ + {1, 1}, /* 1 = 1s */ + {2, 1}, /* 2 = 1.5s */ + {3, 1}, /* 3 = 2s */ + {4, 1}, /* 4 = 2.5s */ + {5, 1}, /* 5 = 3s */ + {6, 1}, /* 6 = 3.5s */ + {7, 1}, /* 7 = 4s */ + {8, 1}, /* 8 = 4.5s */ + {9, 1}, /* 9 = 5s */ + {0xA, 1}, /* 10 = 5.5s */ + {0xB, 1}, /* 11 = 6s */ + {0xC, 1}, /* 12 = 6.5s */ + {0xD, 1}, /* 13 = 7s */ + {0xE, 1}, /* 14 = 7.5s */ + {0xF, 1}, /* 15 = 8s */ + {0, 2}, /* 16 = 5s */ + {1, 2}, /* 17 = 10s */ + {2, 2}, /* 18 = 15s */ + {3, 2}, /* 19 = 20s */ + {4, 2}, /* 20 = 25s */ + {5, 2}, /* 21 = 30s */ + {6, 2}, /* 22 = 35s */ + {7, 2}, /* 23 = 40s */ + {8, 2}, /* 24 = 45s */ + {9, 2}, /* 25 = 50s */ + {0xA, 2}, /* 26 = 55s */ + {0xB, 2}, /* 27 = 60s */ + {0xC, 2}, /* 28 = 65s */ + {0xD, 2}, /* 29 = 70s */ + {0xE, 2}, /* 30 = 75s */ + {0xF, 2}, /* 31 = 80s */ + {0, 3}, /* 32 = 50s */ + {1, 3}, /* 33 = 100s */ + {2, 3}, /* 34 = 150s */ + {3, 3}, /* 35 = 200s */ + {4, 3}, /* 36 = 250s */ + {5, 3}, /* 37 = 300s */ + {6, 3}, /* 38 = 350s */ + {7, 3}, /* 39 = 400s */ + {8, 3}, /* 40 = 450s */ + {9, 3}, /* 41 = 500s */ + {0xA, 3}, /* 42 = 550s */ + {0xB, 3}, /* 43 = 600s */ + {0xC, 3}, /* 44 = 650s */ + {0xD, 3}, /* 45 = 700s */ + {0xE, 3}, /* 46 = 750s */ + {0xF, 3}, /* 47 = 800s */ + {0, 4}, /* 48 = 100s */ + {1, 4}, /* 49 = 200s */ + {2, 4}, /* 50 = 300s */ + {3, 4}, /* 51 = 400s */ + {4, 4}, /* 52 = 500s */ + {5, 4}, /* 53 = 600s */ + {6, 4}, /* 54 = 700s */ + {7, 4}, /* 55 = 800s */ + {8, 4}, /* 56 = 900s */ + {9, 4}, /* 57 = 1000s */ + {0xA, 4}, /* 58 = 1100s */ + {0xB, 4}, /* 59 = 1200s */ + {0xC, 4}, /* 60 = 1300s */ + {0xD, 4}, /* 61 = 1400s */ + {0xE, 4}, /* 62 = 1500s */ + {0xF, 4} /* 63 = 1600s */ +}; + +#define SBC8360_ENABLE 0x120 +#define SBC8360_BASETIME 0x121 + +static int timeout = 27; +static int wd_margin = 0xB; +static int wd_multiplier = 2; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))"); +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +/* + * Kernel methods. + */ + +/* Activate and pre-configure watchdog */ +static void sbc8360_activate(void) +{ + /* Enable the watchdog */ + outb(0x0A, SBC8360_ENABLE); + msleep_interruptible(100); + outb(0x0B, SBC8360_ENABLE); + msleep_interruptible(100); + /* Set timeout multiplier */ + outb(wd_multiplier, SBC8360_ENABLE); + msleep_interruptible(100); + /* Nothing happens until first sbc8360_ping() */ +} + +/* Kernel pings watchdog */ +static void sbc8360_ping(void) +{ + /* Write the base timer register */ + outb(wd_margin, SBC8360_BASETIME); +} + +/* Userspace pings kernel driver, or requests clean close */ +static ssize_t sbc8360_write(struct file *file, const char __user * buf, + size_t count, loff_t * ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + sbc8360_ping(); + } + return count; +} + +static int sbc8360_open(struct inode *inode, struct file *file) +{ + spin_lock(&sbc8360_lock); + if (test_and_set_bit(0, &sbc8360_is_open)) { + spin_unlock(&sbc8360_lock); + return -EBUSY; + } + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate and ping once to start the countdown */ + spin_unlock(&sbc8360_lock); + sbc8360_activate(); + sbc8360_ping(); + return nonseekable_open(inode, file); +} + +static int sbc8360_close(struct inode *inode, struct file *file) +{ + spin_lock(&sbc8360_lock); + if (expect_close == 42) + outb(0, SBC8360_ENABLE); + else + printk(KERN_CRIT PFX + "SBC8360 device closed unexpectedly. SBC8360 will not stop!\n"); + + clear_bit(0, &sbc8360_is_open); + expect_close = 0; + spin_unlock(&sbc8360_lock); + return 0; +} + +/* + * Notifier for system down + */ + +static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Disable the SBC8360 Watchdog */ + outb(0, SBC8360_ENABLE); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations sbc8360_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = sbc8360_write, + .open = sbc8360_open, + .release = sbc8360_close, +}; + +static struct miscdevice sbc8360_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &sbc8360_fops, +}; + +/* + * The SBC8360 needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block sbc8360_notifier = { + .notifier_call = sbc8360_notify_sys, +}; + +static int __init sbc8360_init(void) +{ + int res; + unsigned long int mseconds = 60000; + + if (timeout < 0 || timeout > 63) { + printk(KERN_ERR PFX "Invalid timeout index (must be 0-63).\n"); + res = -EINVAL; + goto out; + } + + if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) { + printk(KERN_ERR PFX "ENABLE method I/O %X is not available.\n", + SBC8360_ENABLE); + res = -EIO; + goto out; + } + if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) { + printk(KERN_ERR PFX + "BASETIME method I/O %X is not available.\n", + SBC8360_BASETIME); + res = -EIO; + goto out_nobasetimereg; + } + + res = register_reboot_notifier(&sbc8360_notifier); + if (res) { + printk(KERN_ERR PFX "Failed to register reboot notifier.\n"); + goto out_noreboot; + } + + spin_lock_init(&sbc8360_lock); + res = misc_register(&sbc8360_miscdev); + if (res) { + printk(KERN_ERR PFX "failed to register misc device\n"); + goto out_nomisc; + } + + wd_margin = wd_times[timeout][0]; + wd_multiplier = wd_times[timeout][1]; + + if (wd_multiplier == 1) + mseconds = (wd_margin + 1) * 500; + else if (wd_multiplier == 2) + mseconds = (wd_margin + 1) * 5000; + else if (wd_multiplier == 3) + mseconds = (wd_margin + 1) * 50000; + else if (wd_multiplier == 4) + mseconds = (wd_margin + 1) * 100000; + + /* My kingdom for the ability to print "0.5 seconds" in the kernel! */ + printk(KERN_INFO PFX "Timeout set at %ld ms.\n", mseconds); + + return 0; + + out_nomisc: + unregister_reboot_notifier(&sbc8360_notifier); + out_noreboot: + release_region(SBC8360_BASETIME, 1); + out_nobasetimereg: + release_region(SBC8360_ENABLE, 1); + out: + return res; +} + +static void __exit sbc8360_exit(void) +{ + misc_deregister(&sbc8360_miscdev); + unregister_reboot_notifier(&sbc8360_notifier); + release_region(SBC8360_ENABLE, 1); + release_region(SBC8360_BASETIME, 1); +} + +module_init(sbc8360_init); +module_exit(sbc8360_exit); + +MODULE_AUTHOR("Ian E. Morgan "); +MODULE_DESCRIPTION("SBC8360 watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.01"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +/* end of sbc8360.c */ diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c new file mode 100644 index 0000000..82cbd88 --- /dev/null +++ b/drivers/watchdog/sbc_epx_c3.c @@ -0,0 +1,223 @@ +/* + * SBC EPX C3 0.1 A Hardware Watchdog Device for the Winsystems EPX-C3 + * single board computer + * + * (c) Copyright 2006 Calin A. Culianu , All Rights + * Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * based on softdog.c by Alan Cox + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "epx_c3: " +static int epx_c3_alive; + +#define WATCHDOG_TIMEOUT 1 /* 1 sec default 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) ")"); + +#define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */ +#define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */ + +static void epx_c3_start(void) +{ + outb(1, EPXC3_WATCHDOG_CTL_REG); +} + +static void epx_c3_stop(void) +{ + + outb(0, EPXC3_WATCHDOG_CTL_REG); + + printk(KERN_INFO PFX "Stopped watchdog timer.\n"); +} + +static void epx_c3_pet(void) +{ + outb(1, EPXC3_WATCHDOG_PET_REG); +} + +/* + * Allow only one person to hold it open + */ +static int epx_c3_open(struct inode *inode, struct file *file) +{ + if (epx_c3_alive) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate timer */ + epx_c3_start(); + epx_c3_pet(); + + epx_c3_alive = 1; + printk(KERN_INFO "Started watchdog timer.\n"); + + return nonseekable_open(inode, file); +} + +static int epx_c3_release(struct inode *inode, struct file *file) +{ + /* Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT */ + if (!nowayout) + epx_c3_stop(); /* Turn the WDT off */ + + epx_c3_alive = 0; + + return 0; +} + +static ssize_t epx_c3_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* Refresh the timer. */ + if (len) + epx_c3_pet(); + return len; +} + +static int epx_c3_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int options, retval = -EINVAL; + int __user *argp = (void __user *)arg; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "Winsystems EPX-C3 H/W Watchdog", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, argp); + case WDIOC_KEEPALIVE: + epx_c3_pet(); + return 0; + case WDIOC_GETTIMEOUT: + return put_user(WATCHDOG_TIMEOUT, argp); + case WDIOC_SETOPTIONS: + if (get_user(options, argp)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + epx_c3_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + epx_c3_start(); + retval = 0; + } + + return retval; + default: + return -ENOTTY; + } +} + +static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + epx_c3_stop(); /* Turn the WDT off */ + + return NOTIFY_DONE; +} + +static const struct file_operations epx_c3_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = epx_c3_write, + .ioctl = epx_c3_ioctl, + .open = epx_c3_open, + .release = epx_c3_release, +}; + +static struct miscdevice epx_c3_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &epx_c3_fops, +}; + +static struct notifier_block epx_c3_notifier = { + .notifier_call = epx_c3_notify_sys, +}; + +static const char banner[] __initdata = + KERN_INFO PFX "Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n"; + +static int __init watchdog_init(void) +{ + int ret; + + if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog")) + return -EBUSY; + + ret = register_reboot_notifier(&epx_c3_notifier); + if (ret) { + printk(KERN_ERR PFX "cannot register reboot notifier " + "(err=%d)\n", ret); + goto out; + } + + ret = misc_register(&epx_c3_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d " + "(err=%d)\n", WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&epx_c3_notifier); + goto out; + } + + printk(banner); + + return 0; + +out: + release_region(EPXC3_WATCHDOG_CTL_REG, 2); + return ret; +} + +static void __exit watchdog_exit(void) +{ + misc_deregister(&epx_c3_miscdev); + unregister_reboot_notifier(&epx_c3_notifier); + release_region(EPXC3_WATCHDOG_CTL_REG, 2); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Calin A. Culianu "); +MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC. Note that there is no way to probe for this device -- so only use it if you are *sure* you are runnning on this specific SBC system from Winsystems! It writes to IO ports 0x1ee and 0x1ef!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c new file mode 100644 index 0000000..9670d47 --- /dev/null +++ b/drivers/watchdog/sc1200wdt.c @@ -0,0 +1,463 @@ +/* + * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver + * (c) Copyright 2002 Zwane Mwaikambo , + * All Rights Reserved. + * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The author(s) of this software shall not be held liable for damages + * of any nature resulting due to the use of this software. This + * software is provided AS-IS with no warranties. + * + * Changelog: + * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware. + * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox. + * 20020222 Zwane Mwaikambo Added probing. + * 20020225 Zwane Mwaikambo Added ISAPNP support. + * 20020412 Rob Radez Broke out start/stop functions + * Return proper status instead of temperature warning + * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls + * Fix CONFIG_WATCHDOG_NOWAYOUT + * 20020530 Joel Becker Add Matt Domsch's nowayout module option + * 20030116 Adam Belay Updated to the latest pnp code + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SC1200_MODULE_VER "build 20020303" +#define SC1200_MODULE_NAME "sc1200wdt" +#define PFX SC1200_MODULE_NAME ": " + +#define MAX_TIMEOUT 255 /* 255 minutes */ +#define PMIR (io) /* Power Management Index Register */ +#define PMDR (io+1) /* Power Management Data Register */ + +/* Data Register indexes */ +#define FER1 0x00 /* Function enable register 1 */ +#define FER2 0x01 /* Function enable register 2 */ +#define PMC1 0x02 /* Power Management Ctrl 1 */ +#define PMC2 0x03 /* Power Management Ctrl 2 */ +#define PMC3 0x04 /* Power Management Ctrl 3 */ +#define WDTO 0x05 /* Watchdog timeout register */ +#define WDCF 0x06 /* Watchdog config register */ +#define WDST 0x07 /* Watchdog status register */ + +/* WDCF bitfields - which devices assert WDO */ +#define KBC_IRQ 0x01 /* Keyboard Controller */ +#define MSE_IRQ 0x02 /* Mouse */ +#define UART1_IRQ 0x03 /* Serial0 */ +#define UART2_IRQ 0x04 /* Serial1 */ +/* 5 -7 are reserved */ + +static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER; +static int timeout = 1; +static int io = -1; +static int io_len = 2; /* for non plug and play */ +static struct semaphore open_sem; +static char expect_close; +static spinlock_t sc1200wdt_lock; /* io port access serialisation */ + +#if defined CONFIG_PNP +static int isapnp = 1; +static struct pnp_dev *wdt_dev; + +module_param(isapnp, int, 0); +MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); +#endif + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "io port"); +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1"); + +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) ")"); + + + +/* Read from Data Register */ +static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data) +{ + spin_lock(&sc1200wdt_lock); + outb_p(index, PMIR); + *data = inb(PMDR); + spin_unlock(&sc1200wdt_lock); +} + + +/* Write to Data Register */ +static inline void sc1200wdt_write_data(unsigned char index, unsigned char data) +{ + spin_lock(&sc1200wdt_lock); + outb_p(index, PMIR); + outb(data, PMDR); + spin_unlock(&sc1200wdt_lock); +} + + +static void sc1200wdt_start(void) +{ + unsigned char reg; + + sc1200wdt_read_data(WDCF, ®); + /* assert WDO when any of the following interrupts are triggered too */ + reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); + sc1200wdt_write_data(WDCF, reg); + /* set the timeout and get the ball rolling */ + sc1200wdt_write_data(WDTO, timeout); +} + + +static void sc1200wdt_stop(void) +{ + sc1200wdt_write_data(WDTO, 0); +} + + +/* This returns the status of the WDO signal, inactive high. */ +static inline int sc1200wdt_status(void) +{ + unsigned char ret; + + sc1200wdt_read_data(WDST, &ret); + /* If the bit is inactive, the watchdog is enabled, so return + * KEEPALIVEPING which is a bit of a kludge because there's nothing + * else for enabled/disabled status + */ + return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */ +} + + +static int sc1200wdt_open(struct inode *inode, struct file *file) +{ + /* allow one at a time */ + if (down_trylock(&open_sem)) + return -EBUSY; + + if (timeout > MAX_TIMEOUT) + timeout = MAX_TIMEOUT; + + sc1200wdt_start(); + printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); + + return nonseekable_open(inode, file); +} + + +static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int new_timeout; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "PC87307/PC97307", + }; + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof ident)) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + return put_user(sc1200wdt_status(), p); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + sc1200wdt_write_data(WDTO, timeout); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, p)) + return -EFAULT; + + /* the API states this is given in secs */ + new_timeout /= 60; + if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) + return -EINVAL; + + timeout = new_timeout; + sc1200wdt_write_data(WDTO, timeout); + /* fall through and return the new timeout */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout * 60, p); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + sc1200wdt_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + sc1200wdt_start(); + retval = 0; + } + + return retval; + } + } +} + + +static int sc1200wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + sc1200wdt_stop(); + printk(KERN_INFO PFX "Watchdog disabled\n"); + } else { + sc1200wdt_write_data(WDTO, timeout); + printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout); + } + up(&open_sem); + expect_close = 0; + + return 0; +} + + +static ssize_t sc1200wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + + sc1200wdt_write_data(WDTO, timeout); + return len; + } + + return 0; +} + + +static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + sc1200wdt_stop(); + + return NOTIFY_DONE; +} + + +static struct notifier_block sc1200wdt_notifier = +{ + .notifier_call = sc1200wdt_notify_sys, +}; + +static const struct file_operations sc1200wdt_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = sc1200wdt_write, + .ioctl = sc1200wdt_ioctl, + .open = sc1200wdt_open, + .release = sc1200wdt_release, +}; + +static struct miscdevice sc1200wdt_miscdev = +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &sc1200wdt_fops, +}; + + +static int __init sc1200wdt_probe(void) +{ + /* The probe works by reading the PMC3 register's default value of 0x0e + * there is one caveat, if the device disables the parallel port or any + * of the UARTs we won't be able to detect it. + * Nb. This could be done with accuracy by reading the SID registers, but + * we don't have access to those io regions. + */ + + unsigned char reg; + + sc1200wdt_read_data(PMC3, ®); + reg &= 0x0f; /* we don't want the UART busy bits */ + return (reg == 0x0e) ? 0 : -ENODEV; +} + + +#if defined CONFIG_PNP + +static struct pnp_device_id scl200wdt_pnp_devices[] = { + /* National Semiconductor PC87307/PC97307 watchdog component */ + {.id = "NSC0800", .driver_data = 0}, + {.id = ""}, +}; + +static int scl200wdt_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) +{ + /* this driver only supports one card at a time */ + if (wdt_dev || !isapnp) + return -EBUSY; + + wdt_dev = dev; + io = pnp_port_start(wdt_dev, 0); + io_len = pnp_port_len(wdt_dev, 0); + + if (!request_region(io, io_len, SC1200_MODULE_NAME)) { + printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); + return -EBUSY; + } + + printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d\n", io, io_len); + return 0; +} + +static void scl200wdt_pnp_remove(struct pnp_dev * dev) +{ + if (wdt_dev){ + release_region(io, io_len); + wdt_dev = NULL; + } +} + +static struct pnp_driver scl200wdt_pnp_driver = { + .name = "scl200wdt", + .id_table = scl200wdt_pnp_devices, + .probe = scl200wdt_pnp_probe, + .remove = scl200wdt_pnp_remove, +}; + +#endif /* CONFIG_PNP */ + + +static int __init sc1200wdt_init(void) +{ + int ret; + + printk("%s\n", banner); + + spin_lock_init(&sc1200wdt_lock); + sema_init(&open_sem, 1); + +#if defined CONFIG_PNP + if (isapnp) { + ret = pnp_register_driver(&scl200wdt_pnp_driver); + if (ret) + goto out_clean; + } +#endif + + if (io == -1) { + printk(KERN_ERR PFX "io parameter must be specified\n"); + ret = -EINVAL; + goto out_pnp; + } + +#if defined CONFIG_PNP + /* now that the user has specified an IO port and we haven't detected + * any devices, disable pnp support */ + isapnp = 0; + pnp_unregister_driver(&scl200wdt_pnp_driver); +#endif + + if (!request_region(io, io_len, SC1200_MODULE_NAME)) { + printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); + ret = -EBUSY; + goto out_pnp; + } + + ret = sc1200wdt_probe(); + if (ret) + goto out_io; + + ret = register_reboot_notifier(&sc1200wdt_notifier); + if (ret) { + printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret); + goto out_io; + } + + ret = misc_register(&sc1200wdt_miscdev); + if (ret) { + printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); + goto out_rbt; + } + + /* ret = 0 */ + +out_clean: + return ret; + +out_rbt: + unregister_reboot_notifier(&sc1200wdt_notifier); + +out_io: + release_region(io, io_len); + +out_pnp: +#if defined CONFIG_PNP + if (isapnp) + pnp_unregister_driver(&scl200wdt_pnp_driver); +#endif + goto out_clean; +} + + +static void __exit sc1200wdt_exit(void) +{ + misc_deregister(&sc1200wdt_miscdev); + unregister_reboot_notifier(&sc1200wdt_notifier); + +#if defined CONFIG_PNP + if(isapnp) + pnp_unregister_driver(&scl200wdt_pnp_driver); + else +#endif + release_region(io, io_len); +} + +module_init(sc1200wdt_init); +module_exit(sc1200wdt_exit); + +MODULE_AUTHOR("Zwane Mwaikambo "); +MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c new file mode 100644 index 0000000..e8594c6 --- /dev/null +++ b/drivers/watchdog/sc520_wdt.c @@ -0,0 +1,435 @@ +/* + * AMD Elan SC520 processor Watchdog Timer driver + * + * Based on acquirewdt.c by Alan Cox, + * and sbc60xxwdt.c by Jakob Oestergaard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The authors do NOT admit liability nor provide warranty for + * any of this software. This material is provided "AS-IS" in + * the hope that it may be useful for others. + * + * (c) Copyright 2001 Scott Jennings + * 9/27 - 2001 [Initial release] + * + * Additional fixes Alan Cox + * - Fixed formatting + * - Removed debug printks + * - Fixed SMP built kernel deadlock + * - Switched to private locks not lock_kernel + * - Used ioremap/writew/readw + * - Added NOWAYOUT support + * 4/12 - 2002 Changes by Rob Radez + * - Change comments + * - Eliminate fop_llseek + * - Change CONFIG_WATCHDOG_NOWAYOUT semantics + * - Add KERN_* tags to printks + * - fix possible wdt_is_open race + * - Report proper capabilities in watchdog_info + * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT, + * GETTIMEOUT, SETOPTIONS} ioctls + * 09/8 - 2003 Changes by Wim Van Sebroeck + * - cleanup of trailing spaces + * - added extra printk's for startup problems + * - use module_param + * - made timeout (the emulated heartbeat) a module_param + * - made the keepalive ping an internal subroutine + * 3/27 - 2004 Changes by Sean Young + * - set MMCR_BASE to 0xfffef000 + * - CBAR does not need to be read + * - removed debugging printks + * + * This WDT driver is different from most other Linux WDT + * drivers in that the driver will ping the watchdog by itself, + * because this particular WDT has a very short timeout (1.6 + * seconds) and it would be insane to count on any userspace + * daemon always getting scheduled within that time frame. + * + * This driver uses memory mapped IO, and spinlock. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define OUR_NAME "sc520_wdt" +#define PFX OUR_NAME ": " + +/* + * The AMD Elan SC520 timeout value is 492us times a power of 2 (0-7) + * + * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s + * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s + * + * We will program the SC520 watchdog for a timeout of 2.01s. + * If we reset the watchdog every ~250ms we should be safe. + */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + */ + +#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ +static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, 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) ")"); + +/* + * AMD Elan SC520 - Watchdog Timer Registers + */ +#define MMCR_BASE 0xfffef000 /* The default base address */ +#define OFFS_WDTMRCTL 0xCB0 /* Watchdog Timer Control Register */ + +/* WDT Control Register bit definitions */ +#define WDT_EXP_SEL_01 0x0001 /* [01] Time-out = 496 us (with 33 Mhz clk). */ +#define WDT_EXP_SEL_02 0x0002 /* [02] Time-out = 508 ms (with 33 Mhz clk). */ +#define WDT_EXP_SEL_03 0x0004 /* [03] Time-out = 1.02 s (with 33 Mhz clk). */ +#define WDT_EXP_SEL_04 0x0008 /* [04] Time-out = 2.03 s (with 33 Mhz clk). */ +#define WDT_EXP_SEL_05 0x0010 /* [05] Time-out = 4.07 s (with 33 Mhz clk). */ +#define WDT_EXP_SEL_06 0x0020 /* [06] Time-out = 8.13 s (with 33 Mhz clk). */ +#define WDT_EXP_SEL_07 0x0040 /* [07] Time-out = 16.27s (with 33 Mhz clk). */ +#define WDT_EXP_SEL_08 0x0080 /* [08] Time-out = 32.54s (with 33 Mhz clk). */ +#define WDT_IRQ_FLG 0x1000 /* [12] Interrupt Request Flag */ +#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */ +#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */ + +static __u16 __iomem *wdtmrctl; + +static void wdt_timer_ping(unsigned long); +static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); +static unsigned long next_heartbeat; +static unsigned long wdt_is_open; +static char wdt_expect_close; +static spinlock_t wdt_spinlock; + +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT */ + spin_lock(&wdt_spinlock); + writew(0xAAAA, wdtmrctl); + writew(0x5555, wdtmrctl); + spin_unlock(&wdt_spinlock); + + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + } else { + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); + } +} + +/* + * Utility routines + */ + +static void wdt_config(int writeval) +{ + __u16 dummy; + unsigned long flags; + + /* buy some time (ping) */ + spin_lock_irqsave(&wdt_spinlock, flags); + dummy=readw(wdtmrctl); /* ensure write synchronization */ + writew(0xAAAA, wdtmrctl); + writew(0x5555, wdtmrctl); + /* unlock WDT = make WDT configuration register writable one time */ + writew(0x3333, wdtmrctl); + writew(0xCCCC, wdtmrctl); + /* write WDT configuration register */ + writew(writeval, wdtmrctl); + spin_unlock_irqrestore(&wdt_spinlock, flags); +} + +static int wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + /* Start the timer */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + + /* Start the watchdog */ + wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04); + + printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); + return 0; +} + +static int wdt_turnoff(void) +{ + /* Stop the timer */ + del_timer(&timer); + + /* Stop the watchdog */ + wdt_config(0); + + printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); + return 0; +} + +static int wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); + return 0; +} + +static int wdt_set_heartbeat(int t) +{ + if ((t < 1) || (t > 3600)) /* arbitrary upper limit */ + return -EINVAL; + + timeout = t; + return 0; +} + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) { + if (!nowayout) { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* now scan */ + for(ofs = 0; ofs != count; ofs++) { + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if(c == 'V') + wdt_expect_close = 42; + } + } + + /* Well, anyhow someone wrote to us, we should return that favour */ + wdt_keepalive(); + } + return count; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + if (nowayout) + __module_get(THIS_MODULE); + + /* Good, fire up the show */ + wdt_startup(); + return nonseekable_open(inode, file); +} + +static int fop_close(struct inode * inode, struct file * file) +{ + if(wdt_expect_close == 42) { + wdt_turnoff(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + wdt_keepalive(); + } + clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; + return 0; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "SC520", + }; + + switch(cmd) + { + default: + return -ENOTTY; + 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: + wdt_keepalive(); + return 0; + case WDIOC_SETOPTIONS: + { + int new_options, retval = -EINVAL; + + if(get_user(new_options, p)) + return -EFAULT; + + if(new_options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(new_options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } + case WDIOC_SETTIMEOUT: + { + int new_timeout; + + if(get_user(new_timeout, p)) + return -EFAULT; + + if(wdt_set_heartbeat(new_timeout)) + return -EINVAL; + + wdt_keepalive(); + /* Fall through */ + } + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + } +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = fop_write, + .open = fop_open, + .release = fop_close, + .ioctl = fop_ioctl, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; + +static void __exit sc520_wdt_unload(void) +{ + if (!nowayout) + wdt_turnoff(); + + /* Deregister */ + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + iounmap(wdtmrctl); +} + +static int __init sc520_wdt_init(void) +{ + int rc = -EBUSY; + + spin_lock_init(&wdt_spinlock); + + /* Check that the timeout value is within it's range ; if not reset to the default */ + if (wdt_set_heartbeat(timeout)) { + wdt_set_heartbeat(WATCHDOG_TIMEOUT); + printk(KERN_INFO PFX "timeout value must be 1<=timeout<=3600, using %d\n", + WATCHDOG_TIMEOUT); + } + + wdtmrctl = ioremap((unsigned long)(MMCR_BASE + OFFS_WDTMRCTL), 2); + if (!wdtmrctl) { + printk(KERN_ERR PFX "Unable to remap memory\n"); + rc = -ENOMEM; + goto err_out_region2; + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + rc); + goto err_out_ioremap; + } + + rc = misc_register(&wdt_miscdev); + if (rc) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, rc); + goto err_out_notifier; + } + + printk(KERN_INFO PFX "WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n", + timeout,nowayout); + + return 0; + +err_out_notifier: + unregister_reboot_notifier(&wdt_notifier); +err_out_ioremap: + iounmap(wdtmrctl); +err_out_region2: + return rc; +} + +module_init(sc520_wdt_init); +module_exit(sc520_wdt_unload); + +MODULE_AUTHOR("Scott and Bill Jennings"); +MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c new file mode 100644 index 0000000..d4fd0fa --- /dev/null +++ b/drivers/watchdog/scx200_wdt.c @@ -0,0 +1,269 @@ +/* drivers/char/watchdog/scx200_wdt.c + + National Semiconductor SCx200 Watchdog support + + Copyright (c) 2001,2002 Christer Weinigel + + Some code taken from: + National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver + (c) Copyright 2002 Zwane Mwaikambo + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The author(s) of this software shall not be held liable for damages + of any nature resulting due to the use of this software. This + software is provided AS-IS with no warranties. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NAME "scx200_wdt" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +static int margin = 60; /* in seconds */ +module_param(margin, int, 0); +MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); + +static u16 wdto_restart; +static struct semaphore open_semaphore; +static char expect_close; + +/* Bits of the WDCNFG register */ +#define W_ENABLE 0x00fa /* Enable watchdog */ +#define W_DISABLE 0x0000 /* Disable watchdog */ + +/* The scaling factor for the timer, this depends on the value of W_ENABLE */ +#define W_SCALE (32768/1024) + +static void scx200_wdt_ping(void) +{ + outw(wdto_restart, scx200_cb_base + SCx200_WDT_WDTO); +} + +static void scx200_wdt_update_margin(void) +{ + printk(KERN_INFO NAME ": timer margin %d seconds\n", margin); + wdto_restart = margin * W_SCALE; +} + +static void scx200_wdt_enable(void) +{ + printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n", + wdto_restart); + + outw(0, scx200_cb_base + SCx200_WDT_WDTO); + outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS); + outw(W_ENABLE, scx200_cb_base + SCx200_WDT_WDCNFG); + + scx200_wdt_ping(); +} + +static void scx200_wdt_disable(void) +{ + printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); + + outw(0, scx200_cb_base + SCx200_WDT_WDTO); + outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS); + outw(W_DISABLE, scx200_cb_base + SCx200_WDT_WDCNFG); +} + +static int scx200_wdt_open(struct inode *inode, struct file *file) +{ + /* only allow one at a time */ + if (down_trylock(&open_semaphore)) + return -EBUSY; + scx200_wdt_enable(); + + return nonseekable_open(inode, file); +} + +static int scx200_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close != 42) { + printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n"); + } else if (!nowayout) { + scx200_wdt_disable(); + } + expect_close = 0; + up(&open_semaphore); + + return 0; +} + +static int scx200_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_HALT || code == SYS_POWER_OFF) + if (!nowayout) + scx200_wdt_disable(); + + return NOTIFY_DONE; +} + +static struct notifier_block scx200_wdt_notifier = +{ + .notifier_call = scx200_wdt_notify_sys, +}; + +static ssize_t scx200_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + /* check for a magic close character */ + if (len) + { + size_t i; + + scx200_wdt_ping(); + + expect_close = 0; + for (i = 0; i < len; ++i) { + char c; + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + + return len; + } + + return 0; +} + +static int scx200_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .identity = "NatSemi SCx200 Watchdog", + .firmware_version = 1, + .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING), + }; + int new_margin; + + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if(copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(0, p)) + return -EFAULT; + return 0; + case WDIOC_KEEPALIVE: + scx200_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, p)) + return -EFAULT; + if (new_margin < 1) + return -EINVAL; + margin = new_margin; + scx200_wdt_update_margin(); + scx200_wdt_ping(); + case WDIOC_GETTIMEOUT: + if (put_user(margin, p)) + return -EFAULT; + return 0; + } +} + +static const struct file_operations scx200_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = scx200_wdt_write, + .ioctl = scx200_wdt_ioctl, + .open = scx200_wdt_open, + .release = scx200_wdt_release, +}; + +static struct miscdevice scx200_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &scx200_wdt_fops, +}; + +static int __init scx200_wdt_init(void) +{ + int r; + + printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n"); + + /* check that we have found the configuration block */ + if (!scx200_cb_present()) + return -ENODEV; + + if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET, + SCx200_WDT_SIZE, + "NatSemi SCx200 Watchdog")) { + printk(KERN_WARNING NAME ": watchdog I/O region busy\n"); + return -EBUSY; + } + + scx200_wdt_update_margin(); + scx200_wdt_disable(); + + sema_init(&open_semaphore, 1); + + r = misc_register(&scx200_wdt_miscdev); + if (r) { + release_region(scx200_cb_base + SCx200_WDT_OFFSET, + SCx200_WDT_SIZE); + return r; + } + + r = register_reboot_notifier(&scx200_wdt_notifier); + if (r) { + printk(KERN_ERR NAME ": unable to register reboot notifier"); + misc_deregister(&scx200_wdt_miscdev); + release_region(scx200_cb_base + SCx200_WDT_OFFSET, + SCx200_WDT_SIZE); + return r; + } + + return 0; +} + +static void __exit scx200_wdt_cleanup(void) +{ + unregister_reboot_notifier(&scx200_wdt_notifier); + misc_deregister(&scx200_wdt_miscdev); + release_region(scx200_cb_base + SCx200_WDT_OFFSET, + SCx200_WDT_SIZE); +} + +module_init(scx200_wdt_init); +module_exit(scx200_wdt_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules" + c-basic-offset: 8 + End: +*/ diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c new file mode 100644 index 0000000..cecbedd --- /dev/null +++ b/drivers/watchdog/shwdt.c @@ -0,0 +1,485 @@ +/* + * drivers/char/watchdog/shwdt.c + * + * Watchdog driver for integrated watchdog in the SuperH processors. + * + * Copyright (C) 2001, 2002, 2003 Paul Mundt + * + * 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. + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * + * 19-Apr-2002 Rob Radez + * Added expect close support, made emulated timeout runtime changeable + * general cleanups, add some ioctls + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFX "shwdt: " + +/* + * Default clock division ratio is 5.25 msecs. For an additional table of + * values, consult the asm-sh/watchdog.h. Overload this at module load + * time. + * + * In order for this to work reliably we need to have HZ set to 1000 or + * something quite higher than 100 (or we need a proper high-res timer + * implementation that will deal with this properly), otherwise the 10ms + * resolution of a jiffy is enough to trigger the overflow. For things like + * the SH-4 and SH-5, this isn't necessarily that big of a problem, though + * for the SH-2 and SH-3, this isn't recommended unless the WDT is absolutely + * necssary. + * + * As a result of this timing problem, the only modes that are particularly + * feasible are the 4096 and the 2048 divisors, which yeild 5.25 and 2.62ms + * overflow periods respectively. + * + * Also, since we can't really expect userspace to be responsive enough + * before the overflow happens, we maintain two seperate timers .. One in + * the kernel for clearing out WOVF every 2ms or so (again, this depends on + * HZ == 1000), and another for monitoring userspace writes to the WDT device. + * + * As such, we currently use a configurable heartbeat interval which defaults + * to 30s. In this case, the userspace daemon is only responsible for periodic + * writes to the device before the next heartbeat is scheduled. If the daemon + * misses its deadline, the kernel timer will allow the WDT to overflow. + */ +static int clock_division_ratio = WTCSR_CKS_4096; + +#define next_ping_period(cks) msecs_to_jiffies(cks - 4) + +static void sh_wdt_ping(unsigned long data); + +static unsigned long shwdt_is_open; +static struct watchdog_info sh_wdt_info; +static char shwdt_expect_close; +static DEFINE_TIMER(timer, sh_wdt_ping, 0, 0); +static unsigned long next_heartbeat; + +#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ +static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ + +static int nowayout = WATCHDOG_NOWAYOUT; + +/** + * sh_wdt_start - Start the Watchdog + * + * Starts the watchdog. + */ +static void sh_wdt_start(void) +{ + __u8 csr; + + next_heartbeat = jiffies + (heartbeat * HZ); + mod_timer(&timer, next_ping_period(clock_division_ratio)); + + csr = sh_wdt_read_csr(); + csr |= WTCSR_WT | clock_division_ratio; + sh_wdt_write_csr(csr); + + sh_wdt_write_cnt(0); + + /* + * These processors have a bit of an inconsistent initialization + * process.. starting with SH-3, RSTS was moved to WTCSR, and the + * RSTCSR register was removed. + * + * On the SH-2 however, in addition with bits being in different + * locations, we must deal with RSTCSR outright.. + */ + csr = sh_wdt_read_csr(); + csr |= WTCSR_TME; + csr &= ~WTCSR_RSTS; + sh_wdt_write_csr(csr); + +#ifdef CONFIG_CPU_SH2 + /* + * Whoever came up with the RSTCSR semantics must've been smoking + * some of the good stuff, since in addition to the WTCSR/WTCNT write + * brain-damage, it's managed to fuck things up one step further.. + * + * If we need to clear the WOVF bit, the upper byte has to be 0xa5.. + * but if we want to touch RSTE or RSTS, the upper byte has to be + * 0x5a.. + */ + csr = sh_wdt_read_rstcsr(); + csr &= ~RSTCSR_RSTS; + sh_wdt_write_rstcsr(csr); +#endif +} + +/** + * sh_wdt_stop - Stop the Watchdog + * Stops the watchdog. + */ +static void sh_wdt_stop(void) +{ + __u8 csr; + + del_timer(&timer); + + csr = sh_wdt_read_csr(); + csr &= ~WTCSR_TME; + sh_wdt_write_csr(csr); +} + +/** + * sh_wdt_keepalive - Keep the Userspace Watchdog Alive + * The Userspace watchdog got a KeepAlive: schedule the next heartbeat. + */ +static inline void sh_wdt_keepalive(void) +{ + next_heartbeat = jiffies + (heartbeat * HZ); +} + +/** + * sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat + * Set the Userspace Watchdog heartbeat + */ +static int sh_wdt_set_heartbeat(int t) +{ + if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */ + return -EINVAL; + + heartbeat = t; + return 0; +} + +/** + * sh_wdt_ping - Ping the Watchdog + * @data: Unused + * + * Clears overflow bit, resets timer counter. + */ +static void sh_wdt_ping(unsigned long data) +{ + if (time_before(jiffies, next_heartbeat)) { + __u8 csr; + + csr = sh_wdt_read_csr(); + csr &= ~WTCSR_IOVF; + sh_wdt_write_csr(csr); + + sh_wdt_write_cnt(0); + + mod_timer(&timer, next_ping_period(clock_division_ratio)); + } else + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping " + "the watchdog\n"); +} + +/** + * sh_wdt_open - Open the Device + * @inode: inode of device + * @file: file handle of device + * + * Watchdog device is opened and started. + */ +static int sh_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &shwdt_is_open)) + return -EBUSY; + if (nowayout) + __module_get(THIS_MODULE); + + sh_wdt_start(); + + return nonseekable_open(inode, file); +} + +/** + * sh_wdt_close - Close the Device + * @inode: inode of device + * @file: file handle of device + * + * Watchdog device is closed and stopped. + */ +static int sh_wdt_close(struct inode *inode, struct file *file) +{ + if (shwdt_expect_close == 42) { + sh_wdt_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not " + "stopping watchdog!\n"); + sh_wdt_keepalive(); + } + + clear_bit(0, &shwdt_is_open); + shwdt_expect_close = 0; + + return 0; +} + +/** + * sh_wdt_write - Write to Device + * @file: file handle of device + * @buf: buffer to write + * @count: length of buffer + * @ppos: offset + * + * Pings the watchdog on write. + */ +static ssize_t sh_wdt_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + shwdt_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + shwdt_expect_close = 42; + } + } + sh_wdt_keepalive(); + } + + return count; +} + +/** + * sh_wdt_mmap - map WDT/CPG registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * A simple mmap() implementation for the corner cases where the counter + * needs to be mapped in userspace directly. Due to the relatively small + * size of the area, neighbouring registers not necessarily tied to the + * CPG will also be accessible through the register page, so this remains + * configurable for users that really know what they're doing. + * + * Additionaly, the register page maps in the CPG register base relative + * to the nearest page-aligned boundary, which requires that userspace do + * the appropriate CPU subtype math for calculating the page offset for + * the counter value. + */ +static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret = -ENOSYS; + +#ifdef CONFIG_SH_WDT_MMAP + unsigned long addr; + + /* Only support the simple cases where we map in a register page. */ + if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) + return -EINVAL; + + /* + * Pick WTCNT as the start, it's usually the first register after the + * FRQCR, and neither one are generally page-aligned out of the box. + */ + addr = WTCNT & ~(PAGE_SIZE - 1); + + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot)) { + printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n", + __FUNCTION__); + return -EAGAIN; + } + + ret = 0; +#endif + + return ret; +} + +/** + * sh_wdt_ioctl - Query Device + * @inode: inode of device + * @file: file handle of device + * @cmd: watchdog command + * @arg: argument + * + * Query basic information from the device or ping it, as outlined by the + * watchdog API. + */ +static int sh_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_heartbeat; + int options, retval = -EINVAL; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info *)arg, + &sh_wdt_info, + sizeof(sh_wdt_info)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_KEEPALIVE: + sh_wdt_keepalive(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, (int *)arg)) + return -EFAULT; + + if (sh_wdt_set_heartbeat(new_heartbeat)) + return -EINVAL; + + sh_wdt_keepalive(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, (int *)arg); + case WDIOC_SETOPTIONS: + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + sh_wdt_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + sh_wdt_start(); + retval = 0; + } + + return retval; + default: + return -ENOTTY; + } + + return 0; +} + +/** + * sh_wdt_notify_sys - Notifier Handler + * @this: notifier block + * @code: notifier event + * @unused: unused + * + * Handles specific events, such as turning off the watchdog during a + * shutdown event. + */ +static int sh_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + sh_wdt_stop(); + + return NOTIFY_DONE; +} + +static const struct file_operations sh_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = sh_wdt_write, + .ioctl = sh_wdt_ioctl, + .open = sh_wdt_open, + .release = sh_wdt_close, + .mmap = sh_wdt_mmap, +}; + +static struct watchdog_info sh_wdt_info = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "SH WDT", +}; + +static struct notifier_block sh_wdt_notifier = { + .notifier_call = sh_wdt_notify_sys, +}; + +static struct miscdevice sh_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &sh_wdt_fops, +}; + +/** + * sh_wdt_init - Initialize module + * Registers the device and notifier handler. Actual device + * initialization is handled by sh_wdt_open(). + */ +static int __init sh_wdt_init(void) +{ + int rc; + + if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) { + clock_division_ratio = WTCSR_CKS_4096; + printk(KERN_INFO PFX "clock_division_ratio value must " + "be 0x5<=x<=0x7, using %d\n", clock_division_ratio); + } + + rc = sh_wdt_set_heartbeat(heartbeat); + if (unlikely(rc)) { + heartbeat = WATCHDOG_HEARTBEAT; + printk(KERN_INFO PFX "heartbeat value must " + "be 1<=x<=3600, using %d\n", heartbeat); + } + + rc = register_reboot_notifier(&sh_wdt_notifier); + if (unlikely(rc)) { + printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", + rc); + return rc; + } + + rc = misc_register(&sh_wdt_miscdev); + if (unlikely(rc)) { + printk(KERN_ERR PFX "Can't register miscdev on " + "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc); + unregister_reboot_notifier(&sh_wdt_notifier); + return rc; + } + + printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", + heartbeat, nowayout); + + return 0; +} + +/** + * sh_wdt_exit - Deinitialize module + * Unregisters the device and notifier handler. Actual device + * deinitialization is handled by sh_wdt_close(). + */ +static void __exit sh_wdt_exit(void) +{ + misc_deregister(&sh_wdt_miscdev); + unregister_reboot_notifier(&sh_wdt_notifier); +} + +MODULE_AUTHOR("Paul Mundt "); +MODULE_DESCRIPTION("SuperH watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +module_param(clock_division_ratio, int, 0); +MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +module_init(sh_wdt_init); +module_exit(sh_wdt_exit); diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c new file mode 100644 index 0000000..d3cb0a7 --- /dev/null +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -0,0 +1,627 @@ +/* + * SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x + * + * Based on acquirewdt.c by Alan Cox + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* 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 const 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 "); +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=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c new file mode 100644 index 0000000..9c36949 --- /dev/null +++ b/drivers/watchdog/softdog.c @@ -0,0 +1,310 @@ +/* + * SoftDog 0.07: A Software Watchdog Device + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * Software only watchdog driver. Unlike its big brother the WDT501P + * driver this won't always recover a failed machine. + * + * 03/96: Angelo Haritsis : + * Modularised. + * Added soft_margin; use upon insmod to change the timer delay. + * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate + * minors. + * + * 19980911 Alan Cox + * Made SMP safe for 2.3.x + * + * 20011127 Joel Becker (jlbec@evilplan.org> + * Added soft_noboot; Allows testing the softdog trigger without + * requiring a recompile. + * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT. + * + * 20020530 Joel Becker + * Added Matt Domsch's nowayout module option. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PFX "SoftDog: " + +#define TIMER_MARGIN 60 /* Default is 60 seconds */ +static int soft_margin = TIMER_MARGIN; /* in seconds */ +module_param(soft_margin, int, 0); +MODULE_PARM_DESC(soft_margin, "Watchdog soft_margin in seconds. (0 0xFFFF)) + return -EINVAL; + + soft_margin = t; + return 0; +} + +/* + * /dev/watchdog handling + */ + +static int softdog_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &driver_open)) + return -EBUSY; + if (!test_and_clear_bit(0, &orphan_timer)) + __module_get(THIS_MODULE); + /* + * Activate timer + */ + softdog_keepalive(); + return nonseekable_open(inode, file); +} + +static int softdog_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (expect_close == 42) { + softdog_stop(); + module_put(THIS_MODULE); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + set_bit(0, &orphan_timer); + softdog_keepalive(); + } + clear_bit(0, &driver_open); + expect_close = 0; + return 0; +} + +static ssize_t softdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) +{ + /* + * Refresh the timer. + */ + if(len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + softdog_keepalive(); + } + return len; +} + +static int softdog_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_margin; + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "Software Watchdog", + }; + switch (cmd) { + default: + return -ENOTTY; + 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: + softdog_keepalive(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, p)) + return -EFAULT; + if (softdog_set_heartbeat(new_margin)) + return -EINVAL; + softdog_keepalive(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(soft_margin, p); + } +} + +/* + * Notifier for system down + */ + +static int softdog_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) { + /* Turn the WDT off */ + softdog_stop(); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations softdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = softdog_write, + .ioctl = softdog_ioctl, + .open = softdog_open, + .release = softdog_release, +}; + +static struct miscdevice softdog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &softdog_fops, +}; + +static struct notifier_block softdog_notifier = { + .notifier_call = softdog_notify_sys, +}; + +static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 initialized. soft_noboot=%d soft_margin=%d sec (nowayout= %d)\n"; + +static int __init watchdog_init(void) +{ + int ret; + + /* Check that the soft_margin value is within it's range ; if not reset to the default */ + if (softdog_set_heartbeat(soft_margin)) { + softdog_set_heartbeat(TIMER_MARGIN); + printk(KERN_INFO PFX "soft_margin value must be 0 + * added support for W83627THF. + * + * (c) Copyright 2003,2007 Pádraig Brady + * + * Based on advantechwdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WATCHDOG_NAME "w83627hf/thf/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, "w83627hf/thf WDT io port (default 0x2E)"); + +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=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +/* + * Kernel methods. + */ + +#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ +#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register (same as EFER) */ +#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ + +static void +w83627hf_select_wd_register(void) +{ + unsigned char c; + outb_p(0x87, WDT_EFER); /* Enter extended function mode */ + outb_p(0x87, WDT_EFER); /* Again according to manual */ + + outb(0x20, WDT_EFER); /* check chip version */ + c = inb(WDT_EFDR); + if (c == 0x82) { /* W83627THF */ + outb_p(0x2b, WDT_EFER); /* select GPIO3 */ + c = ((inb_p(WDT_EFDR) & 0xf7) | 0x04); /* select WDT0 */ + outb_p(0x2b, WDT_EFER); + outb_p(c, WDT_EFDR); /* set GPIO3 to WDT0 */ + } + + outb_p(0x07, WDT_EFER); /* point to logical device number reg */ + outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ + outb_p(0x30, WDT_EFER); /* select CR30 */ + outb_p(0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */ +} + +static void +w83627hf_unselect_wd_register(void) +{ + outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ +} + +/* tyan motherboards seem to set F5 to 0x4C ? + * So explicitly init to appropriate value. */ +static void +w83627hf_init(void) +{ + unsigned char t; + + w83627hf_select_wd_register(); + + outb_p(0xF6, WDT_EFER); /* Select CRF6 */ + t=inb_p(WDT_EFDR); /* read CRF6 */ + if (t != 0) { + printk (KERN_INFO PFX "Watchdog already running. Resetting timeout to %d sec\n", timeout); + outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */ + } + + outb_p(0xF5, WDT_EFER); /* Select CRF5 */ + t=inb_p(WDT_EFDR); /* read CRF5 */ + t&=~0x0C; /* set second mode & disable keyboard turning off watchdog */ + outb_p(t, WDT_EFDR); /* Write back to CRF5 */ + + outb_p(0xF7, WDT_EFER); /* Select CRF7 */ + t=inb_p(WDT_EFDR); /* read CRF7 */ + t&=~0xC0; /* disable keyboard & mouse turning off watchdog */ + outb_p(t, WDT_EFDR); /* Write back to CRF7 */ + + w83627hf_unselect_wd_register(); +} + +static void +wdt_ctrl(int timeout) +{ + spin_lock(&io_lock); + + w83627hf_select_wd_register(); + + outb_p(0xF6, WDT_EFER); /* Select CRF6 */ + outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF6 */ + + w83627hf_unselect_wd_register(); + + spin_unlock(&io_lock); +} + +static int +wdt_ping(void) +{ + wdt_ctrl(timeout); + return 0; +} + +static int +wdt_disable(void) +{ + wdt_ctrl(0); + 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 = "W83627HF 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_ping(); + 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_ping(); + 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 const 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 __init +wdt_init(void) +{ + int ret; + + spin_lock_init(&io_lock); + + printk(KERN_INFO "WDT driver for the Winbond(TM) W83627HF/THF/HG Super I/O chip initialising.\n"); + + 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); + } + + if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_io); + ret = -EIO; + goto out; + } + + w83627hf_init(); + + 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, 1); + goto out; +} + +static void __exit +wdt_exit(void) +{ + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + release_region(wdt_io,1); +} + +module_init(wdt_init); +module_exit(wdt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pádraig Brady "); +MODULE_DESCRIPTION("w83627hf/thf WDT driver"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c new file mode 100644 index 0000000..d9e821d --- /dev/null +++ b/drivers/watchdog/w83697hf_wdt.c @@ -0,0 +1,450 @@ +/* + * w83697hf/hg WDT driver + * + * (c) Copyright 2006 Samuel Tardieu + * (c) Copyright 2006 Marcus Junker + * + * Based on w83627hf_wdt.c which is based on advantechwdt.c + * which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 2003 Pádraig Brady + * + * (c) Copyright 2000-2001 Marek Michalkiewicz + * + * (c) Copyright 1996 Alan Cox , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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=" __MODULE_STRING(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 const 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 , Samuel Tardieu "); +MODULE_DESCRIPTION("w83697hf/hg WDT driver"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c new file mode 100644 index 0000000..3c88fe1 --- /dev/null +++ b/drivers/watchdog/w83877f_wdt.c @@ -0,0 +1,415 @@ +/* + * W83877F Computer Watchdog Timer driver + * + * Based on acquirewdt.c by Alan Cox, + * and sbc60xxwdt.c by Jakob Oestergaard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * The authors do NOT admit liability nor provide warranty for + * any of this software. This material is provided "AS-IS" in + * the hope that it may be useful for others. + * + * (c) Copyright 2001 Scott Jennings + * + * 4/19 - 2001 [Initial revision] + * 9/27 - 2001 Added spinlocking + * 4/12 - 2002 [rob@osinvestor.com] Eliminate extra comments + * Eliminate fop_read + * Eliminate extra spin_unlock + * Added KERN_* tags to printks + * add CONFIG_WATCHDOG_NOWAYOUT support + * fix possible wdt_is_open race + * changed watchdog_info to correctly reflect what the driver offers + * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, WDIOC_SETTIMEOUT, + * WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls + * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces + * added extra printk's for startup problems + * use module_param + * made timeout (the emulated heartbeat) a module_param + * made the keepalive ping an internal subroutine + * + * This WDT driver is different from most other Linux WDT + * drivers in that the driver will ping the watchdog by itself, + * because this particular WDT has a very short timeout (1.6 + * seconds) and it would be insane to count on any userspace + * daemon always getting scheduled within that time frame. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OUR_NAME "w83877f_wdt" +#define PFX OUR_NAME ": " + +#define ENABLE_W83877F_PORT 0x3F0 +#define ENABLE_W83877F 0x87 +#define DISABLE_W83877F 0xAA +#define WDT_PING 0x443 +#define WDT_REGISTER 0x14 +#define WDT_ENABLE 0x9C +#define WDT_DISABLE 0x8C + +/* + * The W83877F seems to be fixed at 1.6s timeout (at least on the + * EMACS PC-104 board I'm using). If we reset the watchdog every + * ~250ms we should be safe. */ + +#define WDT_INTERVAL (HZ/4+1) + +/* + * We must not require too good response from the userspace daemon. + * Here we require the userspace daemon to send us a heartbeat + * char to /dev/watchdog every 30 seconds. + */ + +#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ +static int timeout = WATCHDOG_TIMEOUT; /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, 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 void wdt_timer_ping(unsigned long); +static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); +static unsigned long next_heartbeat; +static unsigned long wdt_is_open; +static char wdt_expect_close; +static spinlock_t wdt_spinlock; + +/* + * Whack the dog + */ + +static void wdt_timer_ping(unsigned long data) +{ + /* If we got a heartbeat pulse within the WDT_US_INTERVAL + * we agree to ping the WDT + */ + if(time_before(jiffies, next_heartbeat)) + { + /* Ping the WDT */ + spin_lock(&wdt_spinlock); + + /* Ping the WDT by reading from WDT_PING */ + inb_p(WDT_PING); + + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + + spin_unlock(&wdt_spinlock); + + } else { + printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); + } +} + +/* + * Utility routines + */ + +static void wdt_change(int writeval) +{ + unsigned long flags; + spin_lock_irqsave(&wdt_spinlock, flags); + + /* buy some time */ + inb_p(WDT_PING); + + /* make W83877F available */ + outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT); + outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT); + + /* enable watchdog */ + outb_p(WDT_REGISTER, ENABLE_W83877F_PORT); + outb_p(writeval, ENABLE_W83877F_PORT+1); + + /* lock the W8387FF away */ + outb_p(DISABLE_W83877F, ENABLE_W83877F_PORT); + + spin_unlock_irqrestore(&wdt_spinlock, flags); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + /* Start the timer */ + mod_timer(&timer, jiffies + WDT_INTERVAL); + + wdt_change(WDT_ENABLE); + + printk(KERN_INFO PFX "Watchdog timer is now enabled.\n"); +} + +static void wdt_turnoff(void) +{ + /* Stop the timer */ + del_timer(&timer); + + wdt_change(WDT_DISABLE); + + printk(KERN_INFO PFX "Watchdog timer is now disabled...\n"); +} + +static void wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); +} + +/* + * /dev/watchdog handling + */ + +static ssize_t fop_write(struct file * file, const char __user * buf, size_t count, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) + { + if (!nowayout) + { + size_t ofs; + + /* note: just in case someone wrote the magic character + * five months ago... */ + wdt_expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for(ofs = 0; ofs != count; ofs++) + { + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if (c == 'V') + wdt_expect_close = 42; + } + } + + /* someone wrote to us, we should restart timer */ + wdt_keepalive(); + } + return count; +} + +static int fop_open(struct inode * inode, struct file * file) +{ + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + + /* Good, fire up the show */ + wdt_startup(); + return nonseekable_open(inode, file); +} + +static int fop_close(struct inode * inode, struct file * file) +{ + if(wdt_expect_close == 42) + wdt_turnoff(); + else { + del_timer(&timer); + printk(KERN_CRIT PFX "device file closed unexpectedly. Will not stop the WDT!\n"); + } + clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; + return 0; +} + +static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident= + { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "W83877F", + }; + + switch(cmd) + { + default: + return -ENOTTY; + 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: + wdt_keepalive(); + return 0; + case WDIOC_SETOPTIONS: + { + int new_options, retval = -EINVAL; + + if(get_user(new_options, p)) + return -EFAULT; + + if(new_options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(new_options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } + case WDIOC_SETTIMEOUT: + { + int new_timeout; + + if(get_user(new_timeout, p)) + return -EFAULT; + + if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ + return -EINVAL; + + timeout = new_timeout; + wdt_keepalive(); + /* Fall through */ + } + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + } +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = fop_write, + .open = fop_open, + .release = fop_close, + .ioctl = fop_ioctl, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + wdt_turnoff(); + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier= +{ + .notifier_call = wdt_notify_sys, +}; + +static void __exit w83877f_wdt_unload(void) +{ + wdt_turnoff(); + + /* Deregister */ + misc_deregister(&wdt_miscdev); + + unregister_reboot_notifier(&wdt_notifier); + release_region(WDT_PING,1); + release_region(ENABLE_W83877F_PORT,2); +} + +static int __init w83877f_wdt_init(void) +{ + int rc = -EBUSY; + + spin_lock_init(&wdt_spinlock); + + if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */ + { + timeout = WATCHDOG_TIMEOUT; + printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", + timeout); + } + + if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) + { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + ENABLE_W83877F_PORT); + rc = -EIO; + goto err_out; + } + + if (!request_region(WDT_PING, 1, "W8387FF WDT")) + { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + WDT_PING); + rc = -EIO; + goto err_out_region1; + } + + rc = misc_register(&wdt_miscdev); + if (rc) + { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + wdt_miscdev.minor, rc); + goto err_out_region2; + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) + { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + rc); + goto err_out_miscdev; + } + + printk(KERN_INFO PFX "WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out_region2: + release_region(WDT_PING,1); +err_out_region1: + release_region(ENABLE_W83877F_PORT,2); +err_out: + return rc; +} + +module_init(w83877f_wdt_init); +module_exit(w83877f_wdt_unload); + +MODULE_AUTHOR("Scott and Bill Jennings"); +MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c new file mode 100644 index 0000000..1579684 --- /dev/null +++ b/drivers/watchdog/w83977f_wdt.c @@ -0,0 +1,542 @@ +/* + * W83977F Watchdog Timer Driver for Winbond W83977F I/O Chip + * + * (c) Copyright 2005 Jose Goncalves + * + * Based on w83877f_wdt.c by Scott Jennings, + * and wdt977.c by Woody Suwalski + * + * ----------------------- + * + * 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 + +#define WATCHDOG_VERSION "1.00" +#define WATCHDOG_NAME "W83977F WDT" +#define PFX WATCHDOG_NAME ": " +#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" + +#define IO_INDEX_PORT 0x3F0 +#define IO_DATA_PORT (IO_INDEX_PORT+1) + +#define UNLOCK_DATA 0x87 +#define LOCK_DATA 0xAA +#define DEVICE_REGISTER 0x07 + +#define DEFAULT_TIMEOUT 45 /* default timeout in seconds */ + +static int timeout = DEFAULT_TIMEOUT; +static int timeoutW; /* timeout in watchdog counter units */ +static unsigned long timer_alive; +static int testmode; +static char expect_close; +static spinlock_t spinlock; + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (15..7635), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); +module_param(testmode, int, 0); +MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); + +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) ")"); + +/* + * Start the watchdog + */ + +static int wdt_start(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* + * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. + * F2 has the timeout in watchdog counter units. + * F3 is set to enable watchdog LED blink at timeout. + * F4 is used to just clear the TIMEOUT'ed state (bit 0). + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(timeoutW,IO_DATA_PORT); + outb_p(0xF3,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF4,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + + /* Set device Aux2 active */ + outb_p(0x30,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + + /* + * Select device Aux1 (dev=7) to set GP16 as the watchdog output + * (in reg E6) and GP13 as the watchdog LED output (in reg E3). + * Map GP16 at pin 119. + * In test mode watch the bit 0 on F4 to indicate "triggered" or + * check watchdog LED on SBC. + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x07,IO_DATA_PORT); + if (!testmode) + { + unsigned pin_map; + + outb_p(0xE6,IO_INDEX_PORT); + outb_p(0x0A,IO_DATA_PORT); + outb_p(0x2C,IO_INDEX_PORT); + pin_map = inb_p(IO_DATA_PORT); + pin_map |= 0x10; + pin_map &= ~(0x20); + outb_p(0x2C,IO_INDEX_PORT); + outb_p(pin_map,IO_DATA_PORT); + } + outb_p(0xE3,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + + /* Set device Aux1 active */ + outb_p(0x30,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + printk(KERN_INFO PFX "activated.\n"); + + return 0; +} + +/* + * Stop the watchdog + */ + +static int wdt_stop(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* + * Select device Aux2 (device=8) to set watchdog regs F2, F3 and F4. + * F2 is reset to its default value (watchdog timer disabled). + * F3 is reset to its default state. + * F4 clears the TIMEOUT'ed state (bit 0) - back to default. + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(0xFF,IO_DATA_PORT); + outb_p(0xF3,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + outb_p(0xF4,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(0x00,IO_DATA_PORT); + + /* + * Select device Aux1 (dev=7) to set GP16 (in reg E6) and + * Gp13 (in reg E3) as inputs. + */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x07,IO_DATA_PORT); + if (!testmode) + { + outb_p(0xE6,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + } + outb_p(0xE3,IO_INDEX_PORT); + outb_p(0x01,IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + printk(KERN_INFO PFX "shutdown.\n"); + + return 0; +} + +/* + * Send a keepalive ping to the watchdog + * This is done by simply re-writing the timeout to reg. 0xF2 + */ + +static int wdt_keepalive(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* Select device Aux2 (device=8) to kick watchdog reg F2 */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF2,IO_INDEX_PORT); + outb_p(timeoutW,IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + return 0; +} + +/* + * Set the watchdog timeout value + */ + +static int wdt_set_timeout(int t) +{ + int tmrval; + + /* + * Convert seconds to watchdog counter time units, rounding up. + * On PCM-5335 watchdog units are 30 seconds/step with 15 sec startup + * value. This information is supplied in the PCM-5335 manual and was + * checked by me on a real board. This is a bit strange because W83977f + * datasheet says counter unit is in minutes! + */ + if (t < 15) + return -EINVAL; + + tmrval = ((t + 15) + 29) / 30; + + if (tmrval > 255) + return -EINVAL; + + /* + * timeout is the timeout in seconds, + * timeoutW is the timeout in watchdog counter units. + */ + timeoutW = tmrval; + timeout = (timeoutW * 30) - 15; + return 0; +} + +/* + * Get the watchdog status + */ + +static int wdt_get_status(int *status) +{ + int new_status; + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* Unlock the SuperIO chip */ + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + outb_p(UNLOCK_DATA,IO_INDEX_PORT); + + /* Select device Aux2 (device=8) to read watchdog reg F4 */ + outb_p(DEVICE_REGISTER,IO_INDEX_PORT); + outb_p(0x08,IO_DATA_PORT); + outb_p(0xF4,IO_INDEX_PORT); + new_status = inb_p(IO_DATA_PORT); + + /* Lock the SuperIO chip */ + outb_p(LOCK_DATA,IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + *status = 0; + if (new_status & 1) + *status |= WDIOF_CARDRESET; + + return 0; +} + + +/* + * /dev/watchdog handling + */ + +static int wdt_open(struct inode *inode, struct file *file) +{ + /* If the watchdog is alive we don't need to start it again */ + if( test_and_set_bit(0, &timer_alive) ) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + wdt_start(); + return nonseekable_open(inode, file); +} + +static int wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (expect_close == 42) + { + wdt_stop(); + clear_bit(0, &timer_alive); + } else { + wdt_keepalive(); + printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n"); + } + expect_close = 0; + return 0; +} + +/* + * wdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if(count) + { + if (!nowayout) + { + size_t ofs; + + /* note: just in case someone wrote the magic character long ago */ + expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for(ofs = 0; ofs != count; ofs++) + { + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if (c == 'V') { + expect_close = 42; + } + } + } + + /* someone wrote to us, we should restart timer */ + wdt_keepalive(); + } + return count; +} + +/* + * wdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = WATCHDOG_NAME, +}; + +static int wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int status; + int new_options, retval = -EINVAL; + int new_timeout; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + + 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: + wdt_get_status(&status); + return put_user(status, uarg.i); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, uarg.i); + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + return 0; + + case WDIOC_SETOPTIONS: + if (get_user (new_options, uarg.i)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + wdt_stop(); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + wdt_start(); + retval = 0; + } + + return retval; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + + if (wdt_set_timeout(new_timeout)) + return -EINVAL; + + wdt_keepalive(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout, uarg.i); + + } +} + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) + wdt_stop(); + return NOTIFY_DONE; +} + +static const struct file_operations wdt_fops= +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .ioctl = wdt_ioctl, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_miscdev= +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; + +static int __init w83977f_wdt_init(void) +{ + int rc; + + printk(KERN_INFO PFX DRIVER_VERSION); + + spin_lock_init(&spinlock); + + /* + * Check that the timeout value is within it's range ; + * if not reset to the default + */ + if (wdt_set_timeout(timeout)) { + wdt_set_timeout(DEFAULT_TIMEOUT); + printk(KERN_INFO PFX "timeout value must be 15<=timeout<=7635, using %d\n", + DEFAULT_TIMEOUT); + } + + if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) + { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + IO_INDEX_PORT); + rc = -EIO; + goto err_out; + } + + rc = misc_register(&wdt_miscdev); + if (rc) + { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + wdt_miscdev.minor, rc); + goto err_out_region; + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) + { + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + rc); + goto err_out_miscdev; + } + + printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", + timeout, nowayout, testmode); + + return 0; + +err_out_miscdev: + misc_deregister(&wdt_miscdev); +err_out_region: + release_region(IO_INDEX_PORT,2); +err_out: + return rc; +} + +static void __exit w83977f_wdt_exit(void) +{ + wdt_stop(); + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + release_region(IO_INDEX_PORT,2); +} + +module_init(w83977f_wdt_init); +module_exit(w83977f_wdt_exit); + +MODULE_AUTHOR("Jose Goncalves "); +MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c new file mode 100644 index 0000000..950905d --- /dev/null +++ b/drivers/watchdog/wafer5823wdt.c @@ -0,0 +1,325 @@ +/* + * ICP Wafer 5823 Single Board Computer WDT driver + * http://www.icpamerica.com/wafer_5823.php + * May also work on other similar models + * + * (c) Copyright 2002 Justin Cormack + * + * Release 0.02 + * + * Based on advantechwdt.c which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WATCHDOG_NAME "Wafer 5823 WDT" +#define PFX WATCHDOG_NAME ": " +#define WD_TIMO 60 /* 60 sec default timeout */ + +static unsigned long wafwdt_is_open; +static char expect_close; +static spinlock_t wafwdt_lock; + +/* + * You must set these - there is no sane way to probe for this board. + * + * To enable, write the timeout value in seconds (1 to 255) to I/O + * port WDT_START, then read the port to start the watchdog. To pat + * the dog, read port WDT_STOP to stop the timer, then read WDT_START + * to restart it again. + */ + +static int wdt_stop = 0x843; +static int wdt_start = 0x443; + +static int timeout = WD_TIMO; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WD_TIMO) "."); + +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 wafwdt_ping(void) +{ + /* pat watchdog */ + spin_lock(&wafwdt_lock); + inb_p(wdt_stop); + inb_p(wdt_start); + spin_unlock(&wafwdt_lock); +} + +static void wafwdt_start(void) +{ + /* start up watchdog */ + outb_p(timeout, wdt_start); + inb_p(wdt_start); +} + +static void +wafwdt_stop(void) +{ + /* stop watchdog */ + inb_p(wdt_stop); +} + +static ssize_t wafwdt_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) +{ + /* See if we got the magic character 'V' and reload the timer */ + if (count) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + /* scan to see whether or not we got the magic character */ + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + /* Well, anyhow someone wrote to us, we should return that favour */ + wafwdt_ping(); + } + return count; +} + +static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_timeout; + void __user *argp = (void __user *)arg; + int __user *p = argp; + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "Wafer 5823 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: + wafwdt_ping(); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, p)) + return -EFAULT; + if ((new_timeout < 1) || (new_timeout > 255)) + return -EINVAL; + timeout = new_timeout; + wafwdt_stop(); + wafwdt_start(); + /* 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) { + wafwdt_start(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + wafwdt_stop(); + retval = 0; + } + + return retval; + } + + default: + return -ENOTTY; + } + return 0; +} + +static int wafwdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &wafwdt_is_open)) + return -EBUSY; + + /* + * Activate + */ + wafwdt_start(); + return nonseekable_open(inode, file); +} + +static int +wafwdt_close(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + wafwdt_stop(); + } else { + printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n"); + wafwdt_ping(); + } + clear_bit(0, &wafwdt_is_open); + expect_close = 0; + return 0; +} + +/* + * Notifier for system down + */ + +static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* Turn the WDT off */ + wafwdt_stop(); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations wafwdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wafwdt_write, + .ioctl = wafwdt_ioctl, + .open = wafwdt_open, + .release = wafwdt_close, +}; + +static struct miscdevice wafwdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wafwdt_fops, +}; + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wafwdt_notifier = { + .notifier_call = wafwdt_notify_sys, +}; + +static int __init wafwdt_init(void) +{ + int ret; + + printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n"); + + spin_lock_init(&wafwdt_lock); + + if (timeout < 1 || timeout > 255) { + timeout = WD_TIMO; + printk (KERN_INFO PFX "timeout value must be 1<=x<=255, using %d\n", + timeout); + } + + if (wdt_stop != wdt_start) { + if(!request_region(wdt_stop, 1, "Wafer 5823 WDT")) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_stop); + ret = -EIO; + goto error; + } + } + + if(!request_region(wdt_start, 1, "Wafer 5823 WDT")) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_start); + ret = -EIO; + goto error2; + } + + ret = register_reboot_notifier(&wafwdt_notifier); + if (ret != 0) { + printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", + ret); + goto error3; + } + + ret = misc_register(&wafwdt_miscdev); + if (ret != 0) { + printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto error4; + } + + printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + + return ret; +error4: + unregister_reboot_notifier(&wafwdt_notifier); +error3: + release_region(wdt_start, 1); +error2: + if (wdt_stop != wdt_start) + release_region(wdt_stop, 1); +error: + return ret; +} + +static void __exit wafwdt_exit(void) +{ + misc_deregister(&wafwdt_miscdev); + unregister_reboot_notifier(&wafwdt_notifier); + if(wdt_stop != wdt_start) + release_region(wdt_stop, 1); + release_region(wdt_start, 1); +} + +module_init(wafwdt_init); +module_exit(wafwdt_exit); + +MODULE_AUTHOR("Justin Cormack"); +MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +/* end of wafer5823wdt.c */ diff --git a/drivers/watchdog/wd501p.h b/drivers/watchdog/wd501p.h new file mode 100644 index 0000000..a4504f4 --- /dev/null +++ b/drivers/watchdog/wd501p.h @@ -0,0 +1,51 @@ +/* + * Industrial Computer Source WDT500/501 driver + * + * (c) Copyright 1995 CymruNET Ltd + * Innovation Centre + * Singleton Park + * Swansea + * Wales + * UK + * SA2 8PP + * + * http://www.cymru.net + * + * This driver is provided under the GNU General Public License, incorporated + * herein by reference. The driver is provided without warranty or + * support. + * + * Release 0.04. + * + */ + + +#define WDT_COUNT0 (io+0) +#define WDT_COUNT1 (io+1) +#define WDT_COUNT2 (io+2) +#define WDT_CR (io+3) +#define WDT_SR (io+4) /* Start buzzer on PCI write */ +#define WDT_RT (io+5) /* Stop buzzer on PCI write */ +#define WDT_BUZZER (io+6) /* PCI only: rd=disable, wr=enable */ +#define WDT_DC (io+7) + +/* The following are only on the PCI card, they're outside of I/O space on + * the ISA card: */ +#define WDT_CLOCK (io+12) /* COUNT2: rd=16.67MHz, wr=2.0833MHz */ +/* inverted opto isolated reset output: */ +#define WDT_OPTONOTRST (io+13) /* wr=enable, rd=disable */ +/* opto isolated reset output: */ +#define WDT_OPTORST (io+14) /* wr=enable, rd=disable */ +/* programmable outputs: */ +#define WDT_PROGOUT (io+15) /* wr=enable, rd=disable */ + + /* FAN 501 500 */ +#define WDC_SR_WCCR 1 /* Active low */ /* X X X */ +#define WDC_SR_TGOOD 2 /* X X - */ +#define WDC_SR_ISOI0 4 /* X X X */ +#define WDC_SR_ISII1 8 /* X X X */ +#define WDC_SR_FANGOOD 16 /* X - - */ +#define WDC_SR_PSUOVER 32 /* Active low */ /* X X - */ +#define WDC_SR_PSUUNDR 64 /* Active low */ /* X X - */ +#define WDC_SR_IRQ 128 /* Active low */ /* X X X */ + diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c new file mode 100644 index 0000000..1d64e27 --- /dev/null +++ b/drivers/watchdog/wdrtas.c @@ -0,0 +1,695 @@ +/* + * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as + * RTAS calls are available + */ + +/* + * RTAS watchdog driver + * + * (C) Copyright IBM Corp. 2005 + * device driver to exploit watchdog RTAS functions + * + * Authors : Utz Bacher + * + * 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, 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 + +#define WDRTAS_MAGIC_CHAR 42 +#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \ + WDIOF_MAGICCLOSE) + +MODULE_AUTHOR("Utz Bacher "); +MODULE_DESCRIPTION("RTAS watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS_MISCDEV(TEMP_MINOR); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int wdrtas_nowayout = 1; +#else +static int wdrtas_nowayout = 0; +#endif + +static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0); +static char wdrtas_expect_close = 0; + +static int wdrtas_interval; + +#define WDRTAS_THERMAL_SENSOR 3 +static int wdrtas_token_get_sensor_state; +#define WDRTAS_SURVEILLANCE_IND 9000 +static int wdrtas_token_set_indicator; +#define WDRTAS_SP_SPI 28 +static int wdrtas_token_get_sp; +static int wdrtas_token_event_scan; + +#define WDRTAS_DEFAULT_INTERVAL 300 + +#define WDRTAS_LOGBUFFER_LEN 128 +static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN]; + + +/*** watchdog access functions */ + +/** + * wdrtas_set_interval - sets the watchdog interval + * @interval: new interval + * + * returns 0 on success, <0 on failures + * + * wdrtas_set_interval sets the watchdog keepalive interval by calling the + * RTAS function set-indicator (surveillance). The unit of interval is + * seconds. + */ +static int +wdrtas_set_interval(int interval) +{ + long result; + static int print_msg = 10; + + /* rtas uses minutes */ + interval = (interval + 59) / 60; + + result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL, + WDRTAS_SURVEILLANCE_IND, 0, interval); + if ( (result < 0) && (print_msg) ) { + printk(KERN_ERR "wdrtas: setting the watchdog to %i " + "timeout failed: %li\n", interval, result); + print_msg--; + } + + return result; +} + +/** + * wdrtas_get_interval - returns the current watchdog interval + * @fallback_value: value (in seconds) to use, if the RTAS call fails + * + * returns the interval + * + * wdrtas_get_interval returns the current watchdog keepalive interval + * as reported by the RTAS function ibm,get-system-parameter. The unit + * of the return value is seconds. + */ +static int +wdrtas_get_interval(int fallback_value) +{ + long result; + char value[4]; + + result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL, + WDRTAS_SP_SPI, (void *)__pa(&value), 4); + if ( (value[0] != 0) || (value[1] != 2) || (value[3] != 0) || + (result < 0) ) { + printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog " + "timeout (%li). Continuing\n", result); + return fallback_value; + } + + /* rtas uses minutes */ + return ((int)value[2]) * 60; +} + +/** + * wdrtas_timer_start - starts watchdog + * + * wdrtas_timer_start starts the watchdog by calling the RTAS function + * set-interval (surveillance) + */ +static void +wdrtas_timer_start(void) +{ + wdrtas_set_interval(wdrtas_interval); +} + +/** + * wdrtas_timer_stop - stops watchdog + * + * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function + * set-interval (surveillance) + */ +static void +wdrtas_timer_stop(void) +{ + wdrtas_set_interval(0); +} + +/** + * wdrtas_log_scanned_event - logs an event we received during keepalive + * + * wdrtas_log_scanned_event prints a message to the log buffer dumping + * the results of the last event-scan call + */ +static void +wdrtas_log_scanned_event(void) +{ + int i; + + for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16) + printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16), + wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1], + wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3], + wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5], + wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7], + wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9], + wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11], + wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13], + wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]); +} + +/** + * wdrtas_timer_keepalive - resets watchdog timer to keep system alive + * + * wdrtas_timer_keepalive restarts the watchdog timer by calling the + * RTAS function event-scan and repeats these calls as long as there are + * events available. All events will be dumped. + */ +static void +wdrtas_timer_keepalive(void) +{ + long result; + + do { + result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL, + RTAS_EVENT_SCAN_ALL_EVENTS, 0, + (void *)__pa(wdrtas_logbuffer), + WDRTAS_LOGBUFFER_LEN); + if (result < 0) + printk(KERN_ERR "wdrtas: event-scan failed: %li\n", + result); + if (result == 0) + wdrtas_log_scanned_event(); + } while (result == 0); +} + +/** + * wdrtas_get_temperature - returns current temperature + * + * returns temperature or <0 on failures + * + * wdrtas_get_temperature returns the current temperature in Fahrenheit. It + * uses the RTAS call get-sensor-state, token 3 to do so + */ +static int +wdrtas_get_temperature(void) +{ + long result; + int temperature = 0; + + result = rtas_call(wdrtas_token_get_sensor_state, 2, 2, + (void *)__pa(&temperature), + WDRTAS_THERMAL_SENSOR, 0); + + if (result < 0) + printk(KERN_WARNING "wdrtas: reading the thermal sensor " + "faild: %li\n", result); + else + temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */ + + return temperature; +} + +/** + * wdrtas_get_status - returns the status of the watchdog + * + * returns a bitmask of defines WDIOF_... as defined in + * include/linux/watchdog.h + */ +static int +wdrtas_get_status(void) +{ + return 0; /* TODO */ +} + +/** + * wdrtas_get_boot_status - returns the reason for the last boot + * + * returns a bitmask of defines WDIOF_... as defined in + * include/linux/watchdog.h, indicating why the watchdog rebooted the system + */ +static int +wdrtas_get_boot_status(void) +{ + return 0; /* TODO */ +} + +/*** watchdog API and operations stuff */ + +/* wdrtas_write - called when watchdog device is written to + * @file: file structure + * @buf: user buffer with data + * @len: amount to data written + * @ppos: position in file + * + * returns the number of successfully processed characters, which is always + * the number of bytes passed to this function + * + * wdrtas_write processes all the data given to it and looks for the magic + * character 'V'. This character allows the watchdog device to be closed + * properly. + */ +static ssize_t +wdrtas_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + int i; + char c; + + if (!len) + goto out; + + if (!wdrtas_nowayout) { + wdrtas_expect_close = 0; + /* look for 'V' */ + for (i = 0; i < len; i++) { + if (get_user(c, buf + i)) + return -EFAULT; + /* allow to close device */ + if (c == 'V') + wdrtas_expect_close = WDRTAS_MAGIC_CHAR; + } + } + + wdrtas_timer_keepalive(); + +out: + return len; +} + +/** + * wdrtas_ioctl - ioctl function for the watchdog device + * @inode: inode structure + * @file: file structure + * @cmd: command for ioctl + * @arg: argument pointer + * + * returns 0 on success, <0 on failure + * + * wdrtas_ioctl implements the watchdog API ioctls + */ +static int +wdrtas_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int __user *argp = (void __user *)arg; + int i; + static struct watchdog_info wdinfo = { + .options = WDRTAS_SUPPORTED_MASK, + .firmware_version = 0, + .identity = "wdrtas" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &wdinfo, sizeof(wdinfo))) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + i = wdrtas_get_status(); + return put_user(i, argp); + + case WDIOC_GETBOOTSTATUS: + i = wdrtas_get_boot_status(); + return put_user(i, argp); + + case WDIOC_GETTEMP: + if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) + return -EOPNOTSUPP; + + i = wdrtas_get_temperature(); + return put_user(i, argp); + + case WDIOC_SETOPTIONS: + if (get_user(i, argp)) + return -EFAULT; + if (i & WDIOS_DISABLECARD) + wdrtas_timer_stop(); + if (i & WDIOS_ENABLECARD) { + wdrtas_timer_keepalive(); + wdrtas_timer_start(); + } + if (i & WDIOS_TEMPPANIC) { + /* not implemented. Done by H8 */ + } + return 0; + + case WDIOC_KEEPALIVE: + wdrtas_timer_keepalive(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(i, argp)) + return -EFAULT; + + if (wdrtas_set_interval(i)) + return -EINVAL; + + wdrtas_timer_keepalive(); + + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) + wdrtas_interval = i; + else + wdrtas_interval = wdrtas_get_interval(i); + /* fallthrough */ + + case WDIOC_GETTIMEOUT: + return put_user(wdrtas_interval, argp); + + default: + return -ENOTTY; + } +} + +/** + * wdrtas_open - open function of watchdog device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success, -EBUSY if the file has been opened already, <0 on + * other failures + * + * function called when watchdog device is opened + */ +static int +wdrtas_open(struct inode *inode, struct file *file) +{ + /* only open once */ + if (atomic_inc_return(&wdrtas_miscdev_open) > 1) { + atomic_dec(&wdrtas_miscdev_open); + return -EBUSY; + } + + wdrtas_timer_start(); + wdrtas_timer_keepalive(); + + return nonseekable_open(inode, file); +} + +/** + * wdrtas_close - close function of watchdog device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success + * + * close function. Always succeeds + */ +static int +wdrtas_close(struct inode *inode, struct file *file) +{ + /* only stop watchdog, if this was announced using 'V' before */ + if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR) + wdrtas_timer_stop(); + else { + printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog " + "not stopped.\n"); + wdrtas_timer_keepalive(); + } + + wdrtas_expect_close = 0; + atomic_dec(&wdrtas_miscdev_open); + return 0; +} + +/** + * wdrtas_temp_read - gives back the temperature in fahrenheit + * @file: file structure + * @buf: user buffer + * @count: number of bytes to be read + * @ppos: position in file + * + * returns always 1 or -EFAULT in case of user space copy failures, <0 on + * other failures + * + * wdrtas_temp_read gives the temperature to the users by copying this + * value as one byte into the user space buffer. The unit is Fahrenheit... + */ +static ssize_t +wdrtas_temp_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int temperature = 0; + + temperature = wdrtas_get_temperature(); + if (temperature < 0) + return temperature; + + if (copy_to_user(buf, &temperature, 1)) + return -EFAULT; + + return 1; +} + +/** + * wdrtas_temp_open - open function of temperature device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success, <0 on failure + * + * function called when temperature device is opened + */ +static int +wdrtas_temp_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +/** + * wdrtas_temp_close - close function of temperature device + * @inode: inode structure + * @file: file structure + * + * returns 0 on success + * + * close function. Always succeeds + */ +static int +wdrtas_temp_close(struct inode *inode, struct file *file) +{ + return 0; +} + +/** + * wdrtas_reboot - reboot notifier function + * @nb: notifier block structure + * @code: reboot code + * @ptr: unused + * + * returns NOTIFY_DONE + * + * wdrtas_reboot stops the watchdog in case of a reboot + */ +static int +wdrtas_reboot(struct notifier_block *this, unsigned long code, void *ptr) +{ + if ( (code==SYS_DOWN) || (code==SYS_HALT) ) + wdrtas_timer_stop(); + + return NOTIFY_DONE; +} + +/*** initialization stuff */ + +static const struct file_operations wdrtas_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdrtas_write, + .ioctl = wdrtas_ioctl, + .open = wdrtas_open, + .release = wdrtas_close, +}; + +static struct miscdevice wdrtas_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdrtas_fops, +}; + +static const struct file_operations wdrtas_temp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = wdrtas_temp_read, + .open = wdrtas_temp_open, + .release = wdrtas_temp_close, +}; + +static struct miscdevice wdrtas_tempdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &wdrtas_temp_fops, +}; + +static struct notifier_block wdrtas_notifier = { + .notifier_call = wdrtas_reboot, +}; + +/** + * wdrtas_get_tokens - reads in RTAS tokens + * + * returns 0 on succes, <0 on failure + * + * wdrtas_get_tokens reads in the tokens for the RTAS calls used in + * this watchdog driver. It tolerates, if "get-sensor-state" and + * "ibm,get-system-parameter" are not available. + */ +static int +wdrtas_get_tokens(void) +{ + wdrtas_token_get_sensor_state = rtas_token("get-sensor-state"); + if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) { + printk(KERN_WARNING "wdrtas: couldn't get token for " + "get-sensor-state. Trying to continue without " + "temperature support.\n"); + } + + wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter"); + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) { + printk(KERN_WARNING "wdrtas: couldn't get token for " + "ibm,get-system-parameter. Trying to continue with " + "a default timeout value of %i seconds.\n", + WDRTAS_DEFAULT_INTERVAL); + } + + wdrtas_token_set_indicator = rtas_token("set-indicator"); + if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) { + printk(KERN_ERR "wdrtas: couldn't get token for " + "set-indicator. Terminating watchdog code.\n"); + return -EIO; + } + + wdrtas_token_event_scan = rtas_token("event-scan"); + if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) { + printk(KERN_ERR "wdrtas: couldn't get token for event-scan. " + "Terminating watchdog code.\n"); + return -EIO; + } + + return 0; +} + +/** + * wdrtas_unregister_devs - unregisters the misc dev handlers + * + * wdrtas_register_devs unregisters the watchdog and temperature watchdog + * misc devs + */ +static void +wdrtas_unregister_devs(void) +{ + misc_deregister(&wdrtas_miscdev); + if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) + misc_deregister(&wdrtas_tempdev); +} + +/** + * wdrtas_register_devs - registers the misc dev handlers + * + * returns 0 on succes, <0 on failure + * + * wdrtas_register_devs registers the watchdog and temperature watchdog + * misc devs + */ +static int +wdrtas_register_devs(void) +{ + int result; + + result = misc_register(&wdrtas_miscdev); + if (result) { + printk(KERN_ERR "wdrtas: couldn't register watchdog misc " + "device. Terminating watchdog code.\n"); + return result; + } + + if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) { + result = misc_register(&wdrtas_tempdev); + if (result) { + printk(KERN_WARNING "wdrtas: couldn't register " + "watchdog temperature misc device. Continuing " + "without temperature support.\n"); + wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE; + } + } + + return 0; +} + +/** + * wdrtas_init - init function of the watchdog driver + * + * returns 0 on succes, <0 on failure + * + * registers the file handlers and the reboot notifier + */ +static int __init +wdrtas_init(void) +{ + if (wdrtas_get_tokens()) + return -ENODEV; + + if (wdrtas_register_devs()) + return -ENODEV; + + if (register_reboot_notifier(&wdrtas_notifier)) { + printk(KERN_ERR "wdrtas: could not register reboot notifier. " + "Terminating watchdog code.\n"); + wdrtas_unregister_devs(); + return -ENODEV; + } + + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) + wdrtas_interval = WDRTAS_DEFAULT_INTERVAL; + else + wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL); + + return 0; +} + +/** + * wdrtas_exit - exit function of the watchdog driver + * + * unregisters the file handlers and the reboot notifier + */ +static void __exit +wdrtas_exit(void) +{ + if (!wdrtas_nowayout) + wdrtas_timer_stop(); + + wdrtas_unregister_devs(); + + unregister_reboot_notifier(&wdrtas_notifier); +} + +module_init(wdrtas_init); +module_exit(wdrtas_exit); diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c new file mode 100644 index 0000000..0a3de6a --- /dev/null +++ b/drivers/watchdog/wdt.c @@ -0,0 +1,640 @@ +/* + * Industrial Computer Source WDT500/501 driver + * + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * Release 0.10. + * + * Fixes + * Dave Gregorich : Modularisation and minor bugs + * Alan Cox : Added the watchdog ioctl() stuff + * Alan Cox : Fixed the reboot problem (as noted by + * Matt Crocker). + * Alan Cox : Added wdt= boot option + * Alan Cox : Cleaned up copy/user stuff + * Tim Hockin : Added insmod parameters, comment cleanup + * Parameterized timeout + * Tigran Aivazian : Restructured wdt_init() to handle failures + * Joel Becker : Added WDIOC_GET/SETTIMEOUT + * Matt Domsch : Added nowayout module option + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "wd501p.h" + +static unsigned long wdt_is_open; +static char expect_close; + +/* + * Module parameters + */ + +#define WD_TIMO 60 /* Default heartbeat = 60 seconds */ + +static int heartbeat = WD_TIMO; +static int wd_heartbeat; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0>8, WDT_COUNT0+ctr); +} + +/** + * wdt_start: + * + * Start the watchdog driver. + */ + +static int wdt_start(void) +{ + inb_p(WDT_DC); /* Disable watchdog */ + wdt_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ + wdt_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ + wdt_ctr_mode(2,0); /* Program CTR2 for Mode 0: Pulse on Terminal Count */ + wdt_ctr_load(0, 8948); /* Count at 100Hz */ + wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ + wdt_ctr_load(2,65535); /* Length of reset pulse */ + outb_p(0, WDT_DC); /* Enable watchdog */ + return 0; +} + +/** + * wdt_stop: + * + * Stop the watchdog driver. + */ + +static int wdt_stop (void) +{ + /* Turn the card off */ + inb_p(WDT_DC); /* Disable watchdog */ + wdt_ctr_load(2,0); /* 0 length reset pulses now */ + return 0; +} + +/** + * wdt_ping: + * + * Reload counter one with the watchdog heartbeat. We don't bother reloading + * the cascade counter. + */ + +static int wdt_ping(void) +{ + /* Write a watchdog value */ + inb_p(WDT_DC); /* Disable watchdog */ + wdt_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ + wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ + outb_p(0, WDT_DC); /* Enable watchdog */ + return 0; +} + +/** + * wdt_set_heartbeat: + * @t: the new heartbeat value that needs to be set. + * + * Set a new heartbeat value for the watchdog device. If the heartbeat value is + * incorrect we keep the old value and return -EINVAL. If successfull we + * return 0. + */ +static int wdt_set_heartbeat(int t) +{ + if ((t < 1) || (t > 65535)) + return -EINVAL; + + heartbeat = t; + wd_heartbeat = t * 100; + return 0; +} + +/** + * wdt_get_status: + * @status: the new status. + * + * Extract the status information from a WDT watchdog device. There are + * several board variants so we have to know which bits are valid. Some + * bits default to one and some to zero in order to be maximally painful. + * + * we then map the bits onto the status ioctl flags. + */ + +static int wdt_get_status(int *status) +{ + unsigned char new_status=inb_p(WDT_SR); + + *status=0; + if (new_status & WDC_SR_ISOI0) + *status |= WDIOF_EXTERN1; + if (new_status & WDC_SR_ISII1) + *status |= WDIOF_EXTERN2; +#ifdef CONFIG_WDT_501 + if (!(new_status & WDC_SR_TGOOD)) + *status |= WDIOF_OVERHEAT; + if (!(new_status & WDC_SR_PSUOVER)) + *status |= WDIOF_POWEROVER; + if (!(new_status & WDC_SR_PSUUNDR)) + *status |= WDIOF_POWERUNDER; + if (tachometer) { + if (!(new_status & WDC_SR_FANGOOD)) + *status |= WDIOF_FANFAULT; + } +#endif /* CONFIG_WDT_501 */ + return 0; +} + +#ifdef CONFIG_WDT_501 +/** + * wdt_get_temperature: + * + * Reports the temperature in degrees Fahrenheit. The API is in + * farenheit. It was designed by an imperial measurement luddite. + */ + +static int wdt_get_temperature(int *temperature) +{ + unsigned short c=inb_p(WDT_RT); + + *temperature = (c * 11 / 15) + 7; + return 0; +} +#endif /* CONFIG_WDT_501 */ + +/** + * wdt_interrupt: + * @irq: Interrupt number + * @dev_id: Unused as we don't allow multiple devices. + * + * Handle an interrupt from the board. These are raised when the status + * map changes in what the board considers an interesting way. That means + * a failure condition occurring. + */ + +static irqreturn_t wdt_interrupt(int irq, void *dev_id) +{ + /* + * Read the status register see what is up and + * then printk it. + */ + unsigned char status=inb_p(WDT_SR); + + printk(KERN_CRIT "WDT status %d\n", status); + +#ifdef CONFIG_WDT_501 + if (!(status & WDC_SR_TGOOD)) + printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT)); + if (!(status & WDC_SR_PSUOVER)) + printk(KERN_CRIT "PSU over voltage.\n"); + if (!(status & WDC_SR_PSUUNDR)) + printk(KERN_CRIT "PSU under voltage.\n"); + if (tachometer) { + if (!(status & WDC_SR_FANGOOD)) + printk(KERN_CRIT "Possible fan fault.\n"); + } +#endif /* CONFIG_WDT_501 */ + if (!(status & WDC_SR_WCCR)) +#ifdef SOFTWARE_REBOOT +#ifdef ONLY_TESTING + printk(KERN_CRIT "Would Reboot.\n"); +#else + printk(KERN_CRIT "Initiating system reboot.\n"); + emergency_restart(); +#endif +#else + printk(KERN_CRIT "Reset in 5ms.\n"); +#endif + return IRQ_HANDLED; +} + + +/** + * wdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +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; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + wdt_ping(); + } + return count; +} + +/** + * wdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. We only actually usefully support + * querying capabilities and current status. + */ + +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_heartbeat; + int status; + + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT| + WDIOF_MAGICCLOSE| + WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = "WDT500/501", + }; + + /* Add options according to the card we have */ + ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); +#ifdef CONFIG_WDT_501 + ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); + if (tachometer) + ident.options |= WDIOF_FANFAULT; +#endif /* CONFIG_WDT_501 */ + + switch(cmd) + { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; + + case WDIOC_GETSTATUS: + wdt_get_status(&status); + return put_user(status, p); + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + case WDIOC_KEEPALIVE: + wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (wdt_set_heartbeat(new_heartbeat)) + return -EINVAL; + + wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + } +} + +/** + * wdt_open: + * @inode: inode of device + * @file: file handle to device + * + * The watchdog device has been opened. The watchdog device is single + * open and on opening we load the counters. Counter zero is a 100Hz + * cascade, into counter 1 which downcounts to reboot. When the counter + * triggers counter 2 downcounts the length of the reset pulse which + * set set to be as long as possible. + */ + +static int wdt_open(struct inode *inode, struct file *file) +{ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* + * Activate + */ + wdt_start(); + return nonseekable_open(inode, file); +} + +/** + * wdt_release: + * @inode: inode to board + * @file: file handle to board + * + * The watchdog has a configurable API. There is a religious dispute + * between people who want their watchdog to be able to shut down and + * those who want to be sure if the watchdog manager dies the machine + * reboots. In the former case we disable the counters, in the latter + * case you have to open it again very soon. + */ + +static int wdt_release(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + wdt_stop(); + clear_bit(0, &wdt_is_open); + } else { + printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); + wdt_ping(); + } + expect_close = 0; + return 0; +} + +#ifdef CONFIG_WDT_501 +/** + * wdt_temp_read: + * @file: file handle to the watchdog board + * @buf: buffer to write 1 byte into + * @count: length of buffer + * @ptr: offset (no seek allowed) + * + * Temp_read reports the temperature in degrees Fahrenheit. The API is in + * farenheit. It was designed by an imperial measurement luddite. + */ + +static ssize_t wdt_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) +{ + int temperature; + + if (wdt_get_temperature(&temperature)) + return -EFAULT; + + if (copy_to_user (buf, &temperature, 1)) + return -EFAULT; + + return 1; +} + +/** + * wdt_temp_open: + * @inode: inode of device + * @file: file handle to device + * + * The temperature device has been opened. + */ + +static int wdt_temp_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +/** + * wdt_temp_release: + * @inode: inode to board + * @file: file handle to board + * + * The temperature device has been closed. + */ + +static int wdt_temp_release(struct inode *inode, struct file *file) +{ + return 0; +} +#endif /* CONFIG_WDT_501 */ + +/** + * notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) { + /* Turn the card off */ + wdt_stop(); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .ioctl = wdt_ioctl, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +#ifdef CONFIG_WDT_501 +static const struct file_operations wdt_temp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = wdt_temp_read, + .open = wdt_temp_open, + .release = wdt_temp_release, +}; + +static struct miscdevice temp_miscdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &wdt_temp_fops, +}; +#endif /* CONFIG_WDT_501 */ + +/* + * The WDT card 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, +}; + +/** + * cleanup_module: + * + * Unload the watchdog. You cannot do this with any file handles open. + * If your watchdog is set to continue ticking on close and you unload + * it, well it keeps ticking. We won't get the interrupt but the board + * will not touch PC memory so all is fine. You just have to load a new + * module in 60 seconds or reboot. + */ + +static void __exit wdt_exit(void) +{ + misc_deregister(&wdt_miscdev); +#ifdef CONFIG_WDT_501 + misc_deregister(&temp_miscdev); +#endif /* CONFIG_WDT_501 */ + unregister_reboot_notifier(&wdt_notifier); + free_irq(irq, NULL); + release_region(io,8); +} + +/** + * wdt_init: + * + * Set up the WDT watchdog board. All we have to do is grab the + * resources we require and bitch if anyone beat us to them. + * The open() function will actually kick the board off. + */ + +static int __init wdt_init(void) +{ + int ret; + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (wdt_set_heartbeat(heartbeat)) { + wdt_set_heartbeat(WD_TIMO); + printk(KERN_INFO "wdt: heartbeat value must be 0, 1998 + * + * 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 +#include + +/* + * Define this to stop the watchdog actually rebooting the machine. + */ +#undef ONLY_TESTING + +static unsigned int soft_margin = 60; /* in seconds */ +static unsigned int reload; +static unsigned long timer_alive; + +#ifdef ONLY_TESTING +/* + * If the timer expires.. + */ +static void watchdog_fire(int irq, void *dev_id) +{ + printk(KERN_CRIT "Watchdog: Would Reboot.\n"); + *CSR_TIMER4_CNTL = 0; + *CSR_TIMER4_CLR = 0; +} +#endif + +/* + * Refresh the timer. + */ +static void watchdog_ping(void) +{ + *CSR_TIMER4_LOAD = reload; +} + +/* + * Allow only one person to hold it open + */ +static int watchdog_open(struct inode *inode, struct file *file) +{ + int ret; + + if (*CSR_SA110_CNTL & (1 << 13)) + return -EBUSY; + + if (test_and_set_bit(1, &timer_alive)) + return -EBUSY; + + reload = soft_margin * (mem_fclk_21285 / 256); + + *CSR_TIMER4_CLR = 0; + watchdog_ping(); + *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD + | TIMER_CNTL_DIV256; + +#ifdef ONLY_TESTING + ret = request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL); + if (ret) { + *CSR_TIMER4_CNTL = 0; + clear_bit(1, &timer_alive); + } +#else + /* + * Setting this bit is irreversible; once enabled, there is + * no way to disable the watchdog. + */ + *CSR_SA110_CNTL |= 1 << 13; + + ret = 0; +#endif + nonseekable_open(inode, file); + return ret; +} + +/* + * Shut off the timer. + * Note: if we really have enabled the watchdog, there + * is no way to turn off. + */ +static int watchdog_release(struct inode *inode, struct file *file) +{ +#ifdef ONLY_TESTING + free_irq(IRQ_TIMER4, NULL); + clear_bit(1, &timer_alive); +#endif + return 0; +} + +static ssize_t +watchdog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* + * Refresh the timer. + */ + if (len) + watchdog_ping(); + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT, + .identity = "Footbridge Watchdog", +}; + +static int +watchdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned int new_margin; + int ret = -ENOTTY; + + switch(cmd) { + case WDIOC_GETSUPPORT: + ret = 0; + if (copy_to_user((void *)arg, &ident, sizeof(ident))) + ret = -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0,(int *)arg); + break; + + case WDIOC_KEEPALIVE: + watchdog_ping(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(new_margin, (int *)arg); + if (ret) + break; + + /* Arbitrary, can't find the card's limits */ + if (new_margin < 0 || new_margin > 60) { + ret = -EINVAL; + break; + } + + soft_margin = new_margin; + reload = soft_margin * (mem_fclk_21285 / 256); + watchdog_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + ret = put_user(soft_margin, (int *)arg); + break; + } + return ret; +} + +static const 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, +}; + +static int __init footbridge_watchdog_init(void) +{ + int retval; + + if (machine_is_netwinder()) + return -ENODEV; + + retval = misc_register(&watchdog_miscdev); + if (retval < 0) + return retval; + + printk("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n", + soft_margin); + + if (machine_is_cats()) + printk("Warning: Watchdog reset may not work on this machine.\n"); + return 0; +} + +static void __exit footbridge_watchdog_exit(void) +{ + misc_deregister(&watchdog_miscdev); +} + +MODULE_AUTHOR("Phil Blundell "); +MODULE_DESCRIPTION("Footbridge watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); + +module_param(soft_margin, int, 0); +MODULE_PARM_DESC(soft_margin,"Watchdog timeout in seconds"); + +module_init(footbridge_watchdog_init); +module_exit(footbridge_watchdog_exit); diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c new file mode 100644 index 0000000..7d300ff --- /dev/null +++ b/drivers/watchdog/wdt977.c @@ -0,0 +1,519 @@ +/* + * Wdt977 0.04: A Watchdog Device for Netwinder W83977AF chip + * + * (c) Copyright 1998 Rebel.com (Woody Suwalski ) + * + * ----------------------- + * + * 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. + * + * ----------------------- + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * 19-Dec-2001 Woody Suwalski: Netwinder fixes, ioctl interface + * 06-Jan-2002 Woody Suwalski: For compatibility, convert all timeouts + * from minutes to seconds. + * 07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in + * nwwatchdog_init. + * 25-Oct-2005 Woody Suwalski: Convert addresses to #defs, add spinlocks + * remove limitiation to be used on Netwinders only + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define WATCHDOG_VERSION "0.04" +#define WATCHDOG_NAME "Wdt977" +#define PFX WATCHDOG_NAME ": " +#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" + +#define IO_INDEX_PORT 0x370 /* on some systems it can be 0x3F0 */ +#define IO_DATA_PORT (IO_INDEX_PORT+1) + +#define UNLOCK_DATA 0x87 +#define LOCK_DATA 0xAA +#define DEVICE_REGISTER 0x07 + + +#define DEFAULT_TIMEOUT 60 /* default timeout in seconds */ + +static int timeout = DEFAULT_TIMEOUT; +static int timeoutM; /* timeout in minutes */ +static unsigned long timer_alive; +static int testmode; +static char expect_close; +static spinlock_t spinlock; + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); +module_param(testmode, int, 0); +MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); + +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) ")"); + +/* + * Start the watchdog + */ + +static int wdt977_start(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* unlock the SuperIO chip */ + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + + /* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4 + * F2 has the timeout in minutes + * F3 could be set to the POWER LED blink (with GP17 set to PowerLed) + * at timeout, and to reset timer on kbd/mouse activity (not impl.) + * F4 is used to just clear the TIMEOUT'ed state (bit 0) + */ + outb_p(DEVICE_REGISTER, IO_INDEX_PORT); + outb_p(0x08, IO_DATA_PORT); + outb_p(0xF2, IO_INDEX_PORT); + outb_p(timeoutM, IO_DATA_PORT); + outb_p(0xF3, IO_INDEX_PORT); + outb_p(0x00, IO_DATA_PORT); /* another setting is 0E for kbd/mouse/LED */ + outb_p(0xF4, IO_INDEX_PORT); + outb_p(0x00, IO_DATA_PORT); + + /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ + /* in test mode watch the bit 1 on F4 to indicate "triggered" */ + if (!testmode) + { + outb_p(DEVICE_REGISTER, IO_INDEX_PORT); + outb_p(0x07, IO_DATA_PORT); + outb_p(0xE6, IO_INDEX_PORT); + outb_p(0x08, IO_DATA_PORT); + } + + /* lock the SuperIO chip */ + outb_p(LOCK_DATA, IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + printk(KERN_INFO PFX "activated.\n"); + + return 0; +} + +/* + * Stop the watchdog + */ + +static int wdt977_stop(void) +{ + unsigned long flags; + spin_lock_irqsave(&spinlock, flags); + + /* unlock the SuperIO chip */ + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + + /* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 + * F3 is reset to its default state + * F4 can clear the TIMEOUT'ed state (bit 0) - back to default + * We can not use GP17 as a PowerLed, as we use its usage as a RedLed + */ + outb_p(DEVICE_REGISTER, IO_INDEX_PORT); + outb_p(0x08, IO_DATA_PORT); + outb_p(0xF2, IO_INDEX_PORT); + outb_p(0xFF, IO_DATA_PORT); + outb_p(0xF3, IO_INDEX_PORT); + outb_p(0x00, IO_DATA_PORT); + outb_p(0xF4, IO_INDEX_PORT); + outb_p(0x00, IO_DATA_PORT); + outb_p(0xF2, IO_INDEX_PORT); + outb_p(0x00, IO_DATA_PORT); + + /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ + outb_p(DEVICE_REGISTER, IO_INDEX_PORT); + outb_p(0x07, IO_DATA_PORT); + outb_p(0xE6, IO_INDEX_PORT); + outb_p(0x08, IO_DATA_PORT); + + /* lock the SuperIO chip */ + outb_p(LOCK_DATA, IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + printk(KERN_INFO PFX "shutdown.\n"); + + return 0; +} + +/* + * Send a keepalive ping to the watchdog + * This is done by simply re-writing the timeout to reg. 0xF2 + */ + +static int wdt977_keepalive(void) +{ + unsigned long flags; + spin_lock_irqsave(&spinlock, flags); + + /* unlock the SuperIO chip */ + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + + /* select device Aux2 (device=8) and kicks watchdog reg F2 */ + /* F2 has the timeout in minutes */ + outb_p(DEVICE_REGISTER, IO_INDEX_PORT); + outb_p(0x08, IO_DATA_PORT); + outb_p(0xF2, IO_INDEX_PORT); + outb_p(timeoutM, IO_DATA_PORT); + + /* lock the SuperIO chip */ + outb_p(LOCK_DATA, IO_INDEX_PORT); + spin_unlock_irqrestore(&spinlock, flags); + + return 0; +} + +/* + * Set the watchdog timeout value + */ + +static int wdt977_set_timeout(int t) +{ + int tmrval; + + /* convert seconds to minutes, rounding up */ + tmrval = (t + 59) / 60; + + if (machine_is_netwinder()) { + /* we have a hw bug somewhere, so each 977 minute is actually only 30sec + * this limits the max timeout to half of device max of 255 minutes... + */ + tmrval += tmrval; + } + + if ((tmrval < 1) || (tmrval > 255)) + return -EINVAL; + + /* timeout is the timeout in seconds, timeoutM is the timeout in minutes) */ + timeout = t; + timeoutM = tmrval; + return 0; +} + +/* + * Get the watchdog status + */ + +static int wdt977_get_status(int *status) +{ + int new_status; + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + + /* unlock the SuperIO chip */ + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + outb_p(UNLOCK_DATA, IO_INDEX_PORT); + + /* select device Aux2 (device=8) and read watchdog reg F4 */ + outb_p(DEVICE_REGISTER, IO_INDEX_PORT); + outb_p(0x08, IO_DATA_PORT); + outb_p(0xF4, IO_INDEX_PORT); + new_status = inb_p(IO_DATA_PORT); + + /* lock the SuperIO chip */ + outb_p(LOCK_DATA, IO_INDEX_PORT); + + spin_unlock_irqrestore(&spinlock, flags); + + *status=0; + if (new_status & 1) + *status |= WDIOF_CARDRESET; + + return 0; +} + + +/* + * /dev/watchdog handling + */ + +static int wdt977_open(struct inode *inode, struct file *file) +{ + /* If the watchdog is alive we don't need to start it again */ + if( test_and_set_bit(0,&timer_alive) ) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + wdt977_start(); + return nonseekable_open(inode, file); +} + +static int wdt977_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (expect_close == 42) + { + wdt977_stop(); + clear_bit(0,&timer_alive); + } else { + wdt977_keepalive(); + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + } + expect_close = 0; + return 0; +} + + +/* + * wdt977_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t wdt977_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) + { + if (!nowayout) + { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != count; i++) + { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + + /* someone wrote to us, we should restart timer */ + wdt977_keepalive(); + } + return count; +} + +/* + * wdt977_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = WATCHDOG_NAME, +}; + +static int wdt977_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int status; + int new_options, retval = -EINVAL; + int new_timeout; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + + 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: + wdt977_get_status(&status); + return put_user(status, uarg.i); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, uarg.i); + + case WDIOC_KEEPALIVE: + wdt977_keepalive(); + return 0; + + case WDIOC_SETOPTIONS: + if (get_user (new_options, uarg.i)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + wdt977_stop(); + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + wdt977_start(); + retval = 0; + } + + return retval; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + + if (wdt977_set_timeout(new_timeout)) + return -EINVAL; + + wdt977_keepalive(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout, uarg.i); + + } +} + +static int wdt977_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + wdt977_stop(); + return NOTIFY_DONE; +} + +static const struct file_operations wdt977_fops= +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt977_write, + .ioctl = wdt977_ioctl, + .open = wdt977_open, + .release = wdt977_release, +}; + +static struct miscdevice wdt977_miscdev= +{ + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt977_fops, +}; + +static struct notifier_block wdt977_notifier = { + .notifier_call = wdt977_notify_sys, +}; + +static int __init wd977_init(void) +{ + int rc; + + //if (!machine_is_netwinder()) + // return -ENODEV; + + printk(KERN_INFO PFX DRIVER_VERSION); + + spin_lock_init(&spinlock); + + /* Check that the timeout value is within it's range ; if not reset to the default */ + if (wdt977_set_timeout(timeout)) + { + wdt977_set_timeout(DEFAULT_TIMEOUT); + printk(KERN_INFO PFX "timeout value must be 60"); +MODULE_DESCRIPTION("W83977AF Watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c new file mode 100644 index 0000000..6baf4ae --- /dev/null +++ b/drivers/watchdog/wdt_pci.c @@ -0,0 +1,756 @@ +/* + * Industrial Computer Source PCI-WDT500/501 driver + * + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox + * + * Release 0.10. + * + * Fixes + * Dave Gregorich : Modularisation and minor bugs + * Alan Cox : Added the watchdog ioctl() stuff + * Alan Cox : Fixed the reboot problem (as noted by + * Matt Crocker). + * Alan Cox : Added wdt= boot option + * Alan Cox : Cleaned up copy/user stuff + * Tim Hockin : Added insmod parameters, comment cleanup + * Parameterized timeout + * JP Nollmann : Added support for PCI wdt501p + * Alan Cox : Split ISA and PCI cards into two drivers + * Jeff Garzik : PCI cleanups + * Tigran Aivazian : Restructured wdtpci_init_one() to handle failures + * Joel Becker : Added WDIOC_GET/SETTIMEOUT + * Zwane Mwaikambo : Magic char closing, locking changes, cleanups + * Matt Domsch : nowayout module option + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WDT_IS_PCI +#include "wd501p.h" + +#define PFX "wdt_pci: " + +/* + * Until Access I/O gets their application for a PCI vendor ID approved, + * I don't think that it's appropriate to move these constants into the + * regular pci_ids.h file. -- JPN 2000/01/18 + */ + +#ifndef PCI_VENDOR_ID_ACCESSIO +#define PCI_VENDOR_ID_ACCESSIO 0x494f +#endif +#ifndef PCI_DEVICE_ID_WDG_CSM +#define PCI_DEVICE_ID_WDG_CSM 0x22c0 +#endif + +/* We can only use 1 card due to the /dev/watchdog restriction */ +static int dev_count; + +static struct semaphore open_sem; +static spinlock_t wdtpci_lock; +static char expect_close; + +static int io; +static int irq; + +/* Default timeout */ +#define WD_TIMO 60 /* Default heartbeat = 60 seconds */ + +static int heartbeat = WD_TIMO; +static int wd_heartbeat; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0>8, WDT_COUNT0+ctr); +} + +/** + * wdtpci_start: + * + * Start the watchdog driver. + */ + +static int wdtpci_start(void) +{ + unsigned long flags; + + spin_lock_irqsave(&wdtpci_lock, flags); + + /* + * "pet" the watchdog, as Access says. + * This resets the clock outputs. + */ + inb_p(WDT_DC); /* Disable watchdog */ + wdtpci_ctr_mode(2,0); /* Program CTR2 for Mode 0: Pulse on Terminal Count */ + outb_p(0, WDT_DC); /* Enable watchdog */ + + inb_p(WDT_DC); /* Disable watchdog */ + outb_p(0, WDT_CLOCK); /* 2.0833MHz clock */ + inb_p(WDT_BUZZER); /* disable */ + inb_p(WDT_OPTONOTRST); /* disable */ + inb_p(WDT_OPTORST); /* disable */ + inb_p(WDT_PROGOUT); /* disable */ + wdtpci_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ + wdtpci_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ + wdtpci_ctr_mode(2,1); /* Program CTR2 for Mode 1: Retriggerable One-Shot */ + wdtpci_ctr_load(0,20833); /* count at 100Hz */ + wdtpci_ctr_load(1,wd_heartbeat);/* Heartbeat */ + /* DO NOT LOAD CTR2 on PCI card! -- JPN */ + outb_p(0, WDT_DC); /* Enable watchdog */ + + spin_unlock_irqrestore(&wdtpci_lock, flags); + return 0; +} + +/** + * wdtpci_stop: + * + * Stop the watchdog driver. + */ + +static int wdtpci_stop (void) +{ + unsigned long flags; + + /* Turn the card off */ + spin_lock_irqsave(&wdtpci_lock, flags); + inb_p(WDT_DC); /* Disable watchdog */ + wdtpci_ctr_load(2,0); /* 0 length reset pulses now */ + spin_unlock_irqrestore(&wdtpci_lock, flags); + return 0; +} + +/** + * wdtpci_ping: + * + * Reload counter one with the watchdog heartbeat. We don't bother reloading + * the cascade counter. + */ + +static int wdtpci_ping(void) +{ + unsigned long flags; + + /* Write a watchdog value */ + spin_lock_irqsave(&wdtpci_lock, flags); + inb_p(WDT_DC); /* Disable watchdog */ + wdtpci_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ + wdtpci_ctr_load(1,wd_heartbeat);/* Heartbeat */ + outb_p(0, WDT_DC); /* Enable watchdog */ + spin_unlock_irqrestore(&wdtpci_lock, flags); + return 0; +} + +/** + * wdtpci_set_heartbeat: + * @t: the new heartbeat value that needs to be set. + * + * Set a new heartbeat value for the watchdog device. If the heartbeat value is + * incorrect we keep the old value and return -EINVAL. If successfull we + * return 0. + */ +static int wdtpci_set_heartbeat(int t) +{ + /* Arbitrary, can't find the card's limits */ + if ((t < 1) || (t > 65535)) + return -EINVAL; + + heartbeat = t; + wd_heartbeat = t * 100; + return 0; +} + +/** + * wdtpci_get_status: + * @status: the new status. + * + * Extract the status information from a WDT watchdog device. There are + * several board variants so we have to know which bits are valid. Some + * bits default to one and some to zero in order to be maximally painful. + * + * we then map the bits onto the status ioctl flags. + */ + +static int wdtpci_get_status(int *status) +{ + unsigned char new_status=inb_p(WDT_SR); + + *status=0; + if (new_status & WDC_SR_ISOI0) + *status |= WDIOF_EXTERN1; + if (new_status & WDC_SR_ISII1) + *status |= WDIOF_EXTERN2; +#ifdef CONFIG_WDT_501_PCI + if (!(new_status & WDC_SR_TGOOD)) + *status |= WDIOF_OVERHEAT; + if (!(new_status & WDC_SR_PSUOVER)) + *status |= WDIOF_POWEROVER; + if (!(new_status & WDC_SR_PSUUNDR)) + *status |= WDIOF_POWERUNDER; + if (tachometer) { + if (!(new_status & WDC_SR_FANGOOD)) + *status |= WDIOF_FANFAULT; + } +#endif /* CONFIG_WDT_501_PCI */ + return 0; +} + +#ifdef CONFIG_WDT_501_PCI +/** + * wdtpci_get_temperature: + * + * Reports the temperature in degrees Fahrenheit. The API is in + * farenheit. It was designed by an imperial measurement luddite. + */ + +static int wdtpci_get_temperature(int *temperature) +{ + unsigned short c=inb_p(WDT_RT); + + *temperature = (c * 11 / 15) + 7; + return 0; +} +#endif /* CONFIG_WDT_501_PCI */ + +/** + * wdtpci_interrupt: + * @irq: Interrupt number + * @dev_id: Unused as we don't allow multiple devices. + * + * Handle an interrupt from the board. These are raised when the status + * map changes in what the board considers an interesting way. That means + * a failure condition occurring. + */ + +static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) +{ + /* + * Read the status register see what is up and + * then printk it. + */ + unsigned char status=inb_p(WDT_SR); + + printk(KERN_CRIT PFX "status %d\n", status); + +#ifdef CONFIG_WDT_501_PCI + if (!(status & WDC_SR_TGOOD)) + printk(KERN_CRIT PFX "Overheat alarm.(%d)\n",inb_p(WDT_RT)); + if (!(status & WDC_SR_PSUOVER)) + printk(KERN_CRIT PFX "PSU over voltage.\n"); + if (!(status & WDC_SR_PSUUNDR)) + printk(KERN_CRIT PFX "PSU under voltage.\n"); + if (tachometer) { + if (!(status & WDC_SR_FANGOOD)) + printk(KERN_CRIT PFX "Possible fan fault.\n"); + } +#endif /* CONFIG_WDT_501_PCI */ + if (!(status&WDC_SR_WCCR)) +#ifdef SOFTWARE_REBOOT +#ifdef ONLY_TESTING + printk(KERN_CRIT PFX "Would Reboot.\n"); +#else + printk(KERN_CRIT PFX "Initiating system reboot.\n"); + emergency_restart(NULL); +#endif +#else + printk(KERN_CRIT PFX "Reset in 5ms.\n"); +#endif + return IRQ_HANDLED; +} + + +/** + * wdtpci_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ + +static ssize_t wdtpci_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; + } + } + wdtpci_ping(); + } + + return count; +} + +/** + * wdtpci_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. We only actually usefully support + * querying capabilities and current status. + */ + +static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_heartbeat; + int status; + void __user *argp = (void __user *)arg; + int __user *p = argp; + + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT| + WDIOF_MAGICCLOSE| + WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = "PCI-WDT500/501", + }; + + /* Add options according to the card we have */ + ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); +#ifdef CONFIG_WDT_501_PCI + ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); + if (tachometer) + ident.options |= WDIOF_FANFAULT; +#endif /* CONFIG_WDT_501_PCI */ + + switch(cmd) + { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0; + + case WDIOC_GETSTATUS: + wdtpci_get_status(&status); + return put_user(status, p); + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + case WDIOC_KEEPALIVE: + wdtpci_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, p)) + return -EFAULT; + + if (wdtpci_set_heartbeat(new_heartbeat)) + return -EINVAL; + + wdtpci_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + } +} + +/** + * wdtpci_open: + * @inode: inode of device + * @file: file handle to device + * + * The watchdog device has been opened. The watchdog device is single + * open and on opening we load the counters. Counter zero is a 100Hz + * cascade, into counter 1 which downcounts to reboot. When the counter + * triggers counter 2 downcounts the length of the reset pulse which + * set set to be as long as possible. + */ + +static int wdtpci_open(struct inode *inode, struct file *file) +{ + if (down_trylock(&open_sem)) + return -EBUSY; + + if (nowayout) { + __module_get(THIS_MODULE); + } + /* + * Activate + */ + wdtpci_start(); + return nonseekable_open(inode, file); +} + +/** + * wdtpci_release: + * @inode: inode to board + * @file: file handle to board + * + * The watchdog has a configurable API. There is a religious dispute + * between people who want their watchdog to be able to shut down and + * those who want to be sure if the watchdog manager dies the machine + * reboots. In the former case we disable the counters, in the latter + * case you have to open it again very soon. + */ + +static int wdtpci_release(struct inode *inode, struct file *file) +{ + if (expect_close == 42) { + wdtpci_stop(); + } else { + printk(KERN_CRIT PFX "Unexpected close, not stopping timer!"); + wdtpci_ping(); + } + expect_close = 0; + up(&open_sem); + return 0; +} + +#ifdef CONFIG_WDT_501_PCI +/** + * wdtpci_temp_read: + * @file: file handle to the watchdog board + * @buf: buffer to write 1 byte into + * @count: length of buffer + * @ptr: offset (no seek allowed) + * + * Read reports the temperature in degrees Fahrenheit. The API is in + * fahrenheit. It was designed by an imperial measurement luddite. + */ + +static ssize_t wdtpci_temp_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) +{ + int temperature; + + if (wdtpci_get_temperature(&temperature)) + return -EFAULT; + + if (copy_to_user (buf, &temperature, 1)) + return -EFAULT; + + return 1; +} + +/** + * wdtpci_temp_open: + * @inode: inode of device + * @file: file handle to device + * + * The temperature device has been opened. + */ + +static int wdtpci_temp_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +/** + * wdtpci_temp_release: + * @inode: inode to board + * @file: file handle to board + * + * The temperature device has been closed. + */ + +static int wdtpci_temp_release(struct inode *inode, struct file *file) +{ + return 0; +} +#endif /* CONFIG_WDT_501_PCI */ + +/** + * notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ + +static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code==SYS_DOWN || code==SYS_HALT) { + /* Turn the card off */ + wdtpci_stop(); + } + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + + +static const struct file_operations wdtpci_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdtpci_write, + .ioctl = wdtpci_ioctl, + .open = wdtpci_open, + .release = wdtpci_release, +}; + +static struct miscdevice wdtpci_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdtpci_fops, +}; + +#ifdef CONFIG_WDT_501_PCI +static const struct file_operations wdtpci_temp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = wdtpci_temp_read, + .open = wdtpci_temp_open, + .release = wdtpci_temp_release, +}; + +static struct miscdevice temp_miscdev = { + .minor = TEMP_MINOR, + .name = "temperature", + .fops = &wdtpci_temp_fops, +}; +#endif /* CONFIG_WDT_501_PCI */ + +/* + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdtpci_notifier = { + .notifier_call = wdtpci_notify_sys, +}; + + +static int __devinit wdtpci_init_one (struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int ret = -EIO; + + dev_count++; + if (dev_count > 1) { + printk (KERN_ERR PFX "this driver only supports 1 device\n"); + return -ENODEV; + } + + if (pci_enable_device (dev)) { + printk (KERN_ERR PFX "Not possible to enable PCI Device\n"); + return -ENODEV; + } + + if (pci_resource_start (dev, 2) == 0x0000) { + printk (KERN_ERR PFX "No I/O-Address for card detected\n"); + ret = -ENODEV; + goto out_pci; + } + + sema_init(&open_sem, 1); + spin_lock_init(&wdtpci_lock); + + irq = dev->irq; + io = pci_resource_start (dev, 2); + + if (request_region (io, 16, "wdt_pci") == NULL) { + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n", io); + goto out_pci; + } + + if (request_irq (irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED, + "wdt_pci", &wdtpci_miscdev)) { + printk (KERN_ERR PFX "IRQ %d is not free\n", irq); + goto out_reg; + } + + printk ("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)\n", + io, irq); + + /* Check that the heartbeat value is within it's range ; if not reset to the default */ + if (wdtpci_set_heartbeat(heartbeat)) { + wdtpci_set_heartbeat(WD_TIMO); + printk(KERN_INFO PFX "heartbeat value must be 0