Release 4.12 drivers/watchdog/it87_wdt.c
  
  
  
/*
 *      Watchdog Timer Driver
 *         for ITE IT87xx Environment Control - Low Pin Count Input / Output
 *
 *      (c) Copyright 2007  Oliver Schuster <olivers137@aol.com>
 *
 *      Based on softdog.c      by Alan Cox,
 *               83977f_wdt.c   by Jose Goncalves,
 *               it87.c         by Chris Gauthron, Jean Delvare
 *
 *      Data-sheets: Publicly available at the ITE website
 *                  http://www.ite.com.tw/
 *
 *      Support of the watchdog timers, which are available on
 *      IT8620, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726,
 *      IT8728 and IT8783.
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define WATCHDOG_VERSION	"1.14"
#define WATCHDOG_NAME		"IT87 WDT"
#define DRIVER_VERSION		WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
#define WD_MAGIC		'V'
/* Defaults for Module Parameter */
#define DEFAULT_NOGAMEPORT	0
#define DEFAULT_NOCIR		0
#define DEFAULT_EXCLUSIVE	1
#define DEFAULT_TIMEOUT		60
#define DEFAULT_TESTMODE	0
#define DEFAULT_NOWAYOUT	WATCHDOG_NOWAYOUT
/* IO Ports */
#define REG		0x2e
#define VAL		0x2f
/* Logical device Numbers LDN */
#define GPIO		0x07
#define GAMEPORT	0x09
#define CIR		0x0a
/* Configuration Registers and Functions */
#define LDNREG		0x07
#define CHIPID		0x20
#define CHIPREV		0x22
#define ACTREG		0x30
#define BASEREG		0x60
/* Chip Id numbers */
#define NO_DEV_ID	0xffff
#define IT8620_ID	0x8620
#define IT8702_ID	0x8702
#define IT8705_ID	0x8705
#define IT8712_ID	0x8712
#define IT8716_ID	0x8716
#define IT8718_ID	0x8718
#define IT8720_ID	0x8720
#define IT8721_ID	0x8721
#define IT8726_ID	0x8726	
/* the data sheet suggest wrongly 0x8716 */
#define IT8728_ID	0x8728
#define IT8783_ID	0x8783
/* GPIO Configuration Registers LDN=0x07 */
#define WDTCTRL		0x71
#define WDTCFG		0x72
#define WDTVALLSB	0x73
#define WDTVALMSB	0x74
/* GPIO Bits WDTCTRL */
#define WDT_CIRINT	0x80
#define WDT_MOUSEINT	0x40
#define WDT_KYBINT	0x20
#define WDT_GAMEPORT	0x10 
/* not in it8718, it8720, it8721, it8728 */
#define WDT_FORCE	0x02
#define WDT_ZERO	0x01
/* GPIO Bits WDTCFG */
#define WDT_TOV1	0x80
#define WDT_KRST	0x40
#define WDT_TOVE	0x20
#define WDT_PWROK	0x10 
/* not in it8721 */
#define WDT_INT_MASK	0x0f
/* CIR Configuration Register LDN=0x0a */
#define CIR_ILS		0x70
/* The default Base address is not always available, we use this */
#define CIR_BASE	0x0208
/* CIR Controller */
#define CIR_DR(b)	(b)
#define CIR_IER(b)	(b + 1)
#define CIR_RCR(b)	(b + 2)
#define CIR_TCR1(b)	(b + 3)
#define CIR_TCR2(b)	(b + 4)
#define CIR_TSR(b)	(b + 5)
#define CIR_RSR(b)	(b + 6)
#define CIR_BDLR(b)	(b + 5)
#define CIR_BDHR(b)	(b + 6)
#define CIR_IIR(b)	(b + 7)
/* Default Base address of Game port */
#define GP_BASE_DEFAULT	0x0201
/* wdt_status */
#define WDTS_TIMER_RUN	0
#define WDTS_DEV_OPEN	1
#define WDTS_KEEPALIVE	2
#define WDTS_LOCKED	3
#define WDTS_USE_GP	4
#define WDTS_EXPECTED	5
#define WDTS_USE_CIR	6
static	unsigned int base, gpact, ciract, max_units, chip_type;
static	unsigned long wdt_status;
static	int nogameport = DEFAULT_NOGAMEPORT;
static int nocir      = DEFAULT_NOCIR;
static	int exclusive  = DEFAULT_EXCLUSIVE;
static	int timeout    = DEFAULT_TIMEOUT;
static	int testmode   = DEFAULT_TESTMODE;
static	bool nowayout   = DEFAULT_NOWAYOUT;
module_param(nogameport, int, 0);
MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
		__MODULE_STRING(DEFAULT_NOGAMEPORT));
module_param(nocir, int, 0);
MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default="
		__MODULE_STRING(DEFAULT_NOCIR));
module_param(exclusive, int, 0);
MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
		__MODULE_STRING(DEFAULT_EXCLUSIVE));
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
		__MODULE_STRING(DEFAULT_TIMEOUT));
module_param(testmode, int, 0);
MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
		__MODULE_STRING(DEFAULT_TESTMODE));
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
		__MODULE_STRING(WATCHDOG_NOWAYOUT));
/* Superio Chip */
static inline int superio_enter(void)
{
	/*
         * Try to reserve REG and REG + 1 for exclusive access.
         */
	if (!request_muxed_region(REG, 2, WATCHDOG_NAME))
		return -EBUSY;
	outb(0x87, REG);
	outb(0x01, REG);
	outb(0x55, REG);
	outb(0x55, REG);
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 36 | 63.16% | 1 | 50.00% | 
| Nat Gurumoorthy | 21 | 36.84% | 1 | 50.00% | 
| Total | 57 | 100.00% | 2 | 100.00% | 
static inline void superio_exit(void)
{
	outb(0x02, REG);
	outb(0x02, VAL);
	release_region(REG, 2);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 23 | 76.67% | 1 | 50.00% | 
| Nat Gurumoorthy | 7 | 23.33% | 1 | 50.00% | 
| Total | 30 | 100.00% | 2 | 100.00% | 
static inline void superio_select(int ldn)
{
	outb(LDNREG, REG);
	outb(ldn, VAL);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 24 | 100.00% | 1 | 100.00% | 
| Total | 24 | 100.00% | 1 | 100.00% | 
static inline int superio_inb(int reg)
{
	outb(reg, REG);
	return inb(VAL);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 23 | 100.00% | 1 | 100.00% | 
| Total | 23 | 100.00% | 1 | 100.00% | 
static inline void superio_outb(int val, int reg)
{
	outb(reg, REG);
	outb(val, VAL);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 27 | 100.00% | 1 | 100.00% | 
| Total | 27 | 100.00% | 1 | 100.00% | 
static inline int superio_inw(int reg)
{
	int val;
	outb(reg++, REG);
	val = inb(VAL) << 8;
	outb(reg, REG);
	val |= inb(VAL);
	return val;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 47 | 100.00% | 1 | 100.00% | 
| Total | 47 | 100.00% | 1 | 100.00% | 
static inline void superio_outw(int val, int reg)
{
	outb(reg++, REG);
	outb(val >> 8, VAL);
	outb(reg, REG);
	outb(val, VAL);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 44 | 100.00% | 1 | 100.00% | 
| Total | 44 | 100.00% | 1 | 100.00% | 
/* Internal function, should be called after superio_select(GPIO) */
static void wdt_update_timeout(void)
{
	unsigned char cfg = WDT_KRST;
	int tm = timeout;
	if (testmode)
		cfg = 0;
	if (tm <= max_units)
		cfg |= WDT_TOV1;
	else
		tm /= 60;
	if (chip_type != IT8721_ID)
		cfg |= WDT_PWROK;
	superio_outb(cfg, WDTCFG);
	superio_outb(tm, WDTVALLSB);
	if (max_units > 255)
		superio_outb(tm>>8, WDTVALMSB);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Ondrej Zajicek | 71 | 87.65% | 1 | 50.00% | 
| Huaro Tomita | 10 | 12.35% | 1 | 50.00% | 
| Total | 81 | 100.00% | 2 | 100.00% | 
static int wdt_round_time(int t)
{
	t += 59;
	t -= t % 60;
	return t;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Ondrej Zajicek | 22 | 100.00% | 1 | 100.00% | 
| Total | 22 | 100.00% | 1 | 100.00% | 
/* watchdog timer handling */
static void wdt_keepalive(void)
{
	if (test_bit(WDTS_USE_GP, &wdt_status))
		inb(base);
	else if (test_bit(WDTS_USE_CIR, &wdt_status))
		/* The timer reloads with around 5 msec delay */
		outb(0x55, CIR_DR(base));
	else {
		if (superio_enter())
			return;
		superio_select(GPIO);
		wdt_update_timeout();
		superio_exit();
	}
	set_bit(WDTS_KEEPALIVE, &wdt_status);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 42 | 57.53% | 1 | 50.00% | 
| Marc van der Wal | 31 | 42.47% | 1 | 50.00% | 
| Total | 73 | 100.00% | 2 | 100.00% | 
static int wdt_start(void)
{
	int ret = superio_enter();
	if (ret)
		return ret;
	superio_select(GPIO);
	if (test_bit(WDTS_USE_GP, &wdt_status))
		superio_outb(WDT_GAMEPORT, WDTCTRL);
	else if (test_bit(WDTS_USE_CIR, &wdt_status))
		superio_outb(WDT_CIRINT, WDTCTRL);
	wdt_update_timeout();
	superio_exit();
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 42 | 60.00% | 1 | 25.00% | 
| Nat Gurumoorthy | 15 | 21.43% | 1 | 25.00% | 
| Marc van der Wal | 11 | 15.71% | 1 | 25.00% | 
| Ondrej Zajicek | 2 | 2.86% | 1 | 25.00% | 
| Total | 70 | 100.00% | 4 | 100.00% | 
static int wdt_stop(void)
{
	int ret = superio_enter();
	if (ret)
		return ret;
	superio_select(GPIO);
	superio_outb(0x00, WDTCTRL);
	superio_outb(WDT_TOV1, WDTCFG);
	superio_outb(0x00, WDTVALLSB);
	if (max_units > 255)
		superio_outb(0x00, WDTVALMSB);
	superio_exit();
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 43 | 65.15% | 1 | 33.33% | 
| Nat Gurumoorthy | 15 | 22.73% | 1 | 33.33% | 
| Ondrej Zajicek | 8 | 12.12% | 1 | 33.33% | 
| Total | 66 | 100.00% | 3 | 100.00% | 
/**
 *      wdt_set_timeout - set a new timeout value with watchdog ioctl
 *      @t: timeout value in seconds
 *
 *      The hardware device has a 8 or 16 bit watchdog timer (depends on
 *      chip version) that can be configured to count seconds or minutes.
 *
 *      Used within WDIOC_SETTIMEOUT watchdog device ioctl.
 */
static int wdt_set_timeout(int t)
{
	if (t < 1 || t > max_units * 60)
		return -EINVAL;
	if (t > max_units)
		timeout = wdt_round_time(t);
	else
		timeout = t;
	if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
		int ret = superio_enter();
		if (ret)
			return ret;
		superio_select(GPIO);
		wdt_update_timeout();
		superio_exit();
	}
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 52 | 63.41% | 1 | 33.33% | 
| Ondrej Zajicek | 19 | 23.17% | 1 | 33.33% | 
| Nat Gurumoorthy | 11 | 13.41% | 1 | 33.33% | 
| Total | 82 | 100.00% | 3 | 100.00% | 
/**
 *      wdt_get_status - determines the status supported by watchdog ioctl
 *      @status: status returned to user space
 *
 *      The status bit of the device does not allow to distinguish
 *      between a regular system reset and a watchdog forced reset.
 *      But, in test mode it is useful, so it is supported through
 *      WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
 *      reports the keepalive signal and the acception of the magic.
 *
 *      Used within WDIOC_GETSTATUS watchdog device ioctl.
 */
static int wdt_get_status(int *status)
{
	*status = 0;
	if (testmode) {
		int ret = superio_enter();
		if (ret)
			return ret;
		superio_select(GPIO);
		if (superio_inb(WDTCTRL) & WDT_ZERO) {
			superio_outb(0x00, WDTCTRL);
			clear_bit(WDTS_TIMER_RUN, &wdt_status);
			*status |= WDIOF_CARDRESET;
		}
		superio_exit();
	}
	if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
		*status |= WDIOF_KEEPALIVEPING;
	if (test_bit(WDTS_EXPECTED, &wdt_status))
		*status |= WDIOF_MAGICCLOSE;
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 95 | 89.62% | 1 | 50.00% | 
| Nat Gurumoorthy | 11 | 10.38% | 1 | 50.00% | 
| Total | 106 | 100.00% | 2 | 100.00% | 
/* /dev/watchdog handling */
/**
 *      wdt_open - watchdog file_operations .open
 *      @inode: inode of the device
 *      @file: file handle to the device
 *
 *      The watchdog timer starts by opening the device.
 *
 *      Used within the file operation of the watchdog device.
 */
static int wdt_open(struct inode *inode, struct file *file)
{
	if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
		return -EBUSY;
	if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
		int ret;
		if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
			__module_get(THIS_MODULE);
		ret = wdt_start();
		if (ret) {
			clear_bit(WDTS_LOCKED, &wdt_status);
			clear_bit(WDTS_TIMER_RUN, &wdt_status);
			clear_bit(WDTS_DEV_OPEN, &wdt_status);
			return ret;
		}
	}
	return nonseekable_open(inode, file);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 74 | 66.07% | 1 | 50.00% | 
| Nat Gurumoorthy | 38 | 33.93% | 1 | 50.00% | 
| Total | 112 | 100.00% | 2 | 100.00% | 
/**
 *      wdt_release - watchdog file_operations .release
 *      @inode: inode of the device
 *      @file: file handle to the device
 *
 *      Closing the watchdog device either stops the watchdog timer
 *      or in the case, that nowayout is set or the magic character
 *      wasn't written, a critical warning about an running watchdog
 *      timer is given.
 *
 *      Used within the file operation of the watchdog device.
 */
static int wdt_release(struct inode *inode, struct file *file)
{
	if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
		if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
			int ret = wdt_stop();
			if (ret) {
				/*
                                 * Stop failed. Just keep the watchdog alive
                                 * and hope nothing bad happens.
                                 */
				set_bit(WDTS_EXPECTED, &wdt_status);
				wdt_keepalive();
				return ret;
			}
			clear_bit(WDTS_TIMER_RUN, &wdt_status);
		} else {
			wdt_keepalive();
			pr_crit("unexpected close, not stopping watchdog!\n");
		}
	}
	clear_bit(WDTS_DEV_OPEN, &wdt_status);
	return 0;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 71 | 73.20% | 1 | 33.33% | 
| Nat Gurumoorthy | 24 | 24.74% | 1 | 33.33% | 
| Joe Perches | 2 | 2.06% | 1 | 33.33% | 
| Total | 97 | 100.00% | 3 | 100.00% | 
/**
 *      wdt_write - watchdog file_operations .write
 *      @file: file handle to the watchdog
 *      @buf: buffer to write
 *      @count: count of bytes
 *      @ppos: pointer to the position to write. No seeks allowed
 *
 *      A write to a watchdog device is defined as a keepalive signal. Any
 *      write of data will do, as we don't define content meaning.
 *
 *      Used within the file operation of the watchdog device.
 */
static ssize_t wdt_write(struct file *file, const char __user *buf,
			    size_t count, loff_t *ppos)
{
	if (count) {
		clear_bit(WDTS_EXPECTED, &wdt_status);
		wdt_keepalive();
	}
	if (!nowayout) {
		size_t ofs;
	/* note: just in case someone wrote the magic character long ago */
		for (ofs = 0; ofs != count; ofs++) {
			char c;
			if (get_user(c, buf + ofs))
				return -EFAULT;
			if (c == WD_MAGIC)
				set_bit(WDTS_EXPECTED, &wdt_status);
		}
	}
	return count;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 102 | 100.00% | 1 | 100.00% | 
| Total | 102 | 100.00% | 1 | 100.00% | 
static const struct watchdog_info ident = {
	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
	.firmware_version =	1,
	.identity = WATCHDOG_NAME,
};
/**
 *      wdt_ioctl - watchdog file_operations .unlocked_ioctl
 *      @file: file handle to the device
 *      @cmd: watchdog command
 *      @arg: argument pointer
 *
 *      The watchdog API defines a common set of functions for all watchdogs
 *      according to their available features.
 *
 *      Used within the file operation of the watchdog device.
 */
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int rc = 0, status, new_options, new_timeout;
	union {
		struct watchdog_info __user *ident;
		int __user *i;
	} uarg;
	uarg.i = (int __user *)arg;
	switch (cmd) {
	case WDIOC_GETSUPPORT:
		return copy_to_user(uarg.ident,
				    &ident, sizeof(ident)) ? -EFAULT : 0;
	case WDIOC_GETSTATUS:
		rc = wdt_get_status(&status);
		if (rc)
			return rc;
		return put_user(status, uarg.i);
	case WDIOC_GETBOOTSTATUS:
		return put_user(0, uarg.i);
	case WDIOC_KEEPALIVE:
		wdt_keepalive();
		return 0;
	case WDIOC_SETOPTIONS:
		if (get_user(new_options, uarg.i))
			return -EFAULT;
		switch (new_options) {
		case WDIOS_DISABLECARD:
			if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
				rc = wdt_stop();
				if (rc)
					return rc;
			}
			clear_bit(WDTS_TIMER_RUN, &wdt_status);
			return 0;
		case WDIOS_ENABLECARD:
			if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
				rc = wdt_start();
				if (rc) {
					clear_bit(WDTS_TIMER_RUN, &wdt_status);
					return rc;
				}
			}
			return 0;
		default:
			return -EFAULT;
		}
	case WDIOC_SETTIMEOUT:
		if (get_user(new_timeout, uarg.i))
			return -EFAULT;
		rc = wdt_set_timeout(new_timeout);
	case WDIOC_GETTIMEOUT:
		if (put_user(timeout, uarg.i))
			return -EFAULT;
		return rc;
	default:
		return -ENOTTY;
	}
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 255 | 86.15% | 1 | 50.00% | 
| Nat Gurumoorthy | 41 | 13.85% | 1 | 50.00% | 
| Total | 296 | 100.00% | 2 | 100.00% | 
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
	void *unused)
{
	if (code == SYS_DOWN || code == SYS_HALT)
		wdt_stop();
	return NOTIFY_DONE;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 35 | 100.00% | 1 | 100.00% | 
| Total | 35 | 100.00% | 1 | 100.00% | 
static const struct file_operations wdt_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.write		= wdt_write,
	.unlocked_ioctl	= wdt_ioctl,
	.open		= wdt_open,
	.release	= wdt_release,
};
static struct miscdevice wdt_miscdev = {
	.minor		= WATCHDOG_MINOR,
	.name		= "watchdog",
	.fops		= &wdt_fops,
};
static struct notifier_block wdt_notifier = {
	.notifier_call = wdt_notify_sys,
};
static int __init it87_wdt_init(void)
{
	int rc = 0;
	int try_gameport = !nogameport;
	u8  chip_rev;
	int gp_rreq_fail = 0;
	wdt_status = 0;
	rc = superio_enter();
	if (rc)
		return rc;
	chip_type = superio_inw(CHIPID);
	chip_rev  = superio_inb(CHIPREV) & 0x0f;
	superio_exit();
	switch (chip_type) {
	case IT8702_ID:
		max_units = 255;
		break;
	case IT8712_ID:
		max_units = (chip_rev < 8) ? 255 : 65535;
		break;
	case IT8716_ID:
	case IT8726_ID:
		max_units = 65535;
		break;
	case IT8620_ID:
	case IT8718_ID:
	case IT8720_ID:
	case IT8721_ID:
	case IT8728_ID:
	case IT8783_ID:
		max_units = 65535;
		try_gameport = 0;
		break;
	case IT8705_ID:
		pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
		       chip_type, chip_rev);
		return -ENODEV;
	case NO_DEV_ID:
		pr_err("no device\n");
		return -ENODEV;
	default:
		pr_err("Unknown Chip found, Chip %04x Revision %04x\n",
		       chip_type, chip_rev);
		return -ENODEV;
	}
	rc = superio_enter();
	if (rc)
		return rc;
	superio_select(GPIO);
	superio_outb(WDT_TOV1, WDTCFG);
	superio_outb(0x00, WDTCTRL);
	/* First try to get Gameport support */
	if (try_gameport) {
		superio_select(GAMEPORT);
		base = superio_inw(BASEREG);
		if (!base) {
			base = GP_BASE_DEFAULT;
			superio_outw(base, BASEREG);
		}
		gpact = superio_inb(ACTREG);
		superio_outb(0x01, ACTREG);
		if (request_region(base, 1, WATCHDOG_NAME))
			set_bit(WDTS_USE_GP, &wdt_status);
		else
			gp_rreq_fail = 1;
	}
	/* If we haven't Gameport support, try to get CIR support */
	if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) {
		if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
			if (gp_rreq_fail)
				pr_err("I/O Address 0x%04x and 0x%04x already in use\n",
				       base, CIR_BASE);
			else
				pr_err("I/O Address 0x%04x already in use\n",
				       CIR_BASE);
			rc = -EIO;
			goto err_out;
		}
		base = CIR_BASE;
		superio_select(CIR);
		superio_outw(base, BASEREG);
		superio_outb(0x00, CIR_ILS);
		ciract = superio_inb(ACTREG);
		superio_outb(0x01, ACTREG);
		if (gp_rreq_fail) {
			superio_select(GAMEPORT);
			superio_outb(gpact, ACTREG);
		}
		set_bit(WDTS_USE_CIR, &wdt_status);
	}
	if (timeout < 1 || timeout > max_units * 60) {
		timeout = DEFAULT_TIMEOUT;
		pr_warn("Timeout value out of range, use default %d sec\n",
			DEFAULT_TIMEOUT);
	}
	if (timeout > max_units)
		timeout = wdt_round_time(timeout);
	rc = register_reboot_notifier(&wdt_notifier);
	if (rc) {
		pr_err("Cannot register reboot notifier (err=%d)\n", rc);
		goto err_out_region;
	}
	rc = misc_register(&wdt_miscdev);
	if (rc) {
		pr_err("Cannot register miscdev on minor=%d (err=%d)\n",
		       wdt_miscdev.minor, rc);
		goto err_out_reboot;
	}
	/* Initialize CIR to use it as keepalive source */
	if (test_bit(WDTS_USE_CIR, &wdt_status)) {
		outb(0x00, CIR_RCR(base));
		outb(0xc0, CIR_TCR1(base));
		outb(0x5c, CIR_TCR2(base));
		outb(0x10, CIR_IER(base));
		outb(0x00, CIR_BDHR(base));
		outb(0x01, CIR_BDLR(base));
		outb(0x09, CIR_IER(base));
	}
	pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n",
		chip_type, chip_rev, timeout,
		nowayout, testmode, exclusive, nogameport, nocir);
	superio_exit();
	return 0;
err_out_reboot:
	unregister_reboot_notifier(&wdt_notifier);
err_out_region:
	if (test_bit(WDTS_USE_GP, &wdt_status))
		release_region(base, 1);
	else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
		release_region(base, 8);
		superio_select(CIR);
		superio_outb(ciract, ACTREG);
	}
err_out:
	if (try_gameport) {
		superio_select(GAMEPORT);
		superio_outb(gpact, ACTREG);
	}
	superio_exit();
	return rc;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 506 | 73.65% | 1 | 10.00% | 
| Ondrej Zajicek | 69 | 10.04% | 2 | 20.00% | 
| Joe Perches | 39 | 5.68% | 1 | 10.00% | 
| Marc van der Wal | 31 | 4.51% | 1 | 10.00% | 
| Nat Gurumoorthy | 30 | 4.37% | 1 | 10.00% | 
| Paolo Teti | 3 | 0.44% | 1 | 10.00% | 
| Diego Elio 'Flameeyes' Pettenò | 3 | 0.44% | 1 | 10.00% | 
| Huaro Tomita | 3 | 0.44% | 1 | 10.00% | 
| Maciej S. Szmigiero | 3 | 0.44% | 1 | 10.00% | 
| Total | 687 | 100.00% | 10 | 100.00% | 
static void __exit it87_wdt_exit(void)
{
	if (superio_enter() == 0) {
		superio_select(GPIO);
		superio_outb(0x00, WDTCTRL);
		superio_outb(0x00, WDTCFG);
		superio_outb(0x00, WDTVALLSB);
		if (max_units > 255)
			superio_outb(0x00, WDTVALMSB);
		if (test_bit(WDTS_USE_GP, &wdt_status)) {
			superio_select(GAMEPORT);
			superio_outb(gpact, ACTREG);
		} else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
			superio_select(CIR);
			superio_outb(ciract, ACTREG);
		}
		superio_exit();
	}
	misc_deregister(&wdt_miscdev);
	unregister_reboot_notifier(&wdt_notifier);
	if (test_bit(WDTS_USE_GP, &wdt_status))
		release_region(base, 1);
	else if (test_bit(WDTS_USE_CIR, &wdt_status))
		release_region(base, 8);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 108 | 69.23% | 1 | 25.00% | 
| Marc van der Wal | 33 | 21.15% | 1 | 25.00% | 
| Ondrej Zajicek | 8 | 5.13% | 1 | 25.00% | 
| Nat Gurumoorthy | 7 | 4.49% | 1 | 25.00% | 
| Total | 156 | 100.00% | 4 | 100.00% | 
module_init(it87_wdt_init);
module_exit(it87_wdt_exit);
MODULE_AUTHOR("Oliver Schuster");
MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
MODULE_LICENSE("GPL");
Overall Contributors
| Person | Tokens | Prop | Commits | CommitProp | 
| Oliver Schuster | 2241 | 77.09% | 1 | 8.33% | 
| Nat Gurumoorthy | 220 | 7.57% | 1 | 8.33% | 
| Ondrej Zajicek | 211 | 7.26% | 2 | 16.67% | 
| Marc van der Wal | 140 | 4.82% | 1 | 8.33% | 
| Joe Perches | 48 | 1.65% | 1 | 8.33% | 
| Huaro Tomita | 21 | 0.72% | 1 | 8.33% | 
| Diego Elio 'Flameeyes' Pettenò | 8 | 0.28% | 1 | 8.33% | 
| Maciej S. Szmigiero | 8 | 0.28% | 1 | 8.33% | 
| Paolo Teti | 7 | 0.24% | 1 | 8.33% | 
| Wim Van Sebroeck | 3 | 0.10% | 2 | 16.67% | 
| Total | 2907 | 100.00% | 12 | 100.00% | 
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.