brintos

brintos / linux-shallow public Read only

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