^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * PC Watchdog Driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * by Ken Hollis (khollis@bitgate.com)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Permission granted from Simon Machell (smachell@berkprod.com)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Written for the Linux Kernel, and GPLed by Ken Hollis
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * 960107 Added request_region routines, modulized the whole thing.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * 960108 Fixed end-of-file pointer (Thanks to Dan Hollis), added
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * WD_TIMEOUT define.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * 960216 Added eof marker on the file, and changed verbose messages.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * 960716 Made functional and cosmetic changes to the source for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * inclusion in Linux 2.0.x kernels, thanks to Alan Cox.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * 960717 Removed read/seek routines, replaced with ioctl. Also, added
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * check_region command due to Alan's suggestion.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * 960821 Made changes to compile in newer 2.0.x kernels. Added
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * "cold reboot sense" entry.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * 960825 Made a few changes to code, deleted some defines and made
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * typedefs to replace them. Made heartbeat reset only available
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * via ioctl, and removed the write routine.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * 960828 Added new items for PC Watchdog Rev.C card.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * 960829 Changed around all of the IOCTLs, added new features,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * added watchdog disable/re-enable routines. Added firmware
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * version reporting. Added read routine for temperature.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * Removed some extra defines, added an autodetect Revision
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * routine.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * 961006 Revised some documentation, fixed some cosmetic bugs. Made
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * drivers to panic the system if it's overheating at bootup.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * 961118 Changed some verbiage on some of the output, tidied up
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * code bits, and added compatibility to 2.1.x.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * 970912 Enabled board on open and disable on close.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * 971107 Took account of recent VFS changes (broke read).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * 971210 Disable board on initialisation in case board already ticking.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * 971222 Changed open/close for temperature handling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * Michael Meskes <meskes@debian.org>.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * 980112 Used minor numbers from include/linux/miscdevice.h
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * 990403 Clear reset status after reading control status register in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * pcwd_showprevstate(). [Marc Boucher <marc@mbsi.ca>]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * 990605 Made changes to code to support Firmware 1.22a, added
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * fairly useless proc entry.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * 990610 removed said useless proc code for the merge <alan>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * 000403 Removed last traces of proc code. <davej>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) * 011214 Added nowayout module option to override
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * Added timeout module option to override default
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) * A bells and whistles driver is available from http://www.pcwd.de/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) * More info available at http://www.berkprod.com/ or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * http://www.pcwatchdog.com/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #include <linux/module.h> /* For module specific items */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #include <linux/moduleparam.h> /* For new moduleparam's */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #include <linux/types.h> /* For standard types (like size_t) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #include <linux/errno.h> /* For the -ENODEV/... values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) #include <linux/kernel.h> /* For printk/panic/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #include <linux/delay.h> /* For mdelay function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) #include <linux/timer.h> /* For timer related operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) #include <linux/jiffies.h> /* For jiffies stuff */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) #include <linux/miscdevice.h> /* For struct miscdevice */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) #include <linux/watchdog.h> /* For the watchdog specific items */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) #include <linux/reboot.h> /* For kernel_power_off() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) #include <linux/init.h> /* For __init/__exit/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) #include <linux/fs.h> /* For file operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) #include <linux/isa.h> /* For isa devices */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) #include <linux/ioport.h> /* For io-port access */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #include <linux/io.h> /* For inb/outb/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) /* Module and version information */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) #define WATCHDOG_VERSION "1.20"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) #define WATCHDOG_DATE "18 Feb 2007"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) #define WATCHDOG_NAME "pcwd"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) * It should be noted that PCWD_REVISION_B was removed because A and B
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) * are essentially the same types of card, with the exception that B
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * has temperature reporting. Since I didn't receive a Rev.B card,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) * the Rev.B card is not supported. (It's a good thing too, as they
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * are no longer in production.)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) #define PCWD_REVISION_A 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) #define PCWD_REVISION_C 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) * These are the auto-probe addresses available.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) * Revision A has an address range of 2 addresses, while Revision C has 4.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) #define PCWD_ISA_NR_CARDS 3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) * These are the defines that describe the control status bits for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) * PCI-PC Watchdog card.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) #define WD_WDRST 0x01 /* Previously reset state */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) #define WD_T110 0x02 /* Temperature overheat sense */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) #define WD_HRTBT 0x04 /* Heartbeat sense */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) #define WD_RLY2 0x08 /* External relay triggered */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) #define WD_SRLY2 0x80 /* Software external relay triggered */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) #define WD_REVC_WTRP 0x01 /* Watchdog Trip status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) #define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) #define WD_REVC_TTRP 0x04 /* Temperature Trip status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) #define WD_REVC_RL2A 0x08 /* Relay 2 activated by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) on-board processor */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) #define WD_REVC_RL1A 0x10 /* Relay 1 active */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) #define WD_REVC_R2DS 0x40 /* Relay 2 disable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) #define WD_REVC_RLY2 0x80 /* Relay 2 activated? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) /* Port 2 : Control Status #2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) #define WD_WDIS 0x10 /* Watchdog Disabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) #define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) #define WD_SSEL 0x40 /* Watchdog Switch Select
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) (1:SW1 <-> 0:SW2) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) #define WD_WCMD 0x80 /* Watchdog Command Mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) /* max. time we give an ISA watchdog card to process a command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) /* 500ms for each 4 bit response (according to spec.) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) #define ISA_COMMAND_TIMEOUT 1000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* Watchdog's internal commands */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) #define CMD_ISA_IDLE 0x00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) #define CMD_ISA_VERSION_INTEGER 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) #define CMD_ISA_VERSION_TENTH 0x02
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) #define CMD_ISA_VERSION_HUNDRETH 0x03
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) #define CMD_ISA_VERSION_MINOR 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) #define CMD_ISA_SWITCH_SETTINGS 0x05
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) #define CMD_ISA_RESET_PC 0x06
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) #define CMD_ISA_ARM_0 0x07
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) #define CMD_ISA_ARM_30 0x08
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) #define CMD_ISA_ARM_60 0x09
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) #define CMD_ISA_DELAY_TIME_2SECS 0x0A
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) #define CMD_ISA_DELAY_TIME_4SECS 0x0B
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) #define CMD_ISA_DELAY_TIME_8SECS 0x0C
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) #define CMD_ISA_RESET_RELAYS 0x0D
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) /* Watchdog's Dip Switch heartbeat values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) static const int heartbeat_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 20, /* OFF-OFF-OFF = 20 Sec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 40, /* OFF-OFF-ON = 40 Sec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 60, /* OFF-ON-OFF = 1 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 300, /* OFF-ON-ON = 5 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 600, /* ON-OFF-OFF = 10 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 1800, /* ON-OFF-ON = 30 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 3600, /* ON-ON-OFF = 1 Hour */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 7200, /* ON-ON-ON = 2 hour */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) * We are using an kernel timer to do the pinging of the watchdog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) * every ~500ms. We try to set the internal heartbeat of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) * watchdog to 2 ms.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) #define WDT_INTERVAL (HZ/2+1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) /* We can only use 1 card due to the /dev/watchdog restriction */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) static int cards_found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) /* internal variables */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) static unsigned long open_allowed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) static char expect_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) static int temp_panic;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) /* this is private data for each ISA-PC watchdog card */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) static struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) char fw_ver_str[6]; /* The cards firmware version */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) int revision; /* The card's revision */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) int supports_temp; /* Whether or not the card has
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) a temperature device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) int command_mode; /* Whether or not the card is in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) command mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) int boot_status; /* The card's boot status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) int io_addr; /* The cards I/O address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) spinlock_t io_lock; /* the lock for io operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) struct timer_list timer; /* The timer that pings the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) unsigned long next_heartbeat; /* the next_heartbeat for the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) } pcwd_private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) /* module parameters */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) #define QUIET 0 /* Default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) #define VERBOSE 1 /* Verbose */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) #define DEBUG 2 /* print fancy stuff too */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) static int debug = QUIET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) module_param(debug, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) MODULE_PARM_DESC(debug,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) /* default heartbeat = delay-time from dip-switches */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) #define WATCHDOG_HEARTBEAT 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) static int heartbeat = WATCHDOG_HEARTBEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) module_param(heartbeat, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) "(2 <= heartbeat <= 7200 or 0=delay-time from dip-switches, default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) MODULE_PARM_DESC(nowayout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) * Internal functions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) static int send_isa_command(int cmd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) int control_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) int port0, last_port0; /* Double read for stabilising */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) pr_debug("sending following data cmd=0x%02x\n", cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) /* The WCMD bit must be 1 and the command is only 4 bits in size */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) control_status = (cmd & 0x0F) | WD_WCMD;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) outb_p(control_status, pcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) udelay(ISA_COMMAND_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) port0 = inb_p(pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) for (i = 0; i < 25; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) last_port0 = port0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) port0 = inb_p(pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) if (port0 == last_port0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) break; /* Data is stable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) udelay(250);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) pr_debug("received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) cmd, port0, last_port0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) return port0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) static int set_command_mode(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) int i, found = 0, count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) /* Set the card into command mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) while ((!found) && (count < 3)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) i = send_isa_command(CMD_ISA_IDLE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (i == 0x00)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) found = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) else if (i == 0xF3) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) /* Card does not like what we've done to it */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) outb_p(0x00, pcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) udelay(1200); /* Spec says wait 1ms */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) outb_p(0x00, pcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) udelay(ISA_COMMAND_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) count++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) pcwd_private.command_mode = found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) pr_debug("command_mode=%d\n", pcwd_private.command_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) return found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) static void unset_command_mode(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) /* Set the card into normal mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) outb_p(0x00, pcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) udelay(ISA_COMMAND_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) pcwd_private.command_mode = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) pr_debug("command_mode=%d\n", pcwd_private.command_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) static inline void pcwd_check_temperature_support(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) if (inb(pcwd_private.io_addr) != 0xF0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) pcwd_private.supports_temp = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) static inline void pcwd_get_firmware(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) int one, ten, hund, minor;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) strcpy(pcwd_private.fw_ver_str, "ERROR");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) if (set_command_mode()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) one = send_isa_command(CMD_ISA_VERSION_INTEGER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) ten = send_isa_command(CMD_ISA_VERSION_TENTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) minor = send_isa_command(CMD_ISA_VERSION_MINOR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) one, ten, hund, minor);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) unset_command_mode();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) static inline int pcwd_get_option_switches(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) int option_switches = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) if (set_command_mode()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) /* Get switch settings */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) unset_command_mode();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) return option_switches;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) static void pcwd_show_card_info(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) int option_switches;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) /* Get some extra info from the hardware (in command/debug/diag mode) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) if (pcwd_private.revision == PCWD_REVISION_A)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) pr_info("ISA-PC Watchdog (REV.A) detected at port 0x%04x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) else if (pcwd_private.revision == PCWD_REVISION_C) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) pcwd_get_firmware();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) pr_info("ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) pcwd_private.io_addr, pcwd_private.fw_ver_str);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) option_switches = pcwd_get_option_switches();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) option_switches,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) ((option_switches & 0x10) ? "ON" : "OFF"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) ((option_switches & 0x08) ? "ON" : "OFF"));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) /* Reprogram internal heartbeat to 2 seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) if (set_command_mode()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) send_isa_command(CMD_ISA_DELAY_TIME_2SECS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) unset_command_mode();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) if (pcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) pr_info("Temperature Option Detected\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) if (pcwd_private.boot_status & WDIOF_CARDRESET)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) pr_info("Previous reboot was caused by the card\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) pr_emerg("Card senses a CPU Overheat. Panicking!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) pr_emerg("CPU Overheat\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) if (pcwd_private.boot_status == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) pr_info("No previous trip detected - Cold boot or reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) static void pcwd_timer_ping(struct timer_list *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) int wdrst_stat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) /* If we got a heartbeat pulse within the WDT_INTERVAL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) * we agree to ping the WDT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) if (time_before(jiffies, pcwd_private.next_heartbeat)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) /* Ping the watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) if (pcwd_private.revision == PCWD_REVISION_A) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) /* Rev A cards are reset by setting the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) WD_WDRST bit in register 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) wdrst_stat = inb_p(pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) wdrst_stat &= 0x0F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) wdrst_stat |= WD_WDRST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) outb_p(wdrst_stat, pcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) /* Re-trigger watchdog by writing to port 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) outb_p(0x00, pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) /* Re-set the timer interval */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) pr_warn("Heartbeat lost! Will not ping the watchdog\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) static int pcwd_start(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) int stat_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) /* Start the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) /* Enable the port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) if (pcwd_private.revision == PCWD_REVISION_C) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) outb_p(0x00, pcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) udelay(ISA_COMMAND_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) stat_reg = inb_p(pcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) if (stat_reg & WD_WDIS) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) pr_info("Could not start watchdog\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) pr_debug("Watchdog started\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) static int pcwd_stop(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) int stat_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) /* Stop the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) del_timer(&pcwd_private.timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) /* Disable the board */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) if (pcwd_private.revision == PCWD_REVISION_C) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) outb_p(0xA5, pcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) udelay(ISA_COMMAND_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) outb_p(0xA5, pcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) udelay(ISA_COMMAND_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) stat_reg = inb_p(pcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) if ((stat_reg & WD_WDIS) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) pr_info("Could not stop watchdog\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) pr_debug("Watchdog stopped\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) static int pcwd_keepalive(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) /* user land ping */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) pr_debug("Watchdog keepalive signal send\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) static int pcwd_set_heartbeat(int t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) if (t < 2 || t > 7200) /* arbitrary upper limit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) heartbeat = t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) pr_debug("New heartbeat: %d\n", heartbeat);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) static int pcwd_get_status(int *status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) int control_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) *status = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) if (pcwd_private.revision == PCWD_REVISION_A)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) /* Rev A cards return status information from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) * the base register, which is used for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) * temperature in other cards. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) control_status = inb(pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) /* Rev C cards return card status in the base
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) * address + 1 register. And use different bits
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) * to indicate a card initiated reset, and an
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) * over-temperature condition. And the reboot
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) * status can be reset. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) control_status = inb(pcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) if (pcwd_private.revision == PCWD_REVISION_A) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) if (control_status & WD_WDRST)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) *status |= WDIOF_CARDRESET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) if (control_status & WD_T110) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) *status |= WDIOF_OVERHEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) if (temp_panic) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) pr_info("Temperature overheat trip!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) kernel_power_off();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) if (control_status & WD_REVC_WTRP)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) *status |= WDIOF_CARDRESET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) if (control_status & WD_REVC_TTRP) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) *status |= WDIOF_OVERHEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) if (temp_panic) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) pr_info("Temperature overheat trip!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) kernel_power_off();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) static int pcwd_clear_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) int control_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) if (pcwd_private.revision == PCWD_REVISION_C) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) pr_info("clearing watchdog trip status\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) control_status = inb_p(pcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) if (debug >= DEBUG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) pr_debug("status was: 0x%02x\n", control_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) pr_debug("sending: 0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) (control_status & WD_REVC_R2DS));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) /* clear reset status & Keep Relay 2 disable state as it is */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) outb_p((control_status & WD_REVC_R2DS),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) pcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) static int pcwd_get_temperature(int *temperature)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) /* check that port 0 gives temperature info and no command results */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) if (pcwd_private.command_mode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) *temperature = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) if (!pcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) * Convert celsius to fahrenheit, since this was
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) * the decided 'standard' for this return value.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) if (debug >= DEBUG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) pr_debug("temperature is: %d F\n", *temperature);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) * /dev/watchdog handling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) int rv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) int temperature;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) int new_heartbeat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) int __user *argp = (int __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) static const struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) .options = WDIOF_OVERHEAT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) WDIOF_CARDRESET |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) .firmware_version = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) .identity = "PCWD",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) if (copy_to_user(argp, &ident, sizeof(ident)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) pcwd_get_status(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) return put_user(status, argp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) return put_user(pcwd_private.boot_status, argp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) case WDIOC_GETTEMP:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) if (pcwd_get_temperature(&temperature))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) return put_user(temperature, argp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) case WDIOC_SETOPTIONS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) if (pcwd_private.revision == PCWD_REVISION_C) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) if (get_user(rv, argp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) if (rv & WDIOS_DISABLECARD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) status = pcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) if (status < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) if (rv & WDIOS_ENABLECARD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) status = pcwd_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) if (status < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) if (rv & WDIOS_TEMPPANIC)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) temp_panic = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) pcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) if (get_user(new_heartbeat, argp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) if (pcwd_set_heartbeat(new_heartbeat))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) pcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) return put_user(heartbeat, argp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) if (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) /* In case it was set long ago */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) for (i = 0; i != len; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) if (get_user(c, buf + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) expect_close = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) pcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) static int pcwd_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) if (test_and_set_bit(0, &open_allowed))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) if (nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) __module_get(THIS_MODULE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) /* Activate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) pcwd_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) pcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) static int pcwd_close(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) if (expect_close == 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) pcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) pr_crit("Unexpected close, not stopping watchdog!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) pcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) clear_bit(0, &open_allowed);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) * /dev/temperature handling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) int temperature;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) if (pcwd_get_temperature(&temperature))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) if (copy_to_user(buf, &temperature, 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) static int pcwd_temp_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) if (!pcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) static int pcwd_temp_close(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) * Kernel Interfaces
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) static const struct file_operations pcwd_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) .write = pcwd_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) .unlocked_ioctl = pcwd_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) .compat_ioctl = compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) .open = pcwd_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) .release = pcwd_close,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) static struct miscdevice pcwd_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) .minor = WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) .name = "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) .fops = &pcwd_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) static const struct file_operations pcwd_temp_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) .read = pcwd_temp_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) .open = pcwd_temp_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) .release = pcwd_temp_close,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) static struct miscdevice temp_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) .minor = TEMP_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) .name = "temperature",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) .fops = &pcwd_temp_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) * Init & exit routines
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) static inline int get_revision(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) int r = PCWD_REVISION_C;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) spin_lock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) /* REV A cards use only 2 io ports; test
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) * presumes a floating bus reads as 0xff. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) if ((inb(pcwd_private.io_addr + 2) == 0xFF) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) (inb(pcwd_private.io_addr + 3) == 0xFF))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) r = PCWD_REVISION_A;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) spin_unlock(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) return r;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) * The ISA cards have a heartbeat bit in one of the registers, which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) * register is card dependent. The heartbeat bit is monitored, and if
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) * found, is considered proof that a Berkshire card has been found.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) * The initial rate is once per second at board start up, then twice
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) * per second for normal operation.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) static int pcwd_isa_match(struct device *dev, unsigned int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) int base_addr = pcwd_ioports[id];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) int port0, last_port0; /* Reg 0, in case it's REV A */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) int port1, last_port1; /* Register 1 for REV C cards */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) pr_debug("pcwd_isa_match id=%d\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) if (!request_region(base_addr, 4, "PCWD")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) pr_info("Port 0x%04x unavailable\n", base_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822) retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) port0 = inb_p(base_addr); /* For REV A boards */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825) port1 = inb_p(base_addr + 1); /* For REV C boards */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826) if (port0 != 0xff || port1 != 0xff) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) /* Not an 'ff' from a floating bus, so must be a card! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) for (i = 0; i < 4; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) msleep(500);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832) last_port0 = port0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) last_port1 = port1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) port0 = inb_p(base_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836) port1 = inb_p(base_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) /* Has either hearbeat bit changed? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) if ((port0 ^ last_port0) & WD_HRTBT ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840) (port1 ^ last_port1) & WD_REVC_HRBT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) retval = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) release_region(base_addr, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) static int pcwd_isa_probe(struct device *dev, unsigned int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856) pr_debug("pcwd_isa_probe id=%d\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858) cards_found++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) if (cards_found == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) pr_info("v%s Ken Hollis (kenji@bitgate.com)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861) WATCHDOG_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) if (cards_found > 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) pr_err("This driver only supports 1 device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 865) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 866) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 867)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 868) if (pcwd_ioports[id] == 0x0000) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 869) pr_err("No I/O-Address for card detected\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 870) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 871) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 872) pcwd_private.io_addr = pcwd_ioports[id];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 873)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 874) spin_lock_init(&pcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 875)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 876) /* Check card's revision */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 877) pcwd_private.revision = get_revision();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 878)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 879) if (!request_region(pcwd_private.io_addr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 880) (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 881) pr_err("I/O address 0x%04x already in use\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 882) pcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 883) ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 884) goto error_request_region;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 885) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 886)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 887) /* Initial variables */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 888) pcwd_private.supports_temp = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 889) temp_panic = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 890) pcwd_private.boot_status = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 891)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 892) /* get the boot_status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 893) pcwd_get_status(&pcwd_private.boot_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 894)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 895) /* clear the "card caused reboot" flag */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 896) pcwd_clear_status();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 897)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 898) timer_setup(&pcwd_private.timer, pcwd_timer_ping, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 899)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 900) /* Disable the board */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 901) pcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 902)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 903) /* Check whether or not the card supports the temperature device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 904) pcwd_check_temperature_support();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 905)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 906) /* Show info about the card itself */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 907) pcwd_show_card_info();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 908)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 909) /* If heartbeat = 0 then we use the heartbeat from the dip-switches */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 910) if (heartbeat == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 911) heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 912)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 913) /* Check that the heartbeat value is within it's range;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 914) if not reset to the default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 915) if (pcwd_set_heartbeat(heartbeat)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 916) pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 917) pr_info("heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 918) WATCHDOG_HEARTBEAT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 919) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 920)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 921) if (pcwd_private.supports_temp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 922) ret = misc_register(&temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 923) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 924) pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 925) TEMP_MINOR, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 926) goto error_misc_register_temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 927) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 928) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 929)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 930) ret = misc_register(&pcwd_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 931) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 932) pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 933) WATCHDOG_MINOR, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 934) goto error_misc_register_watchdog;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 935) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 936)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 937) pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 938) heartbeat, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 939)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 940) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 941)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 942) error_misc_register_watchdog:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 943) if (pcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 944) misc_deregister(&temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 945) error_misc_register_temp:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 946) release_region(pcwd_private.io_addr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 947) (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 948) error_request_region:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 949) pcwd_private.io_addr = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 950) cards_found--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 951) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 952) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 953)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 954) static int pcwd_isa_remove(struct device *dev, unsigned int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 955) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 956) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 957) pr_debug("pcwd_isa_remove id=%d\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 958)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 959) if (!pcwd_private.io_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 960) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 961)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 962) /* Disable the board */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 963) if (!nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 964) pcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 965)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 966) /* Deregister */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 967) misc_deregister(&pcwd_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 968) if (pcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 969) misc_deregister(&temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 970) release_region(pcwd_private.io_addr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 971) (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 972) pcwd_private.io_addr = 0x0000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 973) cards_found--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 974)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 975) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 976) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 977)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 978) static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 979) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 980) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 981) pr_debug("pcwd_isa_shutdown id=%d\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 982)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 983) pcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 984) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 985)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 986) static struct isa_driver pcwd_isa_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 987) .match = pcwd_isa_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 988) .probe = pcwd_isa_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 989) .remove = pcwd_isa_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 990) .shutdown = pcwd_isa_shutdown,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 991) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 992) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 993) .name = WATCHDOG_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 994) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 995) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 996)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 997) module_isa_driver(pcwd_isa_driver, PCWD_ISA_NR_CARDS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 998)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 999) MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1000) "Wim Van Sebroeck <wim@iguana.be>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1001) MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1002) MODULE_VERSION(WATCHDOG_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1003) MODULE_LICENSE("GPL");