From: Ethan Nelson-Moore <enelsonmoore@gmail.com>
To: linux-watchdog@vger.kernel.org
Cc: Ethan Nelson-Moore <enelsonmoore@gmail.com>,
Wim Van Sebroeck <wim@linux-watchdog.org>,
Guenter Roeck <linux@roeck-us.net>
Subject: [PATCH] watchdog: remove drivers for discrete ISA WDT cards
Date: Mon, 4 May 2026 19:30:00 -0700 [thread overview]
Message-ID: <20260505023007.58175-1-enelsonmoore@gmail.com> (raw)
The mixcomwd, pcwd, and wdt (how generic!) drivers support discrete ISA
watchdog cards. The wdt driver in particular does not support
autoprobing and requires manual specification of the I/O port and IRQ
the card uses. A WDT is integrated into many modern chipsets and Super
I/O chips. Embedded boards using a WDT, even ones with ISA slots, are
unlikely to do so via a discrete ISA card due to the amount of space
required. Given these factors, it is highly unlikely anyone is using
these drivers. Remove them.
Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
drivers/watchdog/Kconfig | 49 --
drivers/watchdog/Makefile | 9 +-
drivers/watchdog/mixcomwd.c | 312 -----------
drivers/watchdog/pcwd.c | 996 ------------------------------------
drivers/watchdog/wdt.c | 662 ------------------------
5 files changed, 2 insertions(+), 2026 deletions(-)
delete mode 100644 drivers/watchdog/mixcomwd.c
delete mode 100644 drivers/watchdog/pcwd.c
delete mode 100644 drivers/watchdog/wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index dc78729ba2a5..d826c76c1c68 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -2232,55 +2232,6 @@ config UML_WATCHDOG
tristate "UML watchdog"
depends on UML || COMPILE_TEST
-#
-# 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 <file:Documentation/watchdog/pcwd-watchdog.rst>.
- The PC watchdog cards can be ordered from <http://www.berkprod.com/>.
-
- 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.
-
#
# PCI-based Watchdog Cards
#
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index d2fb16b9f9ce..f10fd646b19c 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -14,19 +14,14 @@ watchdog-$(CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT) += watchdog_hrtimer_pretimeout.o
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o
obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o
-# Only one watchdog can succeed. We probe the ISA/PCI/USB based
+# Only one watchdog can succeed. We probe the PCI/USB based
# watchdog-cards first, then the architecture specific watchdog
# drivers and then the architecture independent "softdog" driver.
-# This means that if your ISA/PCI/USB card isn't detected that
+# This means that if your 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
diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c
deleted file mode 100644
index 1ecd5c48a005..000000000000
--- a/drivers/watchdog/mixcomwd.c
+++ /dev/null
@@ -1,312 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * MixCom Watchdog: A Simple Hardware Watchdog Device
- * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
- *
- * Author: Gergely Madarasz <gorgo@itc.hu>
- *
- * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
- *
- * 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 <Matt_Domsch@dell.com>
- * - added nowayout module option to override
- * CONFIG_WATCHDOG_NOWAYOUT
- *
- * Version 0.6 (2002/04/12): Rob Radez <rob@osinvestor.com>
- * - make mixcomwd_opened unsigned,
- * removed lock_kernel/unlock_kernel from mixcomwd_release,
- * modified ioctl a bit to conform to API
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define VERSION "0.6"
-#define WATCHDOG_NAME "mixcomwd"
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/ioport.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/jiffies.h>
-#include <linux/timer.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
-/*
- * 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[] = {
- /* 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(struct timer_list *unused);
-
-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);
-static char expect_close;
-
-static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, bool, 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(struct timer_list *unused)
-{
- 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) {
- timer_delete(&mixcomwd_timer);
- mixcomwd_timer_alive = 0;
- }
- }
- return stream_open(inode, file);
-}
-
-static int mixcomwd_release(struct inode *inode, struct file *file)
-{
- if (expect_close == 42) {
- if (mixcomwd_timer_alive) {
- pr_err("release called while internal timer alive\n");
- return -EBUSY;
- }
- mixcomwd_timer_alive = 1;
- mod_timer(&mixcomwd_timer, jiffies + 5 * HZ);
- } else
- pr_crit("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 long mixcomwd_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int status;
- static const struct watchdog_info ident = {
- .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
- .firmware_version = 1,
- .identity = "MixCOM watchdog",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof(ident)))
- return -EFAULT;
- break;
- case WDIOC_GETSTATUS:
- status = mixcomwd_opened;
- if (!nowayout)
- status |= mixcomwd_timer_alive;
- return put_user(status, p);
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_KEEPALIVE:
- mixcomwd_ping();
- break;
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static const struct file_operations mixcomwd_fops = {
- .owner = THIS_MODULE,
- .write = mixcomwd_write,
- .unlocked_ioctl = mixcomwd_ioctl,
- .compat_ioctl = compat_ptr_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, ret, 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) {
- pr_err("No card detected, or port not available\n");
- return -ENODEV;
- }
-
- ret = misc_register(&mixcomwd_miscdev);
- if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto error_misc_register_watchdog;
- }
-
- pr_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) {
- pr_warn("I quit now, hardware will probably reboot!\n");
- timer_delete_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 <gorgo@itc.hu>");
-MODULE_DESCRIPTION("MixCom Watchdog driver");
-MODULE_VERSION(VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c
deleted file mode 100644
index d4ea7d6ccd6a..000000000000
--- a/drivers/watchdog/pcwd.c
+++ /dev/null
@@ -1,996 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * 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 <meskes@debian.org>.
- * 980112 Used minor numbers from include/linux/miscdevice.h
- * 990403 Clear reset status after reading control status register in
- * pcwd_showprevstate(). [Marc Boucher <marc@mbsi.ca>]
- * 990605 Made changes to code to support Firmware 1.22a, added
- * fairly useless proc entry.
- * 990610 removed said useless proc code for the merge <alan>
- * 000403 Removed last traces of proc code. <davej>
- * 011214 Added nowayout module option to override
- * CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
- * 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/
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h> /* For module specific items */
-#include <linux/moduleparam.h> /* For new moduleparam's */
-#include <linux/types.h> /* For standard types (like size_t) */
-#include <linux/errno.h> /* For the -ENODEV/... values */
-#include <linux/kernel.h> /* For printk/panic/... */
-#include <linux/delay.h> /* For mdelay function */
-#include <linux/timer.h> /* For timer related operations */
-#include <linux/jiffies.h> /* For jiffies stuff */
-#include <linux/miscdevice.h> /* For struct miscdevice */
-#include <linux/watchdog.h> /* For the watchdog specific items */
-#include <linux/reboot.h> /* For kernel_power_off() */
-#include <linux/init.h> /* For __init/__exit/... */
-#include <linux/fs.h> /* For file operations */
-#include <linux/isa.h> /* For isa devices */
-#include <linux/ioport.h> /* For io-port access */
-#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
-#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
-#include <linux/io.h> /* 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 DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
-
-/*
- * 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 unsigned long open_allowed;
-static char expect_close;
-static int temp_panic;
-
-/* this is private data for each ISA-PC watchdog card */
-static struct {
- char fw_ver_str[6]; /* The cards firmware version */
- int revision; /* The card's revision */
- int supports_temp; /* Whether or not the card has
- a temperature device */
- int command_mode; /* Whether 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)");
-
-/* default heartbeat = delay-time from dip-switches */
-#define WATCHDOG_HEARTBEAT 0
-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 bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, bool, 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)
- pr_debug("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)
- pr_debug("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)
- pr_debug("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)
- pr_debug("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)
- pr_info("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();
- pr_info("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();
- pr_info("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)
- pr_info("Temperature Option Detected\n");
-
- if (pcwd_private.boot_status & WDIOF_CARDRESET)
- pr_info("Previous reboot was caused by the card\n");
-
- if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
- pr_emerg("Card senses a CPU Overheat. Panicking!\n");
- pr_emerg("CPU Overheat\n");
- }
-
- if (pcwd_private.boot_status == 0)
- pr_info("No previous trip detected - Cold boot or reset\n");
-}
-
-static void pcwd_timer_ping(struct timer_list *unused)
-{
- 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 {
- pr_warn("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) {
- pr_info("Could not start watchdog\n");
- return -EIO;
- }
- }
-
- if (debug >= VERBOSE)
- pr_debug("Watchdog started\n");
-
- return 0;
-}
-
-static int pcwd_stop(void)
-{
- int stat_reg;
-
- /* Stop the timer */
- timer_delete(&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) {
- pr_info("Could not stop watchdog\n");
- return -EIO;
- }
- }
-
- if (debug >= VERBOSE)
- pr_debug("Watchdog stopped\n");
-
- return 0;
-}
-
-static int pcwd_keepalive(void)
-{
- /* user land ping */
- pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
-
- if (debug >= DEBUG)
- pr_debug("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)
- pr_debug("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) {
- pr_info("Temperature overheat trip!\n");
- kernel_power_off();
- }
- }
- } else {
- if (control_status & WD_REVC_WTRP)
- *status |= WDIOF_CARDRESET;
-
- if (control_status & WD_REVC_TTRP) {
- *status |= WDIOF_OVERHEAT;
- if (temp_panic) {
- pr_info("Temperature overheat trip!\n");
- kernel_power_off();
- }
- }
- }
-
- 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)
- pr_info("clearing watchdog trip status\n");
-
- control_status = inb_p(pcwd_private.io_addr + 1);
-
- if (debug >= DEBUG) {
- pr_debug("status was: 0x%02x\n", control_status);
- pr_debug("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) {
- pr_debug("temperature is: %d F\n", *temperature);
- }
-
- return 0;
-}
-
-/*
- * /dev/watchdog handling
- */
-
-static long pcwd_ioctl(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 const struct watchdog_info ident = {
- .options = WDIOF_OVERHEAT |
- WDIOF_CARDRESET |
- WDIOF_KEEPALIVEPING |
- WDIOF_SETTIMEOUT |
- WDIOF_MAGICCLOSE,
- .firmware_version = 1,
- .identity = "PCWD",
- };
-
- switch (cmd) {
- 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 (get_user(rv, argp))
- return -EFAULT;
-
- if (rv & WDIOS_DISABLECARD) {
- status = pcwd_stop();
- if (status < 0)
- return status;
- }
- if (rv & WDIOS_ENABLECARD) {
- status = pcwd_start();
- if (status < 0)
- return status;
- }
- 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();
- fallthrough;
-
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, argp);
-
- default:
- return -ENOTTY;
- }
-
- 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 (test_and_set_bit(0, &open_allowed))
- return -EBUSY;
- if (nowayout)
- __module_get(THIS_MODULE);
- /* Activate */
- pcwd_start();
- pcwd_keepalive();
- return stream_open(inode, file);
-}
-
-static int pcwd_close(struct inode *inode, struct file *file)
-{
- if (expect_close == 42)
- pcwd_stop();
- else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- pcwd_keepalive();
- }
- expect_close = 0;
- clear_bit(0, &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 stream_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,
- .write = pcwd_write,
- .unlocked_ioctl = pcwd_ioctl,
- .compat_ioctl = compat_ptr_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,
- .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 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)
- pr_debug("pcwd_isa_match id=%d\n", id);
-
- if (!request_region(base_addr, 4, "PCWD")) {
- pr_info("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 heartbeat 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 pcwd_isa_probe(struct device *dev, unsigned int id)
-{
- int ret;
-
- if (debug >= DEBUG)
- pr_debug("pcwd_isa_probe id=%d\n", id);
-
- cards_found++;
- if (cards_found == 1)
- pr_info("v%s Ken Hollis (kenji@bitgate.com)\n",
- WATCHDOG_VERSION);
-
- if (cards_found > 1) {
- pr_err("This driver only supports 1 device\n");
- return -ENODEV;
- }
-
- if (pcwd_ioports[id] == 0x0000) {
- pr_err("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")) {
- pr_err("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();
-
- timer_setup(&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);
- pr_info("heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
- WATCHDOG_HEARTBEAT);
- }
-
- if (pcwd_private.supports_temp) {
- ret = misc_register(&temp_miscdev);
- if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- TEMP_MINOR, ret);
- goto error_misc_register_temp;
- }
- }
-
- ret = misc_register(&pcwd_miscdev);
- if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto error_misc_register_watchdog;
- }
-
- pr_info("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 void pcwd_isa_remove(struct device *dev, unsigned int id)
-{
- if (debug >= DEBUG)
- pr_debug("pcwd_isa_remove id=%d\n", id);
-
- /* 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--;
-}
-
-static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
-{
- if (debug >= DEBUG)
- pr_debug("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 = pcwd_isa_remove,
- .shutdown = pcwd_isa_shutdown,
- .driver = {
- .owner = THIS_MODULE,
- .name = WATCHDOG_NAME,
- },
-};
-
-module_isa_driver(pcwd_isa_driver, PCWD_ISA_NR_CARDS);
-
-MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
- "Wim Van Sebroeck <wim@iguana.be>");
-MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
-MODULE_VERSION(WATCHDOG_VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c
deleted file mode 100644
index 3980d60bacd8..000000000000
--- a/drivers/watchdog/wdt.c
+++ /dev/null
@@ -1,662 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Industrial Computer Source WDT501 driver
- *
- * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
- * All Rights Reserved.
- *
- * 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 <alan@lxorguk.ukuu.org.uk>
- *
- * 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
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/fs.h>
-#include <linux/ioport.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-
-#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 < heartbeat < 65536, default="
- __MODULE_STRING(WD_TIMO) ")");
-
-static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
- "Watchdog cannot be stopped once started (default="
- __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-
-/* You must set these - there is no sane way to probe for this board. */
-static int io = 0x240;
-static int irq = 11;
-
-static DEFINE_SPINLOCK(wdt_lock);
-
-module_param_hw(io, int, ioport, 0);
-MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
-module_param_hw(irq, int, irq, 0);
-MODULE_PARM_DESC(irq, "WDT irq (default=11)");
-
-/* Support for the Fan Tachometer on the WDT501-P */
-static int tachometer;
-module_param(tachometer, int, 0);
-MODULE_PARM_DESC(tachometer,
- "WDT501-P Fan Tachometer support (0=disable, default=0)");
-
-static int type = 500;
-module_param(type, int, 0);
-MODULE_PARM_DESC(type,
- "WDT501-P Card type (500 or 501, default=500)");
-
-/*
- * Programming support
- */
-
-static void wdt_ctr_mode(int ctr, int mode)
-{
- ctr <<= 6;
- ctr |= 0x30;
- ctr |= (mode << 1);
- outb_p(ctr, WDT_CR);
-}
-
-static void wdt_ctr_load(int ctr, int val)
-{
- outb_p(val&0xFF, WDT_COUNT0+ctr);
- outb_p(val>>8, WDT_COUNT0+ctr);
-}
-
-/**
- * wdt_start:
- *
- * Start the watchdog driver.
- */
-
-static int wdt_start(void)
-{
- unsigned long flags;
- spin_lock_irqsave(&wdt_lock, flags);
- 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 */
- spin_unlock_irqrestore(&wdt_lock, flags);
- return 0;
-}
-
-/**
- * wdt_stop:
- *
- * Stop the watchdog driver.
- */
-
-static int wdt_stop(void)
-{
- unsigned long flags;
- spin_lock_irqsave(&wdt_lock, flags);
- /* Turn the card off */
- inb_p(WDT_DC); /* Disable watchdog */
- wdt_ctr_load(2, 0); /* 0 length reset pulses now */
- spin_unlock_irqrestore(&wdt_lock, flags);
- return 0;
-}
-
-/**
- * wdt_ping:
- *
- * Reload counter one with the watchdog heartbeat. We don't bother
- * reloading the cascade counter.
- */
-
-static void wdt_ping(void)
-{
- unsigned long flags;
- spin_lock_irqsave(&wdt_lock, flags);
- /* 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 */
- spin_unlock_irqrestore(&wdt_lock, flags);
-}
-
-/**
- * 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
- * successful 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:
- *
- * 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(void)
-{
- unsigned char new_status;
- int status = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&wdt_lock, flags);
- new_status = inb_p(WDT_SR);
- spin_unlock_irqrestore(&wdt_lock, flags);
-
- if (new_status & WDC_SR_ISOI0)
- status |= WDIOF_EXTERN1;
- if (new_status & WDC_SR_ISII1)
- status |= WDIOF_EXTERN2;
- if (type == 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;
- }
- }
- return status;
-}
-
-/**
- * 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(void)
-{
- unsigned short c;
- unsigned long flags;
-
- spin_lock_irqsave(&wdt_lock, flags);
- c = inb_p(WDT_RT);
- spin_unlock_irqrestore(&wdt_lock, flags);
- return (c * 11 / 15) + 7;
-}
-
-static void wdt_decode_501(int status)
-{
- if (!(status & WDC_SR_TGOOD))
- pr_crit("Overheat alarm (%d)\n", inb_p(WDT_RT));
- if (!(status & WDC_SR_PSUOVER))
- pr_crit("PSU over voltage\n");
- if (!(status & WDC_SR_PSUUNDR))
- pr_crit("PSU under voltage\n");
-}
-
-/**
- * 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;
-
- spin_lock(&wdt_lock);
- status = inb_p(WDT_SR);
-
- pr_crit("WDT status %d\n", status);
-
- if (type == 501) {
- wdt_decode_501(status);
- if (tachometer) {
- if (!(status & WDC_SR_FANGOOD))
- pr_crit("Possible fan fault\n");
- }
- }
- if (!(status & WDC_SR_WCCR)) {
-#ifdef SOFTWARE_REBOOT
-#ifdef ONLY_TESTING
- pr_crit("Would Reboot\n");
-#else
- pr_crit("Initiating system reboot\n");
- emergency_restart();
-#endif
-#else
- pr_crit("Reset in 5ms\n");
-#endif
- }
- spin_unlock(&wdt_lock);
- 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:
- * @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 long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- int new_heartbeat;
- int status;
-
- 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);
- if (type == 501) {
- ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
- WDIOF_POWEROVER);
- if (tachometer)
- ident.options |= WDIOF_FANFAULT;
- }
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETSTATUS:
- status = wdt_get_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();
- fallthrough;
- case WDIOC_GETTIMEOUT:
- return put_user(heartbeat, p);
- default:
- return -ENOTTY;
- }
-}
-
-/**
- * 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 stream_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 {
- pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
- wdt_ping();
- }
- expect_close = 0;
- return 0;
-}
-
-/**
- * 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 = wdt_get_temperature();
-
- 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 stream_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;
-}
-
-/**
- * wdt_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)
- wdt_stop();
- return NOTIFY_DONE;
-}
-
-/*
- * Kernel Interfaces
- */
-
-
-static const struct file_operations wdt_fops = {
- .owner = THIS_MODULE,
- .write = wdt_write,
- .unlocked_ioctl = wdt_ioctl,
- .compat_ioctl = compat_ptr_ioctl,
- .open = wdt_open,
- .release = wdt_release,
-};
-
-static struct miscdevice wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &wdt_fops,
-};
-
-static const struct file_operations wdt_temp_fops = {
- .owner = THIS_MODULE,
- .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,
-};
-
-/*
- * 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,
-};
-
-/**
- * wdt_exit:
- *
- * 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);
- if (type == 501)
- misc_deregister(&temp_miscdev);
- 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;
-
- if (type != 500 && type != 501) {
- pr_err("unknown card type '%d'\n", type);
- return -ENODEV;
- }
-
- /* 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);
- pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
- WD_TIMO);
- }
-
- if (!request_region(io, 8, "wdt501p")) {
- pr_err("I/O address 0x%04x already in use\n", io);
- ret = -EBUSY;
- goto out;
- }
-
- ret = request_irq(irq, wdt_interrupt, 0, "wdt501p", NULL);
- if (ret) {
- pr_err("IRQ %d is not free\n", irq);
- goto outreg;
- }
-
- ret = register_reboot_notifier(&wdt_notifier);
- if (ret) {
- pr_err("cannot register reboot notifier (err=%d)\n", ret);
- goto outirq;
- }
-
- if (type == 501) {
- ret = misc_register(&temp_miscdev);
- if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- TEMP_MINOR, ret);
- goto outrbt;
- }
- }
-
- ret = misc_register(&wdt_miscdev);
- if (ret) {
- pr_err("cannot register miscdev on minor=%d (err=%d)\n",
- WATCHDOG_MINOR, ret);
- goto outmisc;
- }
-
- pr_info("WDT500/501-P driver 0.10 at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)\n",
- io, irq, heartbeat, nowayout);
- if (type == 501)
- pr_info("Fan Tachometer is %s\n",
- tachometer ? "Enabled" : "Disabled");
- return 0;
-
-outmisc:
- if (type == 501)
- misc_deregister(&temp_miscdev);
-outrbt:
- unregister_reboot_notifier(&wdt_notifier);
-outirq:
- free_irq(irq, NULL);
-outreg:
- release_region(io, 8);
-out:
- return ret;
-}
-
-module_init(wdt_init);
-module_exit(wdt_exit);
-
-MODULE_AUTHOR("Alan Cox");
-MODULE_DESCRIPTION("Driver for ISA ICS watchdog cards (WDT500/501)");
-MODULE_LICENSE("GPL");
--
2.43.0
next reply other threads:[~2026-05-05 2:31 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-05 2:30 Ethan Nelson-Moore [this message]
2026-05-05 3:11 ` [PATCH] watchdog: remove drivers for discrete ISA WDT cards sashiko-bot
2026-05-05 4:59 ` Guenter Roeck
2026-05-05 5:39 ` Ethan Nelson-Moore
2026-05-05 13:05 ` Guenter Roeck
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260505023007.58175-1-enelsonmoore@gmail.com \
--to=enelsonmoore@gmail.com \
--cc=linux-watchdog@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=wim@linux-watchdog.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox