^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) * Industrial Computer Source PCI-WDT500/501 driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * warranty for any of this software. This material is provided
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * "AS-IS" and at no charge.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * Release 0.10.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * Fixes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * Dave Gregorich : Modularisation and minor bugs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * Alan Cox : Added the watchdog ioctl() stuff
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * Alan Cox : Fixed the reboot problem (as noted by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * Matt Crocker).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * Alan Cox : Added wdt= boot option
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * Alan Cox : Cleaned up copy/user stuff
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * Tim Hockin : Added insmod parameters, comment cleanup
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * Parameterized timeout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * JP Nollmann : Added support for PCI wdt501p
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * Alan Cox : Split ISA and PCI cards into two drivers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * Jeff Garzik : PCI cleanups
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * Tigran Aivazian : Restructured wdtpci_init_one() to handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * failures
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * Joel Becker : Added WDIOC_GET/SETTIMEOUT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * Zwane Mwaikambo : Magic char closing, locking changes,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * cleanups
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * Matt Domsch : nowayout module option
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #include <linux/notifier.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #include <linux/reboot.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #define WDT_IS_PCI
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #include "wd501p.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* We can only use 1 card due to the /dev/watchdog restriction */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) static int dev_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) static unsigned long open_lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static DEFINE_SPINLOCK(wdtpci_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static char expect_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static resource_size_t io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static int irq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* Default timeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) #define WD_TIMO 60 /* Default heartbeat = 60 seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) static int heartbeat = WD_TIMO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static int wd_heartbeat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) module_param(heartbeat, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) MODULE_PARM_DESC(heartbeat,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) "Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) __MODULE_STRING(WD_TIMO) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) MODULE_PARM_DESC(nowayout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) /* Support for the Fan Tachometer on the PCI-WDT501 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) static int tachometer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) module_param(tachometer, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) MODULE_PARM_DESC(tachometer,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) "PCI-WDT501 Fan Tachometer support (0=disable, default=0)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static int type = 500;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) module_param(type, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) MODULE_PARM_DESC(type,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) "PCI-WDT501 Card type (500 or 501 , default=500)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) * Programming support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static void wdtpci_ctr_mode(int ctr, int mode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) ctr <<= 6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) ctr |= 0x30;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) ctr |= (mode << 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) outb(ctr, WDT_CR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static void wdtpci_ctr_load(int ctr, int val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) outb(val & 0xFF, WDT_COUNT0 + ctr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) outb(val >> 8, WDT_COUNT0 + ctr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) * wdtpci_start:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) * Start the watchdog driver.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static int wdtpci_start(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) spin_lock_irqsave(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) * "pet" the watchdog, as Access says.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) * This resets the clock outputs.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) inb(WDT_DC); /* Disable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) wdtpci_ctr_mode(2, 0); /* Program CTR2 for Mode 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) Pulse on Terminal Count */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) outb(0, WDT_DC); /* Enable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) inb(WDT_DC); /* Disable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) outb(0, WDT_CLOCK); /* 2.0833MHz clock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) inb(WDT_BUZZER); /* disable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) inb(WDT_OPTONOTRST); /* disable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) inb(WDT_OPTORST); /* disable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) inb(WDT_PROGOUT); /* disable */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) wdtpci_ctr_mode(0, 3); /* Program CTR0 for Mode 3:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) Square Wave Generator */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) wdtpci_ctr_mode(1, 2); /* Program CTR1 for Mode 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) Rate Generator */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) wdtpci_ctr_mode(2, 1); /* Program CTR2 for Mode 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) Retriggerable One-Shot */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) wdtpci_ctr_load(0, 20833); /* count at 100Hz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) /* DO NOT LOAD CTR2 on PCI card! -- JPN */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) outb(0, WDT_DC); /* Enable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) spin_unlock_irqrestore(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) }
^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) * wdtpci_stop:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) * Stop the watchdog driver.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) static int wdtpci_stop(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) /* Turn the card off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) spin_lock_irqsave(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) inb(WDT_DC); /* Disable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) wdtpci_ctr_load(2, 0); /* 0 length reset pulses now */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) spin_unlock_irqrestore(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) * wdtpci_ping:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) * Reload counter one with the watchdog heartbeat. We don't bother
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) * reloading the cascade counter.
^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) static int wdtpci_ping(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) spin_lock_irqsave(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) /* Write a watchdog value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) inb(WDT_DC); /* Disable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) wdtpci_ctr_mode(1, 2); /* Re-Program CTR1 for Mode 2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) Rate Generator */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) outb(0, WDT_DC); /* Enable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) spin_unlock_irqrestore(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) * wdtpci_set_heartbeat:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) * @t: the new heartbeat value that needs to be set.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) * Set a new heartbeat value for the watchdog device. If the heartbeat
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) * value is incorrect we keep the old value and return -EINVAL.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) * If successful we return 0.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) static int wdtpci_set_heartbeat(int t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) /* Arbitrary, can't find the card's limits */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) if (t < 1 || t > 65535)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) heartbeat = t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) wd_heartbeat = t * 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) * wdtpci_get_status:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) * @status: the new status.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) * Extract the status information from a WDT watchdog device. There are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) * several board variants so we have to know which bits are valid. Some
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) * bits default to one and some to zero in order to be maximally painful.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) * we then map the bits onto the status ioctl flags.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) static int wdtpci_get_status(int *status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) unsigned char new_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) spin_lock_irqsave(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) new_status = inb(WDT_SR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) spin_unlock_irqrestore(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) *status = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) if (new_status & WDC_SR_ISOI0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) *status |= WDIOF_EXTERN1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) if (new_status & WDC_SR_ISII1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) *status |= WDIOF_EXTERN2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) if (type == 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (!(new_status & WDC_SR_TGOOD))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) *status |= WDIOF_OVERHEAT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) if (!(new_status & WDC_SR_PSUOVER))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) *status |= WDIOF_POWEROVER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) if (!(new_status & WDC_SR_PSUUNDR))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) *status |= WDIOF_POWERUNDER;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (tachometer) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) if (!(new_status & WDC_SR_FANGOOD))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) *status |= WDIOF_FANFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) * wdtpci_get_temperature:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) * Reports the temperature in degrees Fahrenheit. The API is in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) * farenheit. It was designed by an imperial measurement luddite.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) static int wdtpci_get_temperature(int *temperature)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) unsigned short c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) spin_lock_irqsave(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) c = inb(WDT_RT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) spin_unlock_irqrestore(&wdtpci_lock, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) *temperature = (c * 11 / 15) + 7;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) * wdtpci_interrupt:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) * @irq: Interrupt number
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) * @dev_id: Unused as we don't allow multiple devices.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) * Handle an interrupt from the board. These are raised when the status
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) * map changes in what the board considers an interesting way. That means
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) * a failure condition occurring.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
^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) * Read the status register see what is up and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) * then printk it.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) unsigned char status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) spin_lock(&wdtpci_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) status = inb(WDT_SR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) pr_crit("status %d\n", status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) if (type == 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) if (!(status & WDC_SR_TGOOD)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) pr_crit("Overheat alarm (%d)\n", inb(WDT_RT));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) udelay(8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) if (!(status & WDC_SR_PSUOVER))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) pr_crit("PSU over voltage\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) if (!(status & WDC_SR_PSUUNDR))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) pr_crit("PSU under voltage\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) if (tachometer) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) if (!(status & WDC_SR_FANGOOD))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) pr_crit("Possible fan fault\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) if (!(status & WDC_SR_WCCR)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) #ifdef SOFTWARE_REBOOT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) #ifdef ONLY_TESTING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) pr_crit("Would Reboot\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) pr_crit("Initiating system reboot\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) emergency_restart();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) pr_crit("Reset in 5ms\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) spin_unlock(&wdtpci_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) return IRQ_HANDLED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) * wdtpci_write:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) * @file: file handle to the watchdog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) * @buf: buffer to write (unused as data does not matter here
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) * @count: count of bytes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) * @ppos: pointer to the position to write. No seeks allowed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) * A write to a watchdog device is defined as a keepalive signal. Any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) * write of data will do, as we we don't define content meaning.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) static ssize_t wdtpci_write(struct file *file, const char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) if (count) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) /* In case it was set long ago */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) for (i = 0; i != count; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) if (get_user(c, buf + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) expect_close = 42;
^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) wdtpci_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) return count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) * wdtpci_ioctl:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) * @file: file handle to the device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) * @cmd: watchdog command
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) * @arg: argument pointer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) * The watchdog API defines a common set of functions for all watchdogs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) * according to their available features. We only actually usefully support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) * querying capabilities and current status.
^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) static long wdtpci_ioctl(struct file *file, unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) int __user *p = argp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) int new_heartbeat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) .options = WDIOF_SETTIMEOUT|
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) WDIOF_MAGICCLOSE|
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) WDIOF_KEEPALIVEPING,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) .firmware_version = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) .identity = "PCI-WDT500/501",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) /* Add options according to the card we have */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) if (type == 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) WDIOF_POWEROVER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) if (tachometer)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) ident.options |= WDIOF_FANFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) wdtpci_get_status(&status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) return put_user(status, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) return put_user(0, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) wdtpci_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) if (get_user(new_heartbeat, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) if (wdtpci_set_heartbeat(new_heartbeat))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) wdtpci_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) return put_user(heartbeat, p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) * wdtpci_open:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) * @inode: inode of device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) * @file: file handle to device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) * The watchdog device has been opened. The watchdog device is single
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) * open and on opening we load the counters. Counter zero is a 100Hz
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) * cascade, into counter 1 which downcounts to reboot. When the counter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) * triggers counter 2 downcounts the length of the reset pulse which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) * set set to be as long as possible.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) static int wdtpci_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) if (test_and_set_bit(0, &open_lock))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) if (nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) __module_get(THIS_MODULE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) * Activate
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) wdtpci_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) return stream_open(inode, file);
^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) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) * wdtpci_release:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) * @inode: inode to board
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) * @file: file handle to board
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) * The watchdog has a configurable API. There is a religious dispute
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) * between people who want their watchdog to be able to shut down and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) * those who want to be sure if the watchdog manager dies the machine
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) * reboots. In the former case we disable the counters, in the latter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) * case you have to open it again very soon.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) static int wdtpci_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) if (expect_close == 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) wdtpci_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) pr_crit("Unexpected close, not stopping timer!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) wdtpci_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) clear_bit(0, &open_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) }
^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) * wdtpci_temp_read:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) * @file: file handle to the watchdog board
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) * @buf: buffer to write 1 byte into
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) * @count: length of buffer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) * @ptr: offset (no seek allowed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) * Read reports the temperature in degrees Fahrenheit. The API is in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) * fahrenheit. It was designed by an imperial measurement luddite.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) static ssize_t wdtpci_temp_read(struct file *file, char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) size_t count, loff_t *ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) int temperature;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) if (wdtpci_get_temperature(&temperature))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) if (copy_to_user(buf, &temperature, 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) }
^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) * wdtpci_temp_open:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) * @inode: inode of device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) * @file: file handle to device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) * The temperature device has been opened.
^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) static int wdtpci_temp_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) return stream_open(inode, file);
^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) * wdtpci_temp_release:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) * @inode: inode to board
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) * @file: file handle to board
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) * The temperature device has been closed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) static int wdtpci_temp_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) * notify_sys:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) * @this: our notifier block
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) * @code: the event being reported
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) * @unused: unused
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) * Our notifier is called on system shutdowns. We want to turn the card
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) * off at reboot otherwise the machine will reboot again during memory
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) * test or worse yet during the following fsck. This would suck, in fact
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) * trust me - if it happens it does suck.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) void *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) if (code == SYS_DOWN || code == SYS_HALT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) wdtpci_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) }
^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) * Kernel Interfaces
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) */
^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) static const struct file_operations wdtpci_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) .write = wdtpci_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) .unlocked_ioctl = wdtpci_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) .compat_ioctl = compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) .open = wdtpci_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) .release = wdtpci_release,
^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) static struct miscdevice wdtpci_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) .minor = WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) .name = "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) .fops = &wdtpci_fops,
^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 const struct file_operations wdtpci_temp_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) .read = wdtpci_temp_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) .open = wdtpci_temp_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) .release = wdtpci_temp_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) static struct miscdevice temp_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) .minor = TEMP_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) .name = "temperature",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) .fops = &wdtpci_temp_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) * The WDT card needs to learn about soft shutdowns in order to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) * turn the timebomb registers off.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) static struct notifier_block wdtpci_notifier = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) .notifier_call = wdtpci_notify_sys,
^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)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) static int wdtpci_init_one(struct pci_dev *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) const struct pci_device_id *ent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) int ret = -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) dev_count++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) if (dev_count > 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) pr_err("This driver only supports one device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) if (type != 500 && type != 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) pr_err("unknown card type '%d'\n", type);
^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)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) if (pci_enable_device(dev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) pr_err("Not possible to enable PCI Device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) if (pci_resource_start(dev, 2) == 0x0000) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) pr_err("No I/O-Address for card detected\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) ret = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) goto out_pci;
^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) if (pci_request_region(dev, 2, "wdt_pci")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) pr_err("I/O address 0x%llx already in use\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) (unsigned long long)pci_resource_start(dev, 2));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) goto out_pci;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) irq = dev->irq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) io = pci_resource_start(dev, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) "wdt_pci", &wdtpci_miscdev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) pr_err("IRQ %d is not free\n", irq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) goto out_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) pr_info("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) (unsigned long long)io, irq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) /* Check that the heartbeat value is within its range;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) if not reset to the default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) if (wdtpci_set_heartbeat(heartbeat)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) wdtpci_set_heartbeat(WD_TIMO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) WD_TIMO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) ret = register_reboot_notifier(&wdtpci_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) pr_err("cannot register reboot notifier (err=%d)\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) goto out_irq;
^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) if (type == 501) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) ret = misc_register(&temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) TEMP_MINOR, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) goto out_rbt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) ret = misc_register(&wdtpci_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) pr_err("cannot register miscdev on minor=%d (err=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) WATCHDOG_MINOR, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) goto out_misc;
^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) pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) heartbeat, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) if (type == 501)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) pr_info("Fan Tachometer is %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) tachometer ? "Enabled" : "Disabled");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) out_misc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) if (type == 501)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) misc_deregister(&temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) out_rbt:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) unregister_reboot_notifier(&wdtpci_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) out_irq:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) free_irq(irq, &wdtpci_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) out_reg:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) pci_release_region(dev, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) out_pci:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) pci_disable_device(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) static void wdtpci_remove_one(struct pci_dev *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) /* here we assume only one device will ever have
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) * been picked up and registered by probe function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) misc_deregister(&wdtpci_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) if (type == 501)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) misc_deregister(&temp_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) unregister_reboot_notifier(&wdtpci_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) free_irq(irq, &wdtpci_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) pci_release_region(pdev, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) pci_disable_device(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) dev_count--;
^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)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) static const struct pci_device_id wdtpci_pci_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) .vendor = PCI_VENDOR_ID_ACCESSIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) .subvendor = PCI_ANY_ID,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) .subdevice = PCI_ANY_ID,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) { 0, }, /* terminate list */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) MODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) static struct pci_driver wdtpci_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) .name = "wdt_pci",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) .id_table = wdtpci_pci_tbl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) .probe = wdtpci_init_one,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) .remove = wdtpci_remove_one,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) module_pci_driver(wdtpci_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) MODULE_AUTHOR("JP Nollmann, Alan Cox");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) MODULE_LICENSE("GPL");