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
2027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/watchdog.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/notifier.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/reboot.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h>
306473d160b4aba8023bcf38519a5989694dfd51a7Jean Delvare#include <linux/ioport.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/scx200.h>
329b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox#include <linux/uaccess.h>
339b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox#include <linux/io.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3527c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define DEBUG
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int margin = 60;		/* in seconds */
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(margin, int, 0);
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(margin, "Watchdog margin in seconds");
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4586a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
4686a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic u16 wdto_restart;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char expect_close;
519b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic unsigned long open_lock;
529b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic DEFINE_SPINLOCK(scx_lock);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Bits of the WDCNFG register */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define W_ENABLE 0x00fa		/* Enable watchdog */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define W_DISABLE 0x0000	/* Disable watchdog */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The scaling factor for the timer, this depends on the value of W_ENABLE */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define W_SCALE (32768/1024)
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_ping(void)
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
639b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_lock(&scx_lock);
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(wdto_restart, scx200_cb_base + SCx200_WDT_WDTO);
659b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	spin_unlock(&scx_lock);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_update_margin(void)
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("timer margin %d seconds\n", margin);
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wdto_restart = margin * W_SCALE;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void scx200_wdt_enable(void)
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_debug("enabling watchdog timer, wdto_restart = %d\n", 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{
8927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_debug("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)
11127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
1129b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	else if (!nowayout)
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_disable();
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	expect_close = 0;
1159b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	clear_bit(0, &open_lock);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int scx200_wdt_notify_sys(struct notifier_block *this,
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      unsigned long code, void *unused)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (code == SYS_HALT || code == SYS_POWER_OFF)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!nowayout)
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			scx200_wdt_disable();
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NOTIFY_DONE;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1309b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic struct notifier_block scx200_wdt_notifier = {
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.notifier_call = scx200_wdt_notify_sys,
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t scx200_wdt_write(struct file *file, const char __user *data,
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     size_t len, loff_t *ppos)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check for a magic close character */
1389b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	if (len) {
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t i;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_ping();
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		expect_close = 0;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < len; ++i) {
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			char c;
1467944d3a5a70ee5c1904ed1e8b1d71ff0af2854d9Wim Van Sebroeck			if (get_user(c, data + i))
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -EFAULT;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (c == 'V')
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				expect_close = 42;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return len;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1589b748ed03cabf533a815e5ffc50108a21c98e40cAlan Coxstatic long scx200_wdt_ioctl(struct file *file, unsigned int cmd,
1599b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox							unsigned long arg)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __user *argp = (void __user *)arg;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int __user *p = argp;
1639b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	static const struct watchdog_info ident = {
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.identity = "NatSemi SCx200 Watchdog",
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.firmware_version = 1,
166e73a780272a46e897bd94a4870fd6b6a8655d2d4Wim Van Sebroeck		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
167e73a780272a46e897bd94a4870fd6b6a8655d2d4Wim Van Sebroeck						WDIOF_MAGICCLOSE,
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int new_margin;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETSUPPORT:
1739b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox		if (copy_to_user(argp, &ident, sizeof(ident)))
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETSTATUS:
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETBOOTSTATUS:
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(0, p))
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_KEEPALIVE:
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_ping();
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_SETTIMEOUT:
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (get_user(new_margin, p))
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (new_margin < 1)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		margin = new_margin;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_update_margin();
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		scx200_wdt_ping();
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case WDIOC_GETTIMEOUT:
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (put_user(margin, p))
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EFAULT;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1960c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	default:
1970c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		return -ENOTTY;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20162322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations scx200_wdt_fops = {
2029b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.owner = THIS_MODULE,
2039b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.llseek = no_llseek,
2049b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.write = scx200_wdt_write,
2059b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.unlocked_ioctl = scx200_wdt_ioctl,
2069b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.open = scx200_wdt_open,
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = scx200_wdt_release,
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice scx200_wdt_miscdev = {
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.minor = WATCHDOG_MINOR,
2129b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.name = "watchdog",
2139b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	.fops = &scx200_wdt_fops,
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init scx200_wdt_init(void)
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int r;
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_debug("NatSemi SCx200 Watchdog Driver\n");
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check that we have found the configuration block */
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!scx200_cb_present())
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(scx200_cb_base + SCx200_WDT_OFFSET,
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    SCx200_WDT_SIZE,
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			    "NatSemi SCx200 Watchdog")) {
22927c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_warn("watchdog I/O region busy\n");
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scx200_wdt_update_margin();
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	scx200_wdt_disable();
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
236c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck	r = register_reboot_notifier(&scx200_wdt_notifier);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (r) {
23827c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_err("unable to register reboot notifier\n");
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(scx200_cb_base + SCx200_WDT_OFFSET,
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				SCx200_WDT_SIZE);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return r;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
244c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck	r = misc_register(&scx200_wdt_miscdev);
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (r) {
246c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck		unregister_reboot_notifier(&scx200_wdt_notifier);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(scx200_cb_base + SCx200_WDT_OFFSET,
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				SCx200_WDT_SIZE);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return r;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit scx200_wdt_cleanup(void)
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	misc_deregister(&scx200_wdt_miscdev);
258c6cb13aead3a3cf5bd3e2cfa945602d5cd7825cdWim Van Sebroeck	unregister_reboot_notifier(&scx200_wdt_notifier);
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(scx200_cb_base + SCx200_WDT_OFFSET,
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       SCx200_WDT_SIZE);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(scx200_wdt_init);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(scx200_wdt_cleanup);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Local variables:
2689b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
2699b748ed03cabf533a815e5ffc50108a21c98e40cAlan Cox	c-basic-offset: 8
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    End:
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
272