Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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");