^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Berkshire PCI-PC Watchdog Card Driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * (c) Copyright 2003-2007 Wim Van Sebroeck <wim@iguana.be>.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Based on source code of the following authors:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Ken Hollis <kenji@bitgate.com>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Lindsay Harris <lindsay@bluegum.com>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Alan Cox <alan@lxorguk.ukuu.org.uk>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * Matt Domsch <Matt_Domsch@dell.com>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Rob Radez <rob@osinvestor.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * provide warranty for any of this software. This material is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * provided "AS-IS" and at no charge.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * A bells and whistles driver is available from:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * More info available at
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * http://www.berkprod.com/ or http://www.pcwatchdog.com/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * Includes, defines, variables, module parameters, ...
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include <linux/module.h> /* For module specific items */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #include <linux/moduleparam.h> /* For new moduleparam's */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #include <linux/types.h> /* For standard types (like size_t) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #include <linux/errno.h> /* For the -ENODEV/... values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #include <linux/kernel.h> /* For printk/panic/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #include <linux/delay.h> /* For mdelay function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #include <linux/miscdevice.h> /* For struct miscdevice */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #include <linux/watchdog.h> /* For the watchdog specific items */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #include <linux/notifier.h> /* For notifier support */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #include <linux/reboot.h> /* For reboot_notifier stuff */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #include <linux/init.h> /* For __init/__exit/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #include <linux/fs.h> /* For file operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #include <linux/pci.h> /* For pci functions */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #include <linux/ioport.h> /* For io-port access */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #include <linux/io.h> /* For inb/outb/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) /* Module and version information */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #define WATCHDOG_VERSION "1.03"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #define WATCHDOG_NAME "pcwd_pci"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* Stuff for the PCI ID's */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #ifndef PCI_VENDOR_ID_QUICKLOGIC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #define PCI_VENDOR_ID_QUICKLOGIC 0x11e3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #ifndef PCI_DEVICE_ID_WATCHDOG_PCIPCWD
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) #define PCI_DEVICE_ID_WATCHDOG_PCIPCWD 0x5030
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * These are the defines that describe the control status bits for the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * PCI-PC Watchdog card.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /* Port 1 : Control Status #1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) #define WD_PCI_WTRP 0x01 /* Watchdog Trip status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) #define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) #define WD_PCI_TTRP 0x04 /* Temperature Trip status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #define WD_PCI_RL2A 0x08 /* Relay 2 Active */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) #define WD_PCI_RL1A 0x10 /* Relay 1 Active */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) #define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) #define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) /* Port 2 : Control Status #2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) #define WD_PCI_WDIS 0x10 /* Watchdog Disable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) #define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) #define WD_PCI_WRSP 0x40 /* Watchdog wrote response */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) #define WD_PCI_PCMD 0x80 /* PC has sent command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) /* according to documentation max. time to process a command for the pci
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * watchdog card is 100 ms, so we give it 150 ms to do it's job */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) #define PCI_COMMAND_TIMEOUT 150
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) /* Watchdog's internal commands */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) #define CMD_GET_STATUS 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) #define CMD_GET_FIRMWARE_VERSION 0x08
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) #define CMD_READ_WATCHDOG_TIMEOUT 0x18
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) #define CMD_WRITE_WATCHDOG_TIMEOUT 0x19
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) #define CMD_GET_CLEAR_RESET_COUNT 0x84
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) /* Watchdog's Dip Switch heartbeat values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) static const int heartbeat_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) 5, /* OFF-OFF-OFF = 5 Sec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) 10, /* OFF-OFF-ON = 10 Sec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 30, /* OFF-ON-OFF = 30 Sec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 60, /* OFF-ON-ON = 1 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 300, /* ON-OFF-OFF = 5 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 600, /* ON-OFF-ON = 10 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 1800, /* ON-ON-OFF = 30 Min */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 3600, /* ON-ON-ON = 1 hour */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) /* We can only use 1 card due to the /dev/watchdog restriction */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) static int cards_found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /* internal variables */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static int temp_panic;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) static unsigned long is_active;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) static char expect_release;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) /* this is private data for each PCI-PC watchdog card */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) /* Wether or not the card has a temperature device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) int supports_temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /* The card's boot status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) int boot_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) /* The cards I/O address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) unsigned long io_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) /* the lock for io operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) spinlock_t io_lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) /* the PCI-device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) struct pci_dev *pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) } pcipcwd_private;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) /* module parameters */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) #define QUIET 0 /* Default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) #define VERBOSE 1 /* Verbose */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) #define DEBUG 2 /* print fancy stuff too */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static int debug = QUIET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) module_param(debug, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) #define WATCHDOG_HEARTBEAT 0 /* default heartbeat =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) delay-time from dip-switches */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static int heartbeat = WATCHDOG_HEARTBEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) module_param(heartbeat, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) "(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) * Internal functions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) static int send_command(int cmd, int *msb, int *lsb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) int got_response, count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) pr_debug("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) cmd, *msb, *lsb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) spin_lock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) /* If a command requires data it should be written first.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) * Data for commands with 8 bits of data should be written to port 4.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) * Commands with 16 bits of data, should be written as LSB to port 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) * and MSB to port 5.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) * After the required data has been written then write the command to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) * port 6. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) outb_p(*lsb, pcipcwd_private.io_addr + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) outb_p(*msb, pcipcwd_private.io_addr + 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) outb_p(cmd, pcipcwd_private.io_addr + 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) /* wait till the pci card processed the command, signaled by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) * the WRSP bit in port 2 and give it a max. timeout of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) * PCI_COMMAND_TIMEOUT to process */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) count++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) mdelay(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) if (debug >= DEBUG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (got_response) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) pr_debug("time to process command was: %d ms\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) pr_debug("card did not respond on command!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) if (got_response) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) /* read back response */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) *lsb = inb_p(pcipcwd_private.io_addr + 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) *msb = inb_p(pcipcwd_private.io_addr + 5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) /* clear WRSP bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) inb_p(pcipcwd_private.io_addr + 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) pr_debug("received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) cmd, *msb, *lsb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) spin_unlock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) return got_response;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) static inline void pcipcwd_check_temperature_support(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (inb_p(pcipcwd_private.io_addr) != 0xF0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) pcipcwd_private.supports_temp = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) static int pcipcwd_get_option_switches(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) int option_switches;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) option_switches = inb_p(pcipcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) return option_switches;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) static void pcipcwd_show_card_info(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) int got_fw_rev, fw_rev_major, fw_rev_minor;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) char fw_ver_str[20]; /* The cards firmware version */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) int option_switches;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) &fw_rev_minor);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) if (got_fw_rev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) sprintf(fw_ver_str, "<card no answer>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) /* Get switch settings */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) option_switches = pcipcwd_get_option_switches();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) pr_info("Found card at port 0x%04x (Firmware: %s) %s temp option\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) (int) pcipcwd_private.io_addr, fw_ver_str,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) (pcipcwd_private.supports_temp ? "with" : "without"));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) option_switches,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) ((option_switches & 0x10) ? "ON" : "OFF"),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) ((option_switches & 0x08) ? "ON" : "OFF"));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) if (pcipcwd_private.boot_status & WDIOF_CARDRESET)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) pr_info("Previous reset was caused by the Watchdog card\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) if (pcipcwd_private.boot_status & WDIOF_OVERHEAT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) pr_info("Card sensed a CPU Overheat\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) if (pcipcwd_private.boot_status == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) pr_info("No previous trip detected - Cold boot or reset\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) static int pcipcwd_start(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) int stat_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) spin_lock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) outb_p(0x00, pcipcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) udelay(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) stat_reg = inb_p(pcipcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) spin_unlock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) if (stat_reg & WD_PCI_WDIS) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) pr_err("Card timer not enabled\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) pr_debug("Watchdog started\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) static int pcipcwd_stop(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) int stat_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) spin_lock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) outb_p(0xA5, pcipcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) udelay(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) outb_p(0xA5, pcipcwd_private.io_addr + 3);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) udelay(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) stat_reg = inb_p(pcipcwd_private.io_addr + 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) spin_unlock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) if (!(stat_reg & WD_PCI_WDIS)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) pr_err("Card did not acknowledge disable attempt\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) pr_debug("Watchdog stopped\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) static int pcipcwd_keepalive(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) /* Re-trigger watchdog by writing to port 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) spin_lock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) spin_unlock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) pr_debug("Watchdog keepalive signal send\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) static int pcipcwd_set_heartbeat(int t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) int t_msb = t / 256;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) int t_lsb = t % 256;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) if ((t < 0x0001) || (t > 0xFFFF))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) /* Write new heartbeat to watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) heartbeat = t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) pr_debug("New heartbeat: %d\n", heartbeat);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) static int pcipcwd_get_status(int *status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) int control_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) *status = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) control_status = inb_p(pcipcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) if (control_status & WD_PCI_WTRP)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) *status |= WDIOF_CARDRESET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) if (control_status & WD_PCI_TTRP) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) *status |= WDIOF_OVERHEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) if (temp_panic)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) panic(KBUILD_MODNAME ": Temperature overheat trip!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) if (debug >= DEBUG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) pr_debug("Control Status #1: 0x%02x\n", control_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) static int pcipcwd_clear_status(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) int control_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) int msb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) int reset_counter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) pr_info("clearing watchdog trip status & LED\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) control_status = inb_p(pcipcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) if (debug >= DEBUG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) pr_debug("status was: 0x%02x\n", control_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) pr_debug("sending: 0x%02x\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) (control_status & WD_PCI_R2DS) | WD_PCI_WTRP);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) /* clear trip status & LED and keep mode of relay 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) pcipcwd_private.io_addr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) /* clear reset counter */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) msb = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) reset_counter = 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) if (debug >= DEBUG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) pr_debug("reset count was: 0x%02x\n", reset_counter);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) static int pcipcwd_get_temperature(int *temperature)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) *temperature = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) if (!pcipcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) spin_lock(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) *temperature = inb_p(pcipcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) spin_unlock(&pcipcwd_private.io_lock);
^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) * Convert celsius to fahrenheit, since this was
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) * the decided 'standard' for this return value.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) *temperature = (*temperature * 9 / 5) + 32;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) if (debug >= DEBUG) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) pr_debug("temperature is: %d F\n", *temperature);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) static int pcipcwd_get_timeleft(int *time_left)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) int msb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) int lsb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) /* Read the time that's left before rebooting */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) /* Note: if the board is not yet armed then we will read 0xFFFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) *time_left = (msb << 8) + lsb;
^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("Time left before next reboot: %d\n", *time_left);
^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) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) * /dev/watchdog handling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) static ssize_t pcipcwd_write(struct file *file, const char __user *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) /* See if we got the magic character 'V' and reload the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) if (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) /* note: just in case someone wrote the magic character
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) * five months ago... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) expect_release = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) /* scan to see whether or not we got the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) * magic character */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) for (i = 0; i != len; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) if (get_user(c, data + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) expect_release = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) /* someone wrote to us, we should reload the timer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) pcipcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) int __user *p = argp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) static const struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) .options = WDIOF_OVERHEAT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) WDIOF_CARDRESET |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) .firmware_version = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) .identity = WATCHDOG_DRIVER_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) pcipcwd_get_status(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) return put_user(status, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) return put_user(pcipcwd_private.boot_status, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) case WDIOC_GETTEMP:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) int temperature;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) if (pcipcwd_get_temperature(&temperature))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) return put_user(temperature, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) case WDIOC_SETOPTIONS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) int new_options, retval = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) if (get_user(new_options, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) if (new_options & WDIOS_DISABLECARD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) if (pcipcwd_stop())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) if (new_options & WDIOS_ENABLECARD) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) if (pcipcwd_start())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) retval = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) if (new_options & WDIOS_TEMPPANIC) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) temp_panic = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) retval = 0;
^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) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) pcipcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) int new_heartbeat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) if (get_user(new_heartbeat, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) if (pcipcwd_set_heartbeat(new_heartbeat))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) pcipcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) return put_user(heartbeat, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) case WDIOC_GETTIMELEFT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) int time_left;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) if (pcipcwd_get_timeleft(&time_left))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) return put_user(time_left, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) static int pcipcwd_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) /* /dev/watchdog can only be opened once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) if (test_and_set_bit(0, &is_active)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) if (debug >= VERBOSE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) pr_err("Attempt to open already opened device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) /* Activate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) pcipcwd_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) pcipcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) static int pcipcwd_release(struct inode *inode, struct file *file)
^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) * Shut off the timer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) if (expect_release == 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) pcipcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) pr_crit("Unexpected close, not stopping watchdog!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) pcipcwd_keepalive();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) expect_release = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) clear_bit(0, &is_active);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) * /dev/temperature handling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) static ssize_t pcipcwd_temp_read(struct file *file, char __user *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) int temperature;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) if (pcipcwd_get_temperature(&temperature))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) if (copy_to_user(data, &temperature, 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) static int pcipcwd_temp_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) if (!pcipcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) static int pcipcwd_temp_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) * Notify system
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) void *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) if (code == SYS_DOWN || code == SYS_HALT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) pcipcwd_stop(); /* Turn the WDT off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) * Kernel Interfaces
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) static const struct file_operations pcipcwd_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) .write = pcipcwd_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) .unlocked_ioctl = pcipcwd_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) .compat_ioctl = compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) .open = pcipcwd_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) .release = pcipcwd_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) static struct miscdevice pcipcwd_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) .minor = WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) .name = "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) .fops = &pcipcwd_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) static const struct file_operations pcipcwd_temp_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) .read = pcipcwd_temp_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) .open = pcipcwd_temp_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) .release = pcipcwd_temp_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) static struct miscdevice pcipcwd_temp_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) .minor = TEMP_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) .name = "temperature",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) .fops = &pcipcwd_temp_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) static struct notifier_block pcipcwd_notifier = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) .notifier_call = pcipcwd_notify_sys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) * Init & exit routines
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) static int pcipcwd_card_init(struct pci_dev *pdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) const struct pci_device_id *ent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) int ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) cards_found++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) if (cards_found == 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) pr_info("%s\n", DRIVER_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) if (cards_found > 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) pr_err("This driver only supports 1 device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) if (pci_enable_device(pdev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) pr_err("Not possible to enable PCI Device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) if (pci_resource_start(pdev, 0) == 0x0000) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) pr_err("No I/O-Address for card detected\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) goto err_out_disable_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) spin_lock_init(&pcipcwd_private.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) pcipcwd_private.pdev = pdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) pcipcwd_private.io_addr = pci_resource_start(pdev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) if (pci_request_regions(pdev, WATCHDOG_NAME)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) pr_err("I/O address 0x%04x already in use\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) (int) pcipcwd_private.io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) goto err_out_disable_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) /* get the boot_status */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) pcipcwd_get_status(&pcipcwd_private.boot_status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) /* clear the "card caused reboot" flag */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) pcipcwd_clear_status();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) /* disable card */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) pcipcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) /* Check whether or not the card supports the temperature device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) pcipcwd_check_temperature_support();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) /* Show info about the card itself */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) pcipcwd_show_card_info();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) /* If heartbeat = 0 then we use the heartbeat from the dip-switches */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) if (heartbeat == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) heartbeat =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) heartbeat_tbl[(pcipcwd_get_option_switches() & 0x07)];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) /* Check that the heartbeat value is within it's range ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) * if not reset to the default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) if (pcipcwd_set_heartbeat(heartbeat)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) pr_info("heartbeat value must be 0<heartbeat<65536, using %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) WATCHDOG_HEARTBEAT);
^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) ret = register_reboot_notifier(&pcipcwd_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) if (ret != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) pr_err("cannot register reboot notifier (err=%d)\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) goto err_out_release_region;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) if (pcipcwd_private.supports_temp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) ret = misc_register(&pcipcwd_temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) if (ret != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) TEMP_MINOR, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) goto err_out_unregister_reboot;
^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)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) ret = misc_register(&pcipcwd_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) if (ret != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) WATCHDOG_MINOR, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) goto err_out_misc_deregister;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) heartbeat, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) err_out_misc_deregister:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) if (pcipcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) misc_deregister(&pcipcwd_temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) err_out_unregister_reboot:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) unregister_reboot_notifier(&pcipcwd_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) err_out_release_region:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) pci_release_regions(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) err_out_disable_device:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) pci_disable_device(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) static void pcipcwd_card_exit(struct pci_dev *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) /* Stop the timer before we leave */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) if (!nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) pcipcwd_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) /* Deregister */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) misc_deregister(&pcipcwd_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) if (pcipcwd_private.supports_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) misc_deregister(&pcipcwd_temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) unregister_reboot_notifier(&pcipcwd_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) pci_release_regions(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) pci_disable_device(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) cards_found--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) static const struct pci_device_id pcipcwd_pci_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) { PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) PCI_ANY_ID, PCI_ANY_ID, },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) { 0 }, /* End of list */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) MODULE_DEVICE_TABLE(pci, pcipcwd_pci_tbl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) static struct pci_driver pcipcwd_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) .name = WATCHDOG_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) .id_table = pcipcwd_pci_tbl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) .probe = pcipcwd_card_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) .remove = pcipcwd_card_exit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) module_pci_driver(pcipcwd_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) MODULE_LICENSE("GPL");