From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-dl1-f66.google.com (mail-dl1-f66.google.com [74.125.82.66]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3802E3A2546 for ; Tue, 5 May 2026 02:31:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.66 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777948265; cv=none; b=ptyHfH5IO1fxvvb2t9SRUv+7rxEjxK8flF7kOI2DUK+fl42Ua4XCWssunmSc67GpVdb+JprOTKh3sLL1vkYY5kRS7Rp2M8VuWtUwwYxEViLLTHxkDzME6J4hrjZI2LacQxfqxblB+heR6npm9hQPSX+6kJAc5f6dutadHl3N8Aw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777948265; c=relaxed/simple; bh=k2eo7lheM84s1zP9NaItebn8hKvoV9HCsYcyicxBi8I=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=esnWDDD0rI0iOTkQZarLW7NmKSc14AH0dszRIKG1Y2RYGWVL2SeY4FiaYyyv65SM2WSEeWgoJ7/ege/wYxWRtZMTwDE2WPia0JdesDZlVEdRYYFjhMHpqsOA6YZDHqBwchVW59YoAKtXMxlAtlkC7kK5jf8orv59+ZxwHHpg5Ks= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=MQkcWgV5; arc=none smtp.client-ip=74.125.82.66 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MQkcWgV5" Received: by mail-dl1-f66.google.com with SMTP id a92af1059eb24-12c8f9846c8so6307638c88.0 for ; Mon, 04 May 2026 19:31:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1777948261; x=1778553061; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=0Hbfuo20dcbnK5GUfjSBfoHZWYOZnaO0HHVFFfdbKXY=; b=MQkcWgV5xIlhFZ/HNsnsZv6PbBrrmRyh7f2x4tqzof6Hir1LgsX4FMd9ApbDQO38hY 13mqhBE72wZvfkLk8gpBccA1g2RbLlUIi5HVjfmPWE8d5L+49ic5vZbvLNKX8r+dYZbt mpMaNsh7FSP9sP47UHegtbXewcApqEk55kK3+5T4nFnM+5ZC7er8Dbnxpfv7XHjPosVx Gfm+BzYXjI1Bhfpt86YKsuRQwP/vcX4ISFZKUmvd7YkuF0renx2ACH/fVipGdTX+cXLp Dc0v7sAviVgbfTGrClbgFHk7QUtEXr+Bqy242Tycb7Kc810L+5UVYP7ep2Z3LdR5QZxL ay0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777948261; x=1778553061; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=0Hbfuo20dcbnK5GUfjSBfoHZWYOZnaO0HHVFFfdbKXY=; b=so8+BUj73Kt4QgwAW3T5JcsDvNKKsDWIcvGJ4zYU+IQMpwPLIueBFCY20eaCUYsliS YaOHmFSSqQjsgSUGLOrfXVBrNms7zz5d+Bo+4hLzgOyvOysls+BoFEMNKnWrj9XIsqs7 vSvpp1VTallFWllvcFuWb5h2+zglCxx4Ek4Cp8DI6otcZUJdp/1k0tNrZwqiRsehkMVf JiPN4Id83+j1pQUTmqqRl+tn61I6sE7xAza+XhnvmotxH0tJr1HmLdChAmvqIhfneCQ7 cslocnYX62045rF5B4nJy23XjwzvAKn5DPDsr89TZJghIdRyT3qV7U7vIIAZrKUYsZ2x 7EUw== X-Gm-Message-State: AOJu0YwiiXHr9BqIA2qSc/rbgBd16tmFCKvV66b5+GRr0eYZP9rtj6Ur i6xNGct5XiivrR2FiwuEspMgUsXDqof9/Dt/ZY6Jw3JcLyt4iTe4fvVFy5b8Np7G X-Gm-Gg: AeBDievdP9LyZsPqiCgusZU8reb6qBHUfOrdqKEQYloqIC5tnuykJlY5VxwBvR3ZLCw aPtqdA1Ns/9OpPpin4Pfc4Wpnwv4BzlmZ9qVQYnHV23CY2u/aeo/AEVlbcpMQr1N5Ait5/e9AT9 ikBCCVlkIfKurjhnWfug/MhVe4yl3fR5yr174zwMtUyFnKnILlItNqP0VILqCjb+qh3V6Zl2qYS YqUJdli5kdWRC3Sy+3ORZnBSNkHVCMGOQhDWwu0JYXiUpSyr3iRqvV5EQX+V0+CBwvkIULba1O/ mv+EIKWSEmYZyUWRj4TIITE2RsQL//JWZ0xKrnCcFxMA+MwV8/kFwzuyEEzOvcsm0ZMBzOiKlN0 CJU11ZYxoOqJaxKYLO87pFTYDVMnQpgt1g4/jdh1I8qEhcjLYqDFlsKKxPa3fQFurXp/iEpOg4T e6e0LZ3w1J/jwEVIf0z6r4wvTA66j3XfZtib8brDsUd5rsJpRY0nVUsG4UYgEcmhhdnUMajVcyt kFbhPLFyAHR+jDZJA9DGYSxV8//RNT9rxKvfFTXbBR0CpGkGIrdQRnIrxcMkvEO2YjNPIm/8JVA peutxJkg2wzrr5pFzpPjSrGDRIFa X-Received: by 2002:a05:7022:ea2b:b0:12c:34b9:61bc with SMTP id a92af1059eb24-130b163f911mr795557c88.5.1777948260598; Mon, 04 May 2026 19:31:00 -0700 (PDT) Received: from ethan-latitude5420.. (host-127-24.cafrjco.fresno.ca.us.clients.pavlovmedia.net. [68.180.127.24]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-12df81ec067sm19357550c88.0.2026.05.04.19.30.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 May 2026 19:31:00 -0700 (PDT) From: Ethan Nelson-Moore To: linux-watchdog@vger.kernel.org Cc: Ethan Nelson-Moore , Wim Van Sebroeck , Guenter Roeck Subject: [PATCH] watchdog: remove drivers for discrete ISA WDT cards Date: Mon, 4 May 2026 19:30:00 -0700 Message-ID: <20260505023007.58175-1-enelsonmoore@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-watchdog@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 --- 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 . - 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. - # # 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 - * - * Copyright (c) 1999 ITConsult-Pro Co. - * - * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define VERSION "0.6" -#define WATCHDOG_NAME "mixcomwd" - -#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[] = { - /* 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 "); -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 . - * 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/ - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#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 struct miscdevice */ -#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 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 , " - "Wim Van Sebroeck "); -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 , - * 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 - * - * 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 -#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 < 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