wdt_pci.c revision 9b901ee0cb007eb4e2ee056e5b1c5c2837d53bdb
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
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		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
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
4781830061bbae282d37c9af30084a1116b6239520Andrew Morton#include <linux/delay.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/notifier.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/reboot.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
539f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox#include <linux/io.h>
549f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox#include <linux/uaccess.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/system.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WDT_IS_PCI
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "wd501p.h"
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PFX "wdt_pci: "
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Until Access I/O gets their application for a PCI vendor ID approved,
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * I don't think that it's appropriate to move these constants into the
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * regular pci_ids.h file. -- JPN 2000/01/18
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef PCI_VENDOR_ID_ACCESSIO
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PCI_VENDOR_ID_ACCESSIO 0x494f
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef PCI_DEVICE_ID_WDG_CSM
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PCI_DEVICE_ID_WDG_CSM 0x22c0
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* We can only use 1 card due to the /dev/watchdog restriction */
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dev_count;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
799f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic unsigned long open_lock;
80c7dfd0cca300c5dc49213cf1c78c77393600410dAlexey Dobriyanstatic DEFINE_SPINLOCK(wdtpci_lock);
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char expect_close;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Default timeout */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WD_TIMO 60			/* Default heartbeat = 60 seconds */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int heartbeat = WD_TIMO;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wd_heartbeat;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(heartbeat, int, 0);
929f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan CoxMODULE_PARM_DESC(heartbeat,
939f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		"Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
949f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox				__MODULE_STRING(WD_TIMO) ")");
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
964bfdf37830111321e2cd1fe0102dd776ce93194dAndrey Paninstatic int nowayout = WATCHDOG_NOWAYOUT;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(nowayout, int, 0);
989f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan CoxMODULE_PARM_DESC(nowayout,
999f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		"Watchdog cannot be stopped once started (default="
1009f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Support for the Fan Tachometer on the PCI-WDT501 */
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int tachometer;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(tachometer, int, 0);
1059f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan CoxMODULE_PARM_DESC(tachometer,
1069b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		"PCI-WDT501 Fan Tachometer support (0=disable, default=0)");
1079b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck
1089b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeckstatic int type = 500;
1099b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeckmodule_param(type, int, 0);
1109b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van SebroeckMODULE_PARM_DESC(type,
1119b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		"PCI-WDT501 Card type (500 or 501 , default=500)");
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Programming support
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wdtpci_ctr_mode(int ctr, int mode)
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1199f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ctr <<= 6;
1209f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ctr |= 0x30;
1219f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ctr |= (mode << 1);
1229f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(ctr, WDT_CR);
1239f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void wdtpci_ctr_load(int ctr, int val)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1289f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(val & 0xFF, WDT_COUNT0 + ctr);
1299f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1309f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(val >> 8, WDT_COUNT0 + ctr);
1319f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_start:
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Start the watchdog driver.
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_start(void)
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&wdtpci_lock, flags);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * "pet" the watchdog, as Access says.
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * This resets the clock outputs.
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1509f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
1519f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1529f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(2, 0);		/* Program CTR2 for Mode 0:
1539f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Pulse on Terminal Count */
1549f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_DC);		/* Enable watchdog */
1559f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1569f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
1579f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1589f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_CLOCK);		/* 2.0833MHz clock */
1599f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1609f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_BUZZER);		/* disable */
1619f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1629f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_OPTONOTRST);		/* disable */
1639f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1649f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_OPTORST);		/* disable */
1659f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1669f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_PROGOUT);		/* disable */
1679f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1689f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(0, 3);		/* Program CTR0 for Mode 3:
1699f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Square Wave Generator */
1709f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(1, 2);		/* Program CTR1 for Mode 2:
1719f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Rate Generator */
1729f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(2, 1);		/* Program CTR2 for Mode 1:
1739f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						Retriggerable One-Shot */
1749f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(0, 20833);	/* count at 100Hz */
1759f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* DO NOT LOAD CTR2 on PCI card! -- JPN */
1779f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_DC);		/* Enable watchdog */
1789f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&wdtpci_lock, flags);
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_stop:
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Stop the watchdog driver.
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1909f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic int wdtpci_stop(void)
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn the card off */
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&wdtpci_lock, flags);
1969f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
1979f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
1989f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(2, 0);		/* 0 length reset pulses now */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&wdtpci_lock, flags);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_ping:
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2069f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	Reload counter one with the watchdog heartbeat. We don't bother
2079f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	reloading the cascade counter.
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_ping(void)
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&wdtpci_lock, flags);
2159f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	/* Write a watchdog value */
2169f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	inb(WDT_DC);			/* Disable watchdog */
2179f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
2189f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_mode(1, 2);		/* Re-Program CTR1 for Mode 2:
2199f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox							Rate Generator */
2209f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */
2219f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	outb(0, WDT_DC);		/* Enable watchdog */
2229f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&wdtpci_lock, flags);
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_set_heartbeat:
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@t:		the new heartbeat value that needs to be set.
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2319f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	Set a new heartbeat value for the watchdog device. If the heartbeat
2329f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	value is incorrect we keep the old value and return -EINVAL.
2339f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox *	If successful we return 0.
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_set_heartbeat(int t)
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Arbitrary, can't find the card's limits */
2389f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (t < 1 || t > 65535)
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	heartbeat = t;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wd_heartbeat = t * 100;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_get_status:
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@status:		the new status.
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Extract the status information from a WDT watchdog device. There are
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	several board variants so we have to know which bits are valid. Some
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	bits default to one and some to zero in order to be maximally painful.
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	we then map the bits onto the status ioctl flags.
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_get_status(int *status)
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2599f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned char new_status;
2609f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned long flags;
2619f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox
2629f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_lock_irqsave(&wdtpci_lock, flags);
2639f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	new_status = inb(WDT_SR);
2649f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_unlock_irqrestore(&wdtpci_lock, flags);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2669f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	*status = 0;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (new_status & WDC_SR_ISOI0)
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= WDIOF_EXTERN1;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (new_status & WDC_SR_ISII1)
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= WDIOF_EXTERN2;
2719b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
2729b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(new_status & WDC_SR_TGOOD))
2739b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			*status |= WDIOF_OVERHEAT;
2749b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(new_status & WDC_SR_PSUOVER))
2759b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			*status |= WDIOF_POWEROVER;
2769b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(new_status & WDC_SR_PSUUNDR))
2779b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			*status |= WDIOF_POWERUNDER;
2789b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (tachometer) {
2799b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			if (!(new_status & WDC_SR_FANGOOD))
2809b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck				*status |= WDIOF_FANFAULT;
2819b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_get_temperature:
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Reports the temperature in degrees Fahrenheit. The API is in
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	farenheit. It was designed by an imperial measurement luddite.
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_get_temperature(int *temperature)
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2959f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned short c;
2969f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned long flags;
2979f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_lock_irqsave(&wdtpci_lock, flags);
2989f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	c = inb(WDT_RT);
2999f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
3009f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_unlock_irqrestore(&wdtpci_lock, flags);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*temperature = (c * 11 / 15) + 7;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_interrupt:
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@irq:		Interrupt number
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@dev_id:	Unused as we don't allow multiple devices.
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Handle an interrupt from the board. These are raised when the status
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	map changes in what the board considers an interesting way. That means
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	a failure condition occurring.
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3157d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Read the status register see what is up and
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	then printk it.
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3219f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	unsigned char status;
3229f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox
3239f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_lock(&wdtpci_lock);
3249f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox
3259f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	status = inb(WDT_SR);
3269f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	udelay(8);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_CRIT PFX "status %d\n", status);
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3309b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
3319b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(status & WDC_SR_TGOOD)) {
3329b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			printk(KERN_CRIT PFX "Overheat alarm.(%d)\n",
3339b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck								inb(WDT_RT));
3349b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			udelay(8);
3359b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
3369b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(status & WDC_SR_PSUOVER))
3379b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			printk(KERN_CRIT PFX "PSU over voltage.\n");
3389b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (!(status & WDC_SR_PSUUNDR))
3399b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			printk(KERN_CRIT PFX "PSU under voltage.\n");
3409b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (tachometer) {
3419b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			if (!(status & WDC_SR_FANGOOD))
3429b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck				printk(KERN_CRIT PFX "Possible fan fault.\n");
3439b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3459b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (!(status & WDC_SR_WCCR)) {
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SOFTWARE_REBOOT
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ONLY_TESTING
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_CRIT PFX "Would Reboot.\n");
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_CRIT PFX "Initiating system reboot.\n");
351f82567e55fcd25bb7addf2cfd8b79f36f409dc2eEric W. Biederman		emergency_restart(NULL);
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_CRIT PFX "Reset in 5ms.\n");
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
35659338d4cb68528062f294d95f116357265936076Ilpo Jarvinen	}
3579f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	spin_unlock(&wdtpci_lock);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_write:
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to the watchdog
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@buf: buffer to write (unused as data does not matter here
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@count: count of bytes
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@ppos: pointer to the position to write. No seeks allowed
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	A write to a watchdog device is defined as a keepalive signal. Any
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	write of data will do, as we we don't define content meaning.
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3739f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic ssize_t wdtpci_write(struct file *file, const char __user *buf,
3749b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck						size_t count, loff_t *ppos)
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count) {
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nowayout) {
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t i;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3809b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			/* In case it was set long ago */
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			expect_close = 0;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (i = 0; i != count; i++) {
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				char c;
3857944d3a5a70ee5c1904ed1e8b1d71ff0af2854d9Wim Van Sebroeck				if (get_user(c, buf + i))
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -EFAULT;
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (c == 'V')
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					expect_close = 42;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_ping();
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_ioctl:
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to the device
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@cmd: watchdog command
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@arg: argument pointer
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The watchdog API defines a common set of functions for all watchdogs
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	according to their available features. We only actually usefully support
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	querying capabilities and current status.
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4079f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic long wdtpci_ioctl(struct file *file, unsigned int cmd,
4089f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox							unsigned long arg)
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int __user *p = argp;
4129b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	int new_heartbeat;
4139b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	int status;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static struct watchdog_info ident = {
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.options =		WDIOF_SETTIMEOUT|
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					WDIOF_MAGICCLOSE|
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					WDIOF_KEEPALIVEPING,
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.firmware_version =	1,
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.identity =		"PCI-WDT500/501",
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Add options according to the card we have */
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
4259b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
4269b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
4279b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck							WDIOF_POWEROVER);
4289b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (tachometer)
4299b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			ident.options |= WDIOF_FANFAULT;
4309b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4329f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	switch (cmd) {
4339f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETSUPPORT:
4349f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
4359f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETSTATUS:
4369f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		wdtpci_get_status(&status);
4379f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return put_user(status, p);
4389f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETBOOTSTATUS:
4399f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return put_user(0, p);
4409f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_KEEPALIVE:
4419f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		wdtpci_ping();
4429f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return 0;
4439f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_SETTIMEOUT:
4449f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		if (get_user(new_heartbeat, p))
4459f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			return -EFAULT;
4469f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		if (wdtpci_set_heartbeat(new_heartbeat))
4479f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			return -EINVAL;
4489f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		wdtpci_ping();
4499f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		/* Fall */
4509f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	case WDIOC_GETTIMEOUT:
4519f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		return put_user(heartbeat, p);
4520c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	default:
4530c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return -ENOTTY;
4540c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	}
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_open:
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode of device
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to device
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The watchdog device has been opened. The watchdog device is single
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	open and on opening we load the counters. Counter zero is a 100Hz
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	cascade, into counter 1 which downcounts to reboot. When the counter
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	triggers counter 2 downcounts the length of the reset pulse which
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	set set to be as long as possible.
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_open(struct inode *inode, struct file *file)
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4719f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (test_and_set_bit(0, &open_lock))
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4749f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (nowayout)
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__module_get(THIS_MODULE);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *	Activate
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wdtpci_start();
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return nonseekable_open(inode, file);
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_release:
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode to board
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to board
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The watchdog has a configurable API. There is a religious dispute
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	between people who want their watchdog to be able to shut down and
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	those who want to be sure if the watchdog manager dies the machine
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	reboots. In the former case we disable the counters, in the latter
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	case you have to open it again very soon.
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_release(struct inode *inode, struct file *file)
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (expect_close == 42) {
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_stop();
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_CRIT PFX "Unexpected close, not stopping timer!");
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_ping();
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	expect_close = 0;
5049f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	clear_bit(0, &open_lock);
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_temp_read:
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to the watchdog board
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@buf: buffer to write 1 byte into
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@count: length of buffer
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@ptr: offset (no seek allowed)
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Read reports the temperature in degrees Fahrenheit. The API is in
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	fahrenheit. It was designed by an imperial measurement luddite.
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5199f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic ssize_t wdtpci_temp_read(struct file *file, char __user *buf,
5209f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						size_t count, loff_t *ptr)
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int temperature;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wdtpci_get_temperature(&temperature))
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5279f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (copy_to_user(buf, &temperature, 1))
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 1;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_temp_open:
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode of device
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to device
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The temperature device has been opened.
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_temp_open(struct inode *inode, struct file *file)
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return nonseekable_open(inode, file);
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_temp_release:
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@inode: inode to board
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@file: file handle to board
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The temperature device has been closed.
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_temp_release(struct inode *inode, struct file *file)
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	notify_sys:
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@this: our notifier block
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@code: the event being reported
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	@unused: unused
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Our notifier is called on system shutdowns. We want to turn the card
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	off at reboot otherwise the machine will reboot again during memory
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	test or worse yet during the following fsck. This would suck, in fact
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	trust me - if it happens it does suck.
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,
5729f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox							void *unused)
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5749f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (code == SYS_DOWN || code == SYS_HALT)
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_stop();
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NOTIFY_DONE;
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Kernel Interfaces
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58462322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations wdtpci_fops = {
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek		= no_llseek,
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write		= wdtpci_write,
5889f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	.unlocked_ioctl	= wdtpci_ioctl,
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open		= wdtpci_open,
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release	= wdtpci_release,
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice wdtpci_miscdev = {
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor	= WATCHDOG_MINOR,
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name	= "watchdog",
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops	= &wdtpci_fops,
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59962322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations wdtpci_temp_fops = {
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek		= no_llseek,
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read		= wdtpci_temp_read,
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open		= wdtpci_temp_open,
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release	= wdtpci_temp_release,
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice temp_miscdev = {
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor	= TEMP_MINOR,
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name	= "temperature",
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops	= &wdtpci_temp_fops,
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The WDT card needs to learn about soft shutdowns in order to
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	turn the timebomb registers off.
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct notifier_block wdtpci_notifier = {
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.notifier_call = wdtpci_notify_sys,
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6239f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic int __devinit wdtpci_init_one(struct pci_dev *dev,
6249f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox					const struct pci_device_id *ent)
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -EIO;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_count++;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev_count > 1) {
6309f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX "This driver only supports one device\n");
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6349b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type != 500 && type != 501) {
6359b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		printk(KERN_ERR PFX "unknown card type '%d'.\n", type);
6369b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		return -ENODEV;
6379b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	}
6389b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck
6399f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (pci_enable_device(dev)) {
6409f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX "Not possible to enable PCI Device\n");
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6449f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (pci_resource_start(dev, 2) == 0x0000) {
6459f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX "No I/O-Address for card detected\n");
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENODEV;
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_pci;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = dev->irq;
6519f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	io = pci_resource_start(dev, 2);
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6539f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (request_region(io, 16, "wdt_pci") == NULL) {
6549f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", io);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_pci;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6589f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	if (request_irq(irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED,
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "wdt_pci", &wdtpci_miscdev)) {
6609f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX "IRQ %d is not free\n", irq);
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_reg;
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6649f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	printk(KERN_INFO
6659f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	 "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)\n",
6669f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox								io, irq);
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6689f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	/* Check that the heartbeat value is within its range;
6699f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	   if not reset to the default */
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wdtpci_set_heartbeat(heartbeat)) {
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdtpci_set_heartbeat(WD_TIMO);
6729f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_INFO PFX
6739f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		  "heartbeat value must be 0 < heartbeat < 65536, using %d\n",
6749f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox								WD_TIMO);
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6779f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ret = register_reboot_notifier(&wdtpci_notifier);
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
6799f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX
6809f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			"cannot register reboot notifier (err=%d)\n", ret);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_irq;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6849b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501) {
6859b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		ret = misc_register(&temp_miscdev);
6869b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		if (ret) {
6879b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			printk(KERN_ERR PFX
6889f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			"cannot register miscdev on minor=%d (err=%d)\n",
6899b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck							TEMP_MINOR, ret);
6909b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck			goto out_rbt;
6919b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		}
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6949f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	ret = misc_register(&wdtpci_miscdev);
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
6969f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox		printk(KERN_ERR PFX
6979f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox			"cannot register miscdev on minor=%d (err=%d)\n",
6989f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox						WATCHDOG_MINOR, ret);
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_misc;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		heartbeat, nowayout);
7049b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501)
7059b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
7069f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox				(tachometer ? "Enabled" : "Disabled"));
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = 0;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_misc:
7139b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501)
7149b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		misc_deregister(&temp_miscdev);
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_rbt:
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_reboot_notifier(&wdtpci_notifier);
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_irq:
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(irq, &wdtpci_miscdev);
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_reg:
7209f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	release_region(io, 16);
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_pci:
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(dev);
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7279f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Coxstatic void __devexit wdtpci_remove_one(struct pci_dev *pdev)
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* here we assume only one device will ever have
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * been picked up and registered by probe function */
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&wdtpci_miscdev);
7329b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck	if (type == 501)
7339b901ee0cb007eb4e2ee056e5b1c5c2837d53bdbWim Van Sebroeck		misc_deregister(&temp_miscdev);
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_reboot_notifier(&wdtpci_notifier);
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(irq, &wdtpci_miscdev);
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(io, 16);
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(pdev);
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_count--;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_device_id wdtpci_pci_tbl[] = {
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.vendor	   = PCI_VENDOR_ID_ACCESSIO,
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.device	   = PCI_DEVICE_ID_WDG_CSM,
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.subvendor = PCI_ANY_ID,
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.subdevice = PCI_ANY_ID,
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0, }, /* terminate list */
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl);
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_driver wdtpci_driver = {
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name		= "wdt_pci",
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table	= wdtpci_pci_tbl,
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe		= wdtpci_init_one,
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove		= __devexit_p(wdtpci_remove_one),
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	wdtpci_cleanup:
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Unload the watchdog. You cannot do this with any file handles open.
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	If your watchdog is set to continue ticking on close and you unload
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	it, well it keeps ticking. We won't get the interrupt but the board
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	will not touch PC memory so all is fine. You just have to load a new
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	module in xx seconds or reboot.
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit wdtpci_cleanup(void)
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7749f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	pci_unregister_driver(&wdtpci_driver);
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 	wdtpci_init:
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Set up the WDT watchdog board. All we have to do is grab the
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	resources we require and bitch if anyone beat us to them.
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	The open() function will actually kick the board off.
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init wdtpci_init(void)
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7889f2d1f0da766f84fdb96c9bd79ed0f97036635cbAlan Cox	return pci_register_driver(&wdtpci_driver);
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(wdtpci_init);
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(wdtpci_cleanup);
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("JP Nollmann, Alan Cox");
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards");
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(TEMP_MINOR);
800