scx200_wdt.c revision e73a780272a46e897bd94a4870fd6b6a8655d2d4
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* drivers/char/watchdog/scx200_wdt.c
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   National Semiconductor SCx200 Watchdog support
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Some code taken from:
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   This program is free software; you can redistribute it and/or
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   modify it under the terms of the GNU General Public License as
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   published by the Free Software Foundation; either version 2 of the
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   License, or (at your option) any later version.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The author(s) of this software shall not be held liable for damages
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   of any nature resulting due to the use of this software. This
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   software is provided AS-IS with no warranties. */
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/notifier.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/reboot.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
286473d160b4aba8023bcf38519a5989694dfd51a7Jean Delvare#include <linux/ioport.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/scx200.h>
309b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox#include <linux/uaccess.h>
319b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox#include <linux/io.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NAME "scx200_wdt"
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int margin = 60;		/* in seconds */
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(margin, int, 0);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(margin, "Watchdog margin in seconds");
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
444bfdf37830111321e2cd1fe0102dd776ce93194dAndrey Paninstatic int nowayout = WATCHDOG_NOWAYOUT;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(nowayout, int, 0);
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 wdto_restart;
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char expect_close;
509b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic unsigned long open_lock;
519b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic DEFINE_SPINLOCK(scx_lock);
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Bits of the WDCNFG register */
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define W_ENABLE 0x00fa		/* Enable watchdog */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define W_DISABLE 0x0000	/* Disable watchdog */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The scaling factor for the timer, this depends on the value of W_ENABLE */
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define W_SCALE (32768/1024)
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_ping(void)
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
629b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_lock(&scx_lock);
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(wdto_restart, scx200_cb_base + SCx200_WDT_WDTO);
649b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_unlock(&scx_lock);
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_update_margin(void)
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wdto_restart = margin * W_SCALE;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_enable(void)
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n",
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       wdto_restart);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
789b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_lock(&scx_lock);
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(0, scx200_cb_base + SCx200_WDT_WDTO);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS);
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(W_ENABLE, scx200_cb_base + SCx200_WDT_WDCNFG);
829b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_unlock(&scx_lock);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scx200_wdt_ping();
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_disable(void)
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
919b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_lock(&scx_lock);
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(0, scx200_cb_base + SCx200_WDT_WDTO);
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(SCx200_WDT_WDSTS_WDOVF, scx200_cb_base + SCx200_WDT_WDSTS);
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(W_DISABLE, scx200_cb_base + SCx200_WDT_WDCNFG);
959b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_unlock(&scx_lock);
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int scx200_wdt_open(struct inode *inode, struct file *file)
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* only allow one at a time */
1019b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	if (test_and_set_bit(0, &open_lock))
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scx200_wdt_enable();
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return nonseekable_open(inode, file);
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int scx200_wdt_release(struct inode *inode, struct file *file)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1109b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	if (expect_close != 42)
111a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck		printk(KERN_WARNING NAME
112a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck			": watchdog device closed unexpectedly, "
113a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck			"will not disable the watchdog timer\n");
1149b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	else if (!nowayout)
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_disable();
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	expect_close = 0;
1179b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	clear_bit(0, &open_lock);
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int scx200_wdt_notify_sys(struct notifier_block *this,
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      unsigned long code, void *unused)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (code == SYS_HALT || code == SYS_POWER_OFF)
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nowayout)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			scx200_wdt_disable();
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NOTIFY_DONE;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1329b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic struct notifier_block scx200_wdt_notifier = {
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.notifier_call = scx200_wdt_notify_sys,
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t scx200_wdt_write(struct file *file, const char __user *data,
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     size_t len, loff_t *ppos)
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check for a magic close character */
1409b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	if (len) {
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t i;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_ping();
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		expect_close = 0;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < len; ++i) {
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			char c;
1487944d3a5a70ee5c1904ed1e8b1d71ff0af2854d9Wim Van Sebroeck			if (get_user(c, data + i))
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -EFAULT;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (c == 'V')
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				expect_close = 42;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return len;
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1609b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic long scx200_wdt_ioctl(struct file *file, unsigned int cmd,
1619b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox							unsigned long arg)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int __user *p = argp;
1659b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	static const struct watchdog_info ident = {
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.identity = "NatSemi SCx200 Watchdog",
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.firmware_version = 1,
168e73a780272a46e897bd94a4870fd6b6a8655d2d4Wim Van Sebroeck		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
169e73a780272a46e897bd94a4870fd6b6a8655d2d4Wim Van Sebroeck						WDIOF_MAGICCLOSE,
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int new_margin;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETSUPPORT:
1759b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox		if (copy_to_user(argp, &ident, sizeof(ident)))
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETSTATUS:
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETBOOTSTATUS:
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(0, p))
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_KEEPALIVE:
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_ping();
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_SETTIMEOUT:
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(new_margin, p))
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (new_margin < 1)
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		margin = new_margin;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_update_margin();
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_ping();
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETTIMEOUT:
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(margin, p))
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1980c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	default:
1990c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return -ENOTTY;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20362322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations scx200_wdt_fops = {
2049b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.owner = THIS_MODULE,
2059b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.llseek = no_llseek,
2069b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.write = scx200_wdt_write,
2079b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.unlocked_ioctl = scx200_wdt_ioctl,
2089b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.open = scx200_wdt_open,
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = scx200_wdt_release,
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice scx200_wdt_miscdev = {
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor = WATCHDOG_MINOR,
2149b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.name = "watchdog",
2159b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.fops = &scx200_wdt_fops,
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init scx200_wdt_init(void)
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int r;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check that we have found the configuration block */
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!scx200_cb_present())
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET,
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    SCx200_WDT_SIZE,
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    "NatSemi SCx200 Watchdog")) {
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scx200_wdt_update_margin();
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scx200_wdt_disable();
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
238c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck	r = register_reboot_notifier(&scx200_wdt_notifier);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (r) {
240c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck		printk(KERN_ERR NAME ": unable to register reboot notifier");
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(scx200_cb_base + SCx200_WDT_OFFSET,
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				SCx200_WDT_SIZE);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return r;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
246c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck	r = misc_register(&scx200_wdt_miscdev);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (r) {
248c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck		unregister_reboot_notifier(&scx200_wdt_notifier);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(scx200_cb_base + SCx200_WDT_OFFSET,
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				SCx200_WDT_SIZE);
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return r;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit scx200_wdt_cleanup(void)
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&scx200_wdt_miscdev);
260c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck	unregister_reboot_notifier(&scx200_wdt_notifier);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(scx200_cb_base + SCx200_WDT_OFFSET,
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       SCx200_WDT_SIZE);
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(scx200_wdt_init);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(scx200_wdt_cleanup);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Local variables:
2709b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
2719b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	c-basic-offset: 8
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    End:
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
274