11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Industrial Computer Source PCI-WDT500/501 driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
429fa0586de4fe518f122a915b8c6e92d12e8ca7fAlan Cox *	(c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
59b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck *						All Rights Reserved.
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	This program is free software; you can redistribute it and/or
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	modify it under the terms of the GNU General Public License
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	as published by the Free Software Foundation; either version
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	2 of the License, or (at your option) any later version.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	warranty for any of this software. This material is provided
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	"AS-IS" and at no charge.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Release 0.10.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Fixes
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Dave Gregorich	:	Modularisation and minor bugs
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Alan Cox	:	Added the watchdog ioctl() stuff
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Alan Cox	:	Fixed the reboot problem (as noted by
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *					Matt Crocker).
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Alan Cox	:	Added wdt= boot option
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Alan Cox	:	Cleaned up copy/user stuff
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Tim Hockin	:	Added insmod parameters, comment cleanup
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *					Parameterized timeout
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		JP Nollmann	:	Added support for PCI wdt501p
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Alan Cox	:	Split ISA and PCI cards into two drivers
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Jeff Garzik	:	PCI cleanups
329f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *		Tigran Aivazian	:	Restructured wdtpci_init_one() to handle
339f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *					failures
345f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck *		Joel Becker	:	Added WDIOC_GET/SETTIMEOUT
359f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *		Zwane Mwaikambo	:	Magic char closing, locking changes,
369f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *					cleanups
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		Matt Domsch	:	nowayout module option
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
4981830061bbae282d37c9af30084a1116b6239520Andrew Morton#include <linux/delay.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/notifier.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/reboot.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
559f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox#include <linux/io.h>
569f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox#include <linux/uaccess.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WDT_IS_PCI
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "wd501p.h"
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* We can only use 1 card due to the /dev/watchdog restriction */
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dev_count;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
659f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic unsigned long open_lock;
66c7dfd0cca300c5dc49213cf1c78c77393600410dAlexey Dobriyanstatic DEFINE_SPINLOCK(wdtpci_lock);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char expect_close;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
69d1833c21256e7b0ac3997493d31f0f3926f6d592Randy Dunlapstatic resource_size_t io;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Default timeout */
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_TIMO 60			/* Default heartbeat = 60 seconds */
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int heartbeat = WD_TIMO;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_heartbeat;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(heartbeat, int, 0);
789f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan CoxMODULE_PARM_DESC(heartbeat,
799f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		"Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
809f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox				__MODULE_STRING(WD_TIMO) ")");
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8286a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
8386a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
849f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan CoxMODULE_PARM_DESC(nowayout,
859f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		"Watchdog cannot be stopped once started (default="
869f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Support for the Fan Tachometer on the PCI-WDT501 */
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int tachometer;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(tachometer, int, 0);
919f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan CoxMODULE_PARM_DESC(tachometer,
929b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		"PCI-WDT501 Fan Tachometer support (0=disable, default=0)");
939b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck
949b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeckstatic int type = 500;
959b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeckmodule_param(type, int, 0);
969b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van SebroeckMODULE_PARM_DESC(type,
979b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		"PCI-WDT501 Card type (500 or 501 , default=500)");
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Programming support
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wdtpci_ctr_mode(int ctr, int mode)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1059f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ctr <<= 6;
1069f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ctr |= 0x30;
1079f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ctr |= (mode << 1);
1089f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(ctr, WDT_CR);
1099f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wdtpci_ctr_load(int ctr, int val)
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1149f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(val & 0xFF, WDT_COUNT0 + ctr);
1159f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1169f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(val >> 8, WDT_COUNT0 + ctr);
1179f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_start:
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Start the watchdog driver.
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_start(void)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&wdtpci_lock, flags);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * "pet" the watchdog, as Access says.
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * This resets the clock outputs.
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1369f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
1379f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1389f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(2, 0);		/* Program CTR2 for Mode 0:
1399f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Pulse on Terminal Count */
1409f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_DC);		/* Enable watchdog */
1419f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1429f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
1439f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1449f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_CLOCK);		/* 2.0833MHz clock */
1459f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1469f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_BUZZER);		/* disable */
1479f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1489f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_OPTONOTRST);		/* disable */
1499f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1509f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_OPTORST);		/* disable */
1519f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1529f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_PROGOUT);		/* disable */
1539f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1549f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(0, 3);		/* Program CTR0 for Mode 3:
1559f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Square Wave Generator */
1569f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(1, 2);		/* Program CTR1 for Mode 2:
1579f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Rate Generator */
1589f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(2, 1);		/* Program CTR2 for Mode 1:
1599f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Retriggerable One-Shot */
1609f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(0, 20833);	/* count at 100Hz */
1619f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* DO NOT LOAD CTR2 on PCI card! -- JPN */
1639f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_DC);		/* Enable watchdog */
1649f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&wdtpci_lock, flags);
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_stop:
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Stop the watchdog driver.
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1769f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic int wdtpci_stop(void)
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn the card off */
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&wdtpci_lock, flags);
1829f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
1839f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1849f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(2, 0);		/* 0 length reset pulses now */
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&wdtpci_lock, flags);
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_ping:
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1929f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	Reload counter one with the watchdog heartbeat. We don't bother
1939f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	reloading the cascade counter.
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_ping(void)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&wdtpci_lock, flags);
2019f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	/* Write a watchdog value */
2029f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
2039f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
2049f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(1, 2);		/* Re-Program CTR1 for Mode 2:
2059f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox							Rate Generator */
2069f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
2079f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_DC);		/* Enable watchdog */
2089f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&wdtpci_lock, flags);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_set_heartbeat:
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@t:		the new heartbeat value that needs to be set.
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2179f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	Set a new heartbeat value for the watchdog device. If the heartbeat
2189f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	value is incorrect we keep the old value and return -EINVAL.
2199f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	If successful we return 0.
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_set_heartbeat(int t)
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Arbitrary, can't find the card's limits */
2249f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (t < 1 || t > 65535)
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	heartbeat = t;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wd_heartbeat = t * 100;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_get_status:
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@status:		the new status.
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Extract the status information from a WDT watchdog device. There are
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	several board variants so we have to know which bits are valid. Some
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	bits default to one and some to zero in order to be maximally painful.
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	we then map the bits onto the status ioctl flags.
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_get_status(int *status)
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2459f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned char new_status;
2469f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned long flags;
2479f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox
2489f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_lock_irqsave(&wdtpci_lock, flags);
2499f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	new_status = inb(WDT_SR);
2509f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_unlock_irqrestore(&wdtpci_lock, flags);
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2529f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	*status = 0;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (new_status & WDC_SR_ISOI0)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= WDIOF_EXTERN1;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (new_status & WDC_SR_ISII1)
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= WDIOF_EXTERN2;
2579b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
2589b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(new_status & WDC_SR_TGOOD))
2599b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			*status |= WDIOF_OVERHEAT;
2609b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(new_status & WDC_SR_PSUOVER))
2619b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			*status |= WDIOF_POWEROVER;
2629b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(new_status & WDC_SR_PSUUNDR))
2639b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			*status |= WDIOF_POWERUNDER;
2649b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (tachometer) {
2659b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			if (!(new_status & WDC_SR_FANGOOD))
2669b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck				*status |= WDIOF_FANFAULT;
2679b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_get_temperature:
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Reports the temperature in degrees Fahrenheit. The API is in
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	farenheit. It was designed by an imperial measurement luddite.
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_get_temperature(int *temperature)
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2819f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned short c;
2829f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned long flags;
2839f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_lock_irqsave(&wdtpci_lock, flags);
2849f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	c = inb(WDT_RT);
2859f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
2869f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_unlock_irqrestore(&wdtpci_lock, flags);
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*temperature = (c * 11 / 15) + 7;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_interrupt:
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@irq:		Interrupt number
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@dev_id:	Unused as we don't allow multiple devices.
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Handle an interrupt from the board. These are raised when the status
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	map changes in what the board considers an interesting way. That means
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	a failure condition occurring.
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3017d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Read the status register see what is up and
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	then printk it.
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3079f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned char status;
3089f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox
3099f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_lock(&wdtpci_lock);
3109f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox
3119f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	status = inb(WDT_SR);
3129f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_crit("status %d\n", status);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3169b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
3179b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(status & WDC_SR_TGOOD)) {
31827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			pr_crit("Overheat alarm (%d)\n", inb(WDT_RT));
3199b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			udelay(8);
3209b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
3219b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(status & WDC_SR_PSUOVER))
32227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			pr_crit("PSU over voltage\n");
3239b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(status & WDC_SR_PSUUNDR))
32427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			pr_crit("PSU under voltage\n");
3259b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (tachometer) {
3269b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			if (!(status & WDC_SR_FANGOOD))
32727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches				pr_crit("Possible fan fault\n");
3289b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3309b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (!(status & WDC_SR_WCCR)) {
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SOFTWARE_REBOOT
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ONLY_TESTING
33327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_crit("Would Reboot\n");
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
33527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_crit("Initiating system reboot\n");
336f82567e55fcd25bb7addf2cfd8b79f36f409dc2eEric W. Biederman		emergency_restart(NULL);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
33927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_crit("Reset in 5ms\n");
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
34159338d4cb68528062f294d95f116357265936076Ilpo Jarvinen	}
3429f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_unlock(&wdtpci_lock);
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_write:
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to the watchdog
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@buf: buffer to write (unused as data does not matter here
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@count: count of bytes
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@ppos: pointer to the position to write. No seeks allowed
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	A write to a watchdog device is defined as a keepalive signal. Any
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	write of data will do, as we we don't define content meaning.
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3589f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic ssize_t wdtpci_write(struct file *file, const char __user *buf,
3599b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck						size_t count, loff_t *ppos)
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count) {
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nowayout) {
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t i;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3659b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			/* In case it was set long ago */
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			expect_close = 0;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (i = 0; i != count; i++) {
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				char c;
3707944d3a5a70ee5c1904ed1e8b1d71ff0af2854d9Wim Van Sebroeck				if (get_user(c, buf + i))
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -EFAULT;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (c == 'V')
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					expect_close = 42;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_ping();
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_ioctl:
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to the device
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@cmd: watchdog command
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@arg: argument pointer
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The watchdog API defines a common set of functions for all watchdogs
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	according to their available features. We only actually usefully support
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	querying capabilities and current status.
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3929f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic long wdtpci_ioctl(struct file *file, unsigned int cmd,
3939f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox							unsigned long arg)
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int __user *p = argp;
3979b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	int new_heartbeat;
3989b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	int status;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
400c1bf3acffcddcf794e3d8a1d25fb8bd4d5912383Andrew Morton	struct watchdog_info ident = {
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.options =		WDIOF_SETTIMEOUT|
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					WDIOF_MAGICCLOSE|
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					WDIOF_KEEPALIVEPING,
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.firmware_version =	1,
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.identity =		"PCI-WDT500/501",
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Add options according to the card we have */
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
4109b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
4119b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
4129b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck							WDIOF_POWEROVER);
4139b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (tachometer)
4149b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			ident.options |= WDIOF_FANFAULT;
4159b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	}
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4179f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	switch (cmd) {
4189f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETSUPPORT:
4199f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
4209f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETSTATUS:
4219f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		wdtpci_get_status(&status);
4229f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return put_user(status, p);
4239f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETBOOTSTATUS:
4249f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return put_user(0, p);
4259f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_KEEPALIVE:
4269f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		wdtpci_ping();
4279f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return 0;
4289f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_SETTIMEOUT:
4299f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		if (get_user(new_heartbeat, p))
4309f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			return -EFAULT;
4319f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		if (wdtpci_set_heartbeat(new_heartbeat))
4329f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			return -EINVAL;
4339f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		wdtpci_ping();
4349f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		/* Fall */
4359f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETTIMEOUT:
4369f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return put_user(heartbeat, p);
4370c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	default:
4380c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return -ENOTTY;
4390c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	}
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_open:
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode of device
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to device
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The watchdog device has been opened. The watchdog device is single
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	open and on opening we load the counters. Counter zero is a 100Hz
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	cascade, into counter 1 which downcounts to reboot. When the counter
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	triggers counter 2 downcounts the length of the reset pulse which
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	set set to be as long as possible.
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_open(struct inode *inode, struct file *file)
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4569f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (test_and_set_bit(0, &open_lock))
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4599f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (nowayout)
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__module_get(THIS_MODULE);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Activate
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wdtpci_start();
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return nonseekable_open(inode, file);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_release:
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode to board
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to board
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The watchdog has a configurable API. There is a religious dispute
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	between people who want their watchdog to be able to shut down and
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	those who want to be sure if the watchdog manager dies the machine
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	reboots. In the former case we disable the counters, in the latter
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	case you have to open it again very soon.
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_release(struct inode *inode, struct file *file)
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (expect_close == 42) {
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_stop();
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
48527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_crit("Unexpected close, not stopping timer!\n");
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_ping();
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	expect_close = 0;
4899f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	clear_bit(0, &open_lock);
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_temp_read:
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to the watchdog board
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@buf: buffer to write 1 byte into
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@count: length of buffer
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@ptr: offset (no seek allowed)
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Read reports the temperature in degrees Fahrenheit. The API is in
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	fahrenheit. It was designed by an imperial measurement luddite.
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5049f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic ssize_t wdtpci_temp_read(struct file *file, char __user *buf,
5059f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						size_t count, loff_t *ptr)
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int temperature;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wdtpci_get_temperature(&temperature))
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5129f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (copy_to_user(buf, &temperature, 1))
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 1;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_temp_open:
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode of device
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to device
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The temperature device has been opened.
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_temp_open(struct inode *inode, struct file *file)
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return nonseekable_open(inode, file);
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_temp_release:
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode to board
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to board
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The temperature device has been closed.
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_temp_release(struct inode *inode, struct file *file)
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	notify_sys:
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@this: our notifier block
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@code: the event being reported
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@unused: unused
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Our notifier is called on system shutdowns. We want to turn the card
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	off at reboot otherwise the machine will reboot again during memory
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	test or worse yet during the following fsck. This would suck, in fact
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	trust me - if it happens it does suck.
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,
5579f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox							void *unused)
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5599f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (code == SYS_DOWN || code == SYS_HALT)
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_stop();
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NOTIFY_DONE;
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Kernel Interfaces
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
56962322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations wdtpci_fops = {
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek		= no_llseek,
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write		= wdtpci_write,
5739f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	.unlocked_ioctl	= wdtpci_ioctl,
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open		= wdtpci_open,
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release	= wdtpci_release,
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice wdtpci_miscdev = {
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor	= WATCHDOG_MINOR,
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name	= "watchdog",
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops	= &wdtpci_fops,
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58462322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations wdtpci_temp_fops = {
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek		= no_llseek,
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read		= wdtpci_temp_read,
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open		= wdtpci_temp_open,
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release	= wdtpci_temp_release,
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice temp_miscdev = {
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor	= TEMP_MINOR,
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name	= "temperature",
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops	= &wdtpci_temp_fops,
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The WDT card needs to learn about soft shutdowns in order to
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	turn the timebomb registers off.
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct notifier_block wdtpci_notifier = {
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.notifier_call = wdtpci_notify_sys,
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6089f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic int __devinit wdtpci_init_one(struct pci_dev *dev,
6099f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox					const struct pci_device_id *ent)
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -EIO;
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_count++;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev_count > 1) {
61527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("This driver only supports one device\n");
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6199b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type != 500 && type != 501) {
62027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("unknown card type '%d'\n", type);
6219b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		return -ENODEV;
6229b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	}
6239b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck
6249f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (pci_enable_device(dev)) {
62527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Not possible to enable PCI Device\n");
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6299f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (pci_resource_start(dev, 2) == 0x0000) {
63027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("No I/O-Address for card detected\n");
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENODEV;
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_pci;
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
635119d3e56e7c82a73d27b5dd010c52dab1bc9f846Wim Van Sebroeck	if (pci_request_region(dev, 2, "wdt_pci")) {
63627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("I/O address 0x%llx already in use\n",
63727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		       (unsigned long long)pci_resource_start(dev, 2));
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_pci;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641119d3e56e7c82a73d27b5dd010c52dab1bc9f846Wim Van Sebroeck	irq = dev->irq;
642119d3e56e7c82a73d27b5dd010c52dab1bc9f846Wim Van Sebroeck	io = pci_resource_start(dev, 2);
643119d3e56e7c82a73d27b5dd010c52dab1bc9f846Wim Van Sebroeck
64486b5912880453532440358b1486410ad49ef7672Yong Zhang	if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED,
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "wdt_pci", &wdtpci_miscdev)) {
64627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("IRQ %d is not free\n", irq);
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_reg;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
65127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		(unsigned long long)io, irq);
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6539f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	/* Check that the heartbeat value is within its range;
6549f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	   if not reset to the default */
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wdtpci_set_heartbeat(heartbeat)) {
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_set_heartbeat(WD_TIMO);
65727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n",
65827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			WD_TIMO);
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6619f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ret = register_reboot_notifier(&wdtpci_notifier);
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
66327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("cannot register reboot notifier (err=%d)\n", ret);
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_irq;
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6679b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
6689b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		ret = misc_register(&temp_miscdev);
6699b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (ret) {
67027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			pr_err("cannot register miscdev on minor=%d (err=%d)\n",
67127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			       TEMP_MINOR, ret);
6729b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			goto out_rbt;
6739b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6769f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ret = misc_register(&wdtpci_miscdev);
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
67827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
67927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		       WATCHDOG_MINOR, ret);
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_misc;
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		heartbeat, nowayout);
6859b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501)
68627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_info("Fan Tachometer is %s\n",
68727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches			tachometer ? "Enabled" : "Disabled");
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = 0;
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_misc:
6949b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501)
6959b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		misc_deregister(&temp_miscdev);
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_rbt:
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_reboot_notifier(&wdtpci_notifier);
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_irq:
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(irq, &wdtpci_miscdev);
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_reg:
701119d3e56e7c82a73d27b5dd010c52dab1bc9f846Wim Van Sebroeck	pci_release_region(dev, 2);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_pci:
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(dev);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7089f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic void __devexit wdtpci_remove_one(struct pci_dev *pdev)
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* here we assume only one device will ever have
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * been picked up and registered by probe function */
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&wdtpci_miscdev);
7139b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501)
7149b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		misc_deregister(&temp_miscdev);
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_reboot_notifier(&wdtpci_notifier);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(irq, &wdtpci_miscdev);
717119d3e56e7c82a73d27b5dd010c52dab1bc9f846Wim Van Sebroeck	pci_release_region(pdev, 2);
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(pdev);
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_count--;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7234562f53940432369df88e195ef8f9b642bdf7cd6Wim Van Sebroeckstatic DEFINE_PCI_DEVICE_TABLE(wdtpci_pci_tbl) = {
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.vendor	   = PCI_VENDOR_ID_ACCESSIO,
7269f2cc6f759ca0b072107c171a3b5cd79c7ea5de3H Hartley Sweeten		.device	   = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.subvendor = PCI_ANY_ID,
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.subdevice = PCI_ANY_ID,
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0, }, /* terminate list */
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_driver wdtpci_driver = {
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name		= "wdt_pci",
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table	= wdtpci_pci_tbl,
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe		= wdtpci_init_one,
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove		= __devexit_p(wdtpci_remove_one),
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_cleanup:
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Unload the watchdog. You cannot do this with any file handles open.
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	If your watchdog is set to continue ticking on close and you unload
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	it, well it keeps ticking. We won't get the interrupt but the board
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	will not touch PC memory so all is fine. You just have to load a new
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	module in xx seconds or reboot.
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit wdtpci_cleanup(void)
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7559f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	pci_unregister_driver(&wdtpci_driver);
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7605f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck *	wdtpci_init:
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Set up the WDT watchdog board. All we have to do is grab the
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	resources we require and bitch if anyone beat us to them.
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The open() function will actually kick the board off.
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init wdtpci_init(void)
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7699f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	return pci_register_driver(&wdtpci_driver);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(wdtpci_init);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(wdtpci_cleanup);
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("JP Nollmann, Alan Cox");
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(TEMP_MINOR);
781