11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	(c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *			All Rights Reserved.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.
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 *	The author(s) of this software shall not be held liable for damages
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	of any nature resulting due to the use of this software. This
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	software is provided AS-IS with no warranties.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Changelog:
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	20020220 Zwane Mwaikambo	Code based on datasheet, no hardware.
18103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *	20020221 Zwane Mwaikambo	Cleanups as suggested by Jeff Garzik
19103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *					and Alan Cox.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	20020222 Zwane Mwaikambo	Added probing.
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	20020225 Zwane Mwaikambo	Added ISAPNP support.
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	20020412 Rob Radez		Broke out start/stop functions
23103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *		 <rob@osinvestor.com>	Return proper status instead of
24103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *					temperature warning
25103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *					Add WDIOC_GETBOOTSTATUS and
26103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *					WDIOC_SETOPTIONS ioctls
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *					Fix CONFIG_WATCHDOG_NOWAYOUT
28103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *	20020530 Joel Becker		Add Matt Domsch's nowayout module
29103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox *					option
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	20030116 Adam Belay		Updated to the latest pnp code
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/notifier.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/reboot.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pnp.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
476188e10d38b8d7244ee7776d5f1f88c837b4b93fMatthew Wilcox#include <linux/semaphore.h>
48103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox#include <linux/io.h>
49103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox#include <linux/uaccess.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define SC1200_MODULE_VER	"build 20020303"
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define SC1200_MODULE_NAME	"sc1200wdt"
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	MAX_TIMEOUT	255	/* 255 minutes */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PMIR		(io)	/* Power Management Index Register */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PMDR		(io+1)	/* Power Management Data Register */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Data Register indexes */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define FER1		0x00	/* Function enable register 1 */
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define FER2		0x01	/* Function enable register 2 */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PMC1		0x02	/* Power Management Ctrl 1 */
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PMC2		0x03	/* Power Management Ctrl 2 */
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PMC3		0x04	/* Power Management Ctrl 3 */
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WDTO		0x05	/* Watchdog timeout register */
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	WDCF		0x06	/* Watchdog config register */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WDST		0x07	/* Watchdog status register */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* WDCF bitfields - which devices assert WDO */
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define KBC_IRQ		0x01	/* Keyboard Controller */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MSE_IRQ		0x02	/* Mouse */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define UART1_IRQ	0x03	/* Serial0 */
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define UART2_IRQ	0x04	/* Serial1 */
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 5 -7 are reserved */
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int timeout = 1;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io = -1;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io_len = 2;		/* for non plug and play */
78103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic unsigned long open_flag;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char expect_close;
80c7dfd0cca300c5dc49213cf1c78c77393600410dAlexey Dobriyanstatic DEFINE_SPINLOCK(sc1200wdt_lock);	/* io port access serialisation */
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined CONFIG_PNP
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int isapnp = 1;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pnp_dev *wdt_dev;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(isapnp, int, 0);
87103a1d5c57fac3623613b130b104f5b03367b31cAlan CoxMODULE_PARM_DESC(isapnp,
88103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	"When set to 0 driver ISA PnP support will be disabled");
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(io, int, 0);
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(io, "io port");
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(timeout, int, 0);
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9686a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
9786a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
98103a1d5c57fac3623613b130b104f5b03367b31cAlan CoxMODULE_PARM_DESC(nowayout,
99103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	"Watchdog cannot be stopped once started (default="
100103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read from Data Register */
105103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic inline void __sc1200wdt_read_data(unsigned char index,
106103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox						unsigned char *data)
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(index, PMIR);
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*data = inb(PMDR);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
112103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic void sc1200wdt_read_data(unsigned char index, unsigned char *data)
113103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox{
114103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	spin_lock(&sc1200wdt_lock);
115103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	__sc1200wdt_read_data(index, data);
116103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	spin_unlock(&sc1200wdt_lock);
117103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox}
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Write to Data Register */
120103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic inline void __sc1200wdt_write_data(unsigned char index,
121103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox						unsigned char data)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb_p(index, PMIR);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(data, PMDR);
125103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox}
126103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox
127103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic inline void sc1200wdt_write_data(unsigned char index,
128103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox						unsigned char data)
129103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox{
130103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	spin_lock(&sc1200wdt_lock);
131103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	__sc1200wdt_write_data(index, data);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&sc1200wdt_lock);
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void sc1200wdt_start(void)
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char reg;
139103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	spin_lock(&sc1200wdt_lock);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	__sc1200wdt_read_data(WDCF, &reg);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* assert WDO when any of the following interrupts are triggered too */
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
144103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	__sc1200wdt_write_data(WDCF, reg);
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set the timeout and get the ball rolling */
146103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	__sc1200wdt_write_data(WDTO, timeout);
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
148103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	spin_unlock(&sc1200wdt_lock);
149103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox}
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void sc1200wdt_stop(void)
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sc1200wdt_write_data(WDTO, 0);
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This returns the status of the WDO signal, inactive high. */
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int sc1200wdt_status(void)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char ret;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sc1200wdt_read_data(WDST, &ret);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If the bit is inactive, the watchdog is enabled, so return
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * KEEPALIVEPING which is a bit of a kludge because there's nothing
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * else for enabled/disabled status
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
166103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sc1200wdt_open(struct inode *inode, struct file *file)
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* allow one at a time */
172103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	if (test_and_set_bit(0, &open_flag))
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (timeout > MAX_TIMEOUT)
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		timeout = MAX_TIMEOUT;
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sc1200wdt_start();
17927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("Watchdog enabled, timeout = %d min(s)", timeout);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1816abe78bf195c633f67f6349e3d09b2bcd5d32a79Wim Van Sebroeck	return nonseekable_open(inode, file);
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
185103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
186103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox						unsigned long arg)
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int new_timeout;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int __user *p = argp;
191103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	static const struct watchdog_info ident = {
192103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
193103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox							WDIOF_MAGICCLOSE,
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.firmware_version = 0,
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.identity = "PC87307/PC97307",
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
199103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	case WDIOC_GETSUPPORT:
200e04ab958727a4b314df3e40036d72d9348835d0cWim Van Sebroeck		if (copy_to_user(argp, &ident, sizeof(ident)))
201103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox			return -EFAULT;
202103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		return 0;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
204103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	case WDIOC_GETSTATUS:
205103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		return put_user(sc1200wdt_status(), p);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
207103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	case WDIOC_GETBOOTSTATUS:
208103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		return put_user(0, p);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
210103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	case WDIOC_SETOPTIONS:
211103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	{
212103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		int options, retval = -EINVAL;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
214103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		if (get_user(options, p))
215103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox			return -EFAULT;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
217103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		if (options & WDIOS_DISABLECARD) {
218103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox			sc1200wdt_stop();
219103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox			retval = 0;
220103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		}
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
222103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		if (options & WDIOS_ENABLECARD) {
223103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox			sc1200wdt_start();
224103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox			retval = 0;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
226103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox
227103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		return retval;
228103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	}
2290c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	case WDIOC_KEEPALIVE:
2300c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		sc1200wdt_write_data(WDTO, timeout);
2310c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return 0;
2320c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck
2330c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	case WDIOC_SETTIMEOUT:
2340c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		if (get_user(new_timeout, p))
2350c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			return -EFAULT;
2360c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		/* the API states this is given in secs */
2370c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		new_timeout /= 60;
2380c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
2390c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck			return -EINVAL;
2400c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		timeout = new_timeout;
2410c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		sc1200wdt_write_data(WDTO, timeout);
2420c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		/* fall through and return the new timeout */
2430c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck
2440c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	case WDIOC_GETTIMEOUT:
2450c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return put_user(timeout * 60, p);
2460c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck
247103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	default:
248103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox		return -ENOTTY;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sc1200wdt_release(struct inode *inode, struct file *file)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (expect_close == 42) {
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc1200wdt_stop();
25727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_info("Watchdog disabled\n");
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc1200wdt_write_data(WDTO, timeout);
26027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_crit("Unexpected close!, timeout = %d min(s)\n", timeout);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
262103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	clear_bit(0, &open_flag);
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	expect_close = 0;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
269103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic ssize_t sc1200wdt_write(struct file *file, const char __user *data,
270103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox						size_t len, loff_t *ppos)
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (len) {
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nowayout) {
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t i;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			expect_close = 0;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (i = 0; i != len; i++) {
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				char c;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2817944d3a5a70ee5c1904ed1e8b1d71ff0af2854d9Wim Van Sebroeck				if (get_user(c, data + i))
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -EFAULT;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (c == 'V')
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					expect_close = 42;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc1200wdt_write_data(WDTO, timeout);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return len;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
296103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic int sc1200wdt_notify_sys(struct notifier_block *this,
297103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox					unsigned long code, void *unused)
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (code == SYS_DOWN || code == SYS_HALT)
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc1200wdt_stop();
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NOTIFY_DONE;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
306103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic struct notifier_block sc1200wdt_notifier = {
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.notifier_call =	sc1200wdt_notify_sys,
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
310103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic const struct file_operations sc1200wdt_fops = {
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner		= THIS_MODULE,
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.llseek		= no_llseek,
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write		= sc1200wdt_write,
314103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	.unlocked_ioctl = sc1200wdt_ioctl,
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open		= sc1200wdt_open,
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release	= sc1200wdt_release,
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
319103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic struct miscdevice sc1200wdt_miscdev = {
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor		= WATCHDOG_MINOR,
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name		= "watchdog",
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops		= &sc1200wdt_fops,
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init sc1200wdt_probe(void)
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The probe works by reading the PMC3 register's default value of 0x0e
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * there is one caveat, if the device disables the parallel port or any
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * of the UARTs we won't be able to detect it.
331103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	 * NB. This could be done with accuracy by reading the SID registers,
332103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	 * but we don't have access to those io regions.
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char reg;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sc1200wdt_read_data(PMC3, &reg);
338103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	reg &= 0x0f;		/* we don't want the UART busy bits */
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (reg == 0x0e) ? 0 : -ENODEV;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined CONFIG_PNP
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pnp_device_id scl200wdt_pnp_devices[] = {
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* National Semiconductor PC87307/PC97307 watchdog component */
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{.id = "NSC0800", .driver_data = 0},
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{.id = ""},
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic int scl200wdt_pnp_probe(struct pnp_dev *dev,
352103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox					const struct pnp_device_id *dev_id)
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* this driver only supports one card at a time */
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wdt_dev || !isapnp)
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wdt_dev = dev;
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	io = pnp_port_start(wdt_dev, 0);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	io_len = pnp_port_len(wdt_dev, 0);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
36327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register IO port %#x\n", io);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
36727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("PnP device found at io port %#x/%d\n", io, io_len);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371103a1d5c57fac3623613b130b104f5b03367b31cAlan Coxstatic void scl200wdt_pnp_remove(struct pnp_dev *dev)
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
373103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	if (wdt_dev) {
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(io, io_len);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wdt_dev = NULL;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pnp_driver scl200wdt_pnp_driver = {
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name		= "scl200wdt",
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table	= scl200wdt_pnp_devices,
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe		= scl200wdt_pnp_probe,
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove		= scl200wdt_pnp_remove,
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_PNP */
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init sc1200wdt_init(void)
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("%s\n", SC1200_MODULE_VER);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined CONFIG_PNP
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (isapnp) {
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = pnp_register_driver(&scl200wdt_pnp_driver);
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret)
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out_clean;
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (io == -1) {
40427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("io parameter must be specified\n");
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
406150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mita		goto out_pnp;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined CONFIG_PNP
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* now that the user has specified an IO port and we haven't detected
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * any devices, disable pnp support */
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	isapnp = 0;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pnp_unregister_driver(&scl200wdt_pnp_driver);
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
41727c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register IO port %#x\n", io);
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EBUSY;
419150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mita		goto out_pnp;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = sc1200wdt_probe();
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret)
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_io;
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = register_reboot_notifier(&sc1200wdt_notifier);
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
42827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register reboot notifier err = %d\n", ret);
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_io;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = misc_register(&sc1200wdt_miscdev);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
43427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("Unable to register miscdev on minor %d\n",
43527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		       WATCHDOG_MINOR);
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_rbt;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* ret = 0 */
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_clean:
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_rbt:
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_reboot_notifier(&sc1200wdt_notifier);
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_io:
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(io, io_len);
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
450150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mitaout_pnp:
451150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mita#if defined CONFIG_PNP
452150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mita	if (isapnp)
453150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mita		pnp_unregister_driver(&scl200wdt_pnp_driver);
454150ed8ed63b96d7f93ef7e6081797aa0df2b1abdAkinobu Mita#endif
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out_clean;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit sc1200wdt_exit(void)
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&sc1200wdt_miscdev);
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_reboot_notifier(&sc1200wdt_notifier);
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined CONFIG_PNP
465103a1d5c57fac3623613b130b104f5b03367b31cAlan Cox	if (isapnp)
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pnp_unregister_driver(&scl200wdt_pnp_driver);
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(io, io_len);
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(sc1200wdt_init);
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(sc1200wdt_exit);
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
476143a2e54bf53216674eada16e8953f48b159e08aWim Van SebroeckMODULE_DESCRIPTION(
477143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck	"Driver for National Semiconductor PC87307/PC97307 watchdog component");
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
480