The module is called machzwd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+config W83877F_WDT
+ tristate "W83877F Computer Watchdog"
+ depends on WATCHDOG
+
+config SC520_WDT
+ tristate "AMD Elan SC520 processor Watchdog"
+ depends on WATCHDOG
+
+config ALIM7101_WDT
+ tristate "ALi M7101 PMU Computer Watchdog"
+ depends on WATCHDOG
+
+config SC1200_WDT
+ tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
+ depends on WATCHDOG
+
+config WAFER_WDT
+ tristate "ICP Wafer 5823 Single Board Computer Watchdog"
+ depends on WATCHDOG
+
endmenu
obj-$(CONFIG_SH_WDT) += shwdt.o
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
+obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
+obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
+obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
+obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
--- /dev/null
+/*
+ * ALi M7101 PMU Computer Watchdog Timer driver for Linux 2.4.x
+ *
+ * Based on w83877f_wdt.c by Scott Jennings <management@oro.net>
+ * and the Cobalt kernel WDT timer driver by Tim Hockin
+ * <thockin@cobaltnet.com>
+ *
+ * (c)2002 Steve Hill <steve@navaho.co.uk>
+ *
+ * Theory of operation:
+ * A Watchdog Timer (WDT) is a hardware circuit that can
+ * reset the computer system in case of a software fault.
+ * You probably knew that already.
+ *
+ * Usually a userspace daemon will notify the kernel WDT driver
+ * via the /proc/watchdog special device file that userspace is
+ * still alive, at regular intervals. When such a notification
+ * occurs, the driver will usually tell the hardware watchdog
+ * that everything is in order, and that the watchdog should wait
+ * for yet another little while to reset the system.
+ * If userspace fails (RAM error, kernel bug, whatever), the
+ * notifications cease to occur, and the hardware watchdog will
+ * reset the system (causing a reboot) after the timeout occurs.
+ *
+ * This WDT driver is different from most other Linux WDT
+ * drivers in that the driver will ping the watchdog by itself,
+ * because this particular WDT has a very short timeout (1.6
+ * seconds) and it would be insane to count on any userspace
+ * daemon always getting scheduled within that time frame.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#define OUR_NAME "alim7101_wdt"
+
+#define WDT_ENABLE 0x9C
+#define WDT_DISABLE 0x8C
+
+#define ALI_7101_WDT 0x92
+#define ALI_WDT_ARM 0x01
+
+/*
+ * We're going to use a 1 second timeout.
+ * If we reset the watchdog every ~250ms we should be safe. */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ */
+
+#define WDT_HEARTBEAT (HZ * 30)
+
+static void wdt_timer_ping(unsigned long);
+static struct timer_list timer;
+static unsigned long next_heartbeat;
+static unsigned long wdt_is_open;
+static int wdt_expect_close;
+static struct pci_dev *alim7101_pmu;
+
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ char tmp;
+
+ if(time_before(jiffies, next_heartbeat))
+ {
+ /* Ping the WDT (this is actually a disarm/arm sequence) */
+ pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
+ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
+ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM));
+ } else {
+ printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
+ }
+ /* Re-set the timer interval */
+ timer.expires = jiffies + WDT_INTERVAL;
+ add_timer(&timer);
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_change(int writeval)
+{
+ char tmp;
+
+ pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
+ if (writeval == WDT_ENABLE)
+ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM));
+ else
+ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
+}
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+
+ /* We must enable before we kick off the timer in case the timer
+ occurs as we ping it */
+
+ wdt_change(WDT_ENABLE);
+
+ /* Start the timer */
+ timer.expires = jiffies + WDT_INTERVAL;
+ add_timer(&timer);
+
+
+ printk(OUR_NAME ": Watchdog timer is now enabled.\n");
+}
+
+static void wdt_turnoff(void)
+{
+ /* Stop the timer */
+ del_timer_sync(&timer);
+ wdt_change(WDT_DISABLE);
+ printk(OUR_NAME ": Watchdog timer is now disabled...\n");
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
+{
+ /* We can't seek */
+ if(ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /* See if we got the magic character */
+ if(count)
+ {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ wdt_expect_close = 0;
+
+ /* now scan */
+ for(ofs = 0; ofs != count; ofs++)
+ {
+ char c;
+ if(get_user(c, buf+ofs))
+ return -EFAULT;
+ if(c == 'V')
+ wdt_expect_close = 1;
+ }
+ /* someone wrote to us, we should restart timer */
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+ return 1;
+ };
+ return 0;
+}
+
+static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
+{
+ /* No can do */
+ return -EINVAL;
+}
+
+static int fop_open(struct inode * inode, struct file * file)
+{
+ /* Just in case we're already talking to someone... */
+ if(test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /* Good, fire up the show */
+ wdt_startup();
+ return 0;
+}
+
+static int fop_close(struct inode * inode, struct file * file)
+{
+#ifdef CONFIG_WDT_NOWAYOUT
+ if(wdt_expect_close)
+ wdt_turnoff();
+ else {
+ printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
+ }
+#else
+ wdt_turnoff();
+#endif
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info ident=
+ {
+ 0,
+ 1,
+ "ALiM7101"
+ };
+
+ switch(cmd)
+ {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
+ case WDIOC_KEEPALIVE:
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static struct file_operations wdt_fops = {
+ .owner= THIS_MODULE,
+ .llseek= no_llseek,
+ .read= fop_read,
+ .write= fop_write,
+ .open= fop_open,
+ .release= fop_close,
+ .ioctl= fop_ioctl
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor=WATCHDOG_MINOR,
+ .name="watchdog",
+ .fops=&wdt_fops
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
+{
+ if (code==SYS_DOWN || code==SYS_HALT) wdt_turnoff();
+ if (code==SYS_RESTART) {
+ /*
+ * Cobalt devices have no way of rebooting themselves other than
+ * getting the watchdog to pull reset, so we restart the watchdog on
+ * reboot with no heartbeat
+ */
+ wdt_change(WDT_ENABLE);
+ printk(OUR_NAME ": Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n");
+ };
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier=
+{
+ wdt_notify_sys,
+ 0,
+ 0
+};
+
+static void __exit alim7101_wdt_unload(void)
+{
+ wdt_turnoff();
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+}
+
+static int __init alim7101_wdt_init(void)
+{
+ int rc = -EBUSY;
+ struct pci_dev *ali1543_south;
+ char tmp;
+
+ printk(KERN_INFO OUR_NAME ": Steve Hill <steve@navaho.co.uk>.\n");
+ alim7101_pmu = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,NULL);
+ if (!alim7101_pmu) {
+ printk(KERN_INFO OUR_NAME ": ALi M7101 PMU not present - WDT not set\n");
+ return -EBUSY;
+ };
+
+ /* Set the WDT in the PMU to 1 second */
+ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02);
+
+ ali1543_south = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
+ if (!ali1543_south) {
+ printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge not present - WDT not set\n");
+ return -EBUSY;
+ };
+ pci_read_config_byte(ali1543_south, 0x5e, &tmp);
+ if ((tmp & 0x1e) != 0x12) {
+ printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n");
+ return -EBUSY;
+ };
+
+ init_timer(&timer);
+ timer.function = wdt_timer_ping;
+ timer.data = 1;
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc)
+ return rc;
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ misc_deregister(&wdt_miscdev);
+ return rc;
+ };
+
+ printk(KERN_INFO OUR_NAME ": WDT driver for ALi M7101 initialised.\n");
+ return 0;
+}
+
+module_init(alim7101_wdt_init);
+module_exit(alim7101_wdt_unload);
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Steve Hill");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * IndyDog 0.2 A Hardware Watchdog Device for SGI IP22
+ *
+ * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved.
+ *
+ * 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.
+ *
+ * based on softdog.c by Alan Cox <alan@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/sgi/sgimc.h>
+
+static unsigned long indydog_alive;
+static struct sgimc_misc_ctrl *mcmisc_regs;
+
+static void indydog_ping()
+{
+ mcmisc_regs->watchdogt = 0;
+}
+
+
+/*
+ * Allow only one person to hold it open
+ */
+
+static int indydog_open(struct inode *inode, struct file *file)
+{
+ u32 mc_ctrl0;
+
+ if( test_and_set_bit(0,&indydog_alive) )
+ return -EBUSY;
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+ MOD_INC_USE_COUNT;
+#endif
+ /*
+ * Activate timer
+ */
+ mcmisc_regs = (struct sgimc_misc_ctrl *)(KSEG1+0x1fa00000);
+
+ mc_ctrl0 = mcmisc_regs->cpuctrl0 | SGIMC_CCTRL0_WDOG;
+ mcmisc_regs->cpuctrl0 = mc_ctrl0;
+ indydog_ping();
+
+ printk("Started watchdog timer.\n");
+ return 0;
+}
+
+static int indydog_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we defined ...NOWAYOUT
+ */
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ {
+ u32 mc_ctrl0 = mcmisc_regs->cpuctrl0;
+ mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
+ mcmisc_regs->cpuctrl0 = mc_ctrl0;
+ printk("Stopped watchdog timer.\n");
+ }
+#endif
+ clear_bit(0,&indydog_alive);
+ return 0;
+}
+
+static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ /* Can't seek (pwrite) on this device */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /*
+ * Refresh the timer.
+ */
+ if(len) {
+ indydog_ping();
+ return 1;
+ }
+ return 0;
+}
+
+static int indydog_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info ident = {
+ identity: "Hardware Watchdog for SGI IP22",
+ };
+ switch (cmd) {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
+ return -EFAULT;
+ return 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0,(int *)arg);
+ case WDIOC_KEEPALIVE:
+ indydog_ping();
+ return 0;
+ }
+}
+
+static struct file_operations indydog_fops = {
+ .owner = THIS_MODULE,
+ .write = indydog_write,
+ .ioctl = indydog_ioctl,
+ .open = indydog_open,
+ .release= indydog_release,
+};
+
+static struct miscdevice indydog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &indydog_fops,
+};
+
+static const char banner[] __initdata = KERN_INFO "Hardware Watchdog Timer for SGI IP22: 0.2\n";
+
+static int __init watchdog_init(void)
+{
+ int ret;
+
+ ret = misc_register(&indydog_miscdev);
+
+ if (ret)
+ return ret;
+
+ printk(banner);
+
+ return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+ misc_deregister(&indydog_miscdev);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
+ * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,
+ * All Rights Reserved.
+ * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.
+ *
+ * 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.
+ *
+ * The author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ *
+ * Changelog:
+ * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware.
+ * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox.
+ * 20020222 Zwane Mwaikambo Added probing.
+ * 20020225 Zwane Mwaikambo Added ISAPNP support.
+ * 20020412 Rob Radez Broke out start/stop functions
+ * <rob@osinvestor.com> Return proper status instead of temperature warning
+ * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls
+ * Fix CONFIG_WATCHDOG_NOWAYOUT
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/isapnp.h>
+
+#define SC1200_MODULE_VER "build 20020303"
+#define SC1200_MODULE_NAME "sc1200wdt"
+#define PFX SC1200_MODULE_NAME ": "
+
+#define MAX_TIMEOUT 255 /* 255 minutes */
+#define PMIR (io) /* Power Management Index Register */
+#define PMDR (io+1) /* Power Management Data Register */
+
+/* Data Register indexes */
+#define FER1 0x00 /* Function enable register 1 */
+#define FER2 0x01 /* Function enable register 2 */
+#define PMC1 0x02 /* Power Management Ctrl 1 */
+#define PMC2 0x03 /* Power Management Ctrl 2 */
+#define PMC3 0x04 /* Power Management Ctrl 3 */
+#define WDTO 0x05 /* Watchdog timeout register */
+#define WDCF 0x06 /* Watchdog config register */
+#define WDST 0x07 /* Watchdog status register */
+
+/* WDCF bitfields - which devices assert WDO */
+#define KBC_IRQ 0x01 /* Keyboard Controller */
+#define MSE_IRQ 0x02 /* Mouse */
+#define UART1_IRQ 0x03 /* Serial0 */
+#define UART2_IRQ 0x04 /* Serial1 */
+/* 5 -7 are reserved */
+
+static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER;
+static int timeout = 1;
+static int io = -1;
+static int io_len = 2; /* for non plug and play */
+struct semaphore open_sem;
+static char expect_close;
+spinlock_t sc1200wdt_lock; /* io port access serialisation */
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+static int isapnp = 1;
+static struct pci_dev *wdt_dev;
+
+MODULE_PARM(isapnp, "i");
+MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled");
+#endif
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "io port");
+MODULE_PARM(timeout, "i");
+MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
+
+
+
+/* Read from Data Register */
+static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data)
+{
+ spin_lock(&sc1200wdt_lock);
+ outb_p(index, PMIR);
+ *data = inb(PMDR);
+ spin_unlock(&sc1200wdt_lock);
+}
+
+
+/* Write to Data Register */
+static inline void sc1200wdt_write_data(unsigned char index, unsigned char data)
+{
+ spin_lock(&sc1200wdt_lock);
+ outb_p(index, PMIR);
+ outb(data, PMDR);
+ spin_unlock(&sc1200wdt_lock);
+}
+
+
+static void sc1200wdt_start(void)
+{
+ unsigned char reg;
+
+ sc1200wdt_read_data(WDCF, ®);
+ /* assert WDO when any of the following interrupts are triggered too */
+ reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
+ sc1200wdt_write_data(WDCF, reg);
+ /* set the timeout and get the ball rolling */
+ sc1200wdt_write_data(WDTO, timeout);
+}
+
+
+static void sc1200wdt_stop(void)
+{
+ sc1200wdt_write_data(WDTO, 0);
+}
+
+
+/* This returns the status of the WDO signal, inactive high. */
+static inline int sc1200wdt_status(void)
+{
+ unsigned char ret;
+
+ sc1200wdt_read_data(WDST, &ret);
+ /* If the bit is inactive, the watchdog is enabled, so return
+ * KEEPALIVEPING which is a bit of a kludge because there's nothing
+ * else for enabled/disabled status
+ */
+ return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */
+}
+
+
+static int sc1200wdt_open(struct inode *inode, struct file *file)
+{
+ /* allow one at a time */
+ if (down_trylock(&open_sem))
+ return -EBUSY;
+
+ if (timeout > MAX_TIMEOUT)
+ timeout = MAX_TIMEOUT;
+
+ sc1200wdt_start();
+ printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout);
+
+ return 0;
+}
+
+
+static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int new_timeout;
+ static struct watchdog_info ident = {
+ options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+ firmware_version: 0,
+ identity: "PC87307/PC97307"
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOTTY; /* Keep Pavel Machek amused ;) */
+
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident))
+ return -EFAULT;
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ return put_user(sc1200wdt_status(), (int *)arg);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int *)arg);
+
+ case WDIOC_KEEPALIVE:
+ sc1200wdt_write_data(WDTO, timeout);
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, (int *)arg))
+ return -EFAULT;
+
+ /* the API states this is given in secs */
+ new_timeout /= 60;
+ if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
+ return -EINVAL;
+
+ timeout = new_timeout;
+ sc1200wdt_write_data(WDTO, timeout);
+ /* fall through and return the new timeout */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout * 60, (int *)arg);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, (int *)arg))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ sc1200wdt_stop();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ sc1200wdt_start();
+ retval = 0;
+ }
+
+ return retval;
+ }
+ }
+}
+
+
+static int sc1200wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ sc1200wdt_stop();
+ printk(KERN_INFO PFX "Watchdog disabled\n");
+ } else {
+ sc1200wdt_write_data(WDTO, timeout);
+ printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout);
+ }
+ up(&open_sem);
+ expect_close = 0;
+
+ return 0;
+}
+
+
+static ssize_t sc1200wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ if (len) {
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != len; i++)
+ {
+ char c;
+ if(get_user(c, data+i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+#endif
+ sc1200wdt_write_data(WDTO, timeout);
+ return len;
+ }
+
+ return 0;
+}
+
+
+static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ sc1200wdt_stop();
+
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block sc1200wdt_notifier =
+{
+ notifier_call: sc1200wdt_notify_sys
+};
+
+static struct file_operations sc1200wdt_fops =
+{
+ owner: THIS_MODULE,
+ write: sc1200wdt_write,
+ ioctl: sc1200wdt_ioctl,
+ open: sc1200wdt_open,
+ release: sc1200wdt_release
+};
+
+static struct miscdevice sc1200wdt_miscdev =
+{
+ minor: WATCHDOG_MINOR,
+ name: "watchdog",
+ fops: &sc1200wdt_fops,
+};
+
+
+static int __init sc1200wdt_probe(void)
+{
+ /* The probe works by reading the PMC3 register's default value of 0x0e
+ * there is one caveat, if the device disables the parallel port or any
+ * of the UARTs we won't be able to detect it.
+ * Nb. This could be done with accuracy by reading the SID registers, but
+ * we don't have access to those io regions.
+ */
+
+ unsigned char reg;
+
+ sc1200wdt_read_data(PMC3, ®);
+ reg &= 0x0f; /* we don't want the UART busy bits */
+ return (reg == 0x0e) ? 0 : -ENODEV;
+}
+
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+
+static int __init sc1200wdt_isapnp_probe(void)
+{
+ int ret;
+
+ /* The WDT is logical device 8 on the main device */
+ wdt_dev = isapnp_find_dev(NULL, ISAPNP_VENDOR('N','S','C'), ISAPNP_FUNCTION(0x08), NULL);
+ if (!wdt_dev)
+ return -ENODEV;
+
+ if (wdt_dev->prepare(wdt_dev) < 0) {
+ printk(KERN_ERR PFX "ISA PnP found device that could not be autoconfigured\n");
+ return -EAGAIN;
+ }
+
+ if (!(pci_resource_flags(wdt_dev, 0) & IORESOURCE_IO)) {
+ printk(KERN_ERR PFX "ISA PnP could not find io ports\n");
+ return -ENODEV;
+ }
+
+ ret = wdt_dev->activate(wdt_dev);
+ if (ret && (ret != -EBUSY))
+ return -ENOMEM;
+
+ /* io port resource overriding support? */
+ io = pci_resource_start(wdt_dev, 0);
+ io_len = pci_resource_len(wdt_dev, 0);
+
+ printk(KERN_DEBUG PFX "ISA PnP found device at io port %#x/%d\n", io, io_len);
+ return 0;
+}
+
+#endif /* CONFIG_ISAPNP */
+
+
+static int __init sc1200wdt_init(void)
+{
+ int ret;
+
+ printk(banner);
+
+ spin_lock_init(&sc1200wdt_lock);
+ sema_init(&open_sem, 1);
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if (isapnp) {
+ ret = sc1200wdt_isapnp_probe();
+ if (ret)
+ goto out_clean;
+ }
+#endif
+
+ if (io == -1) {
+ printk(KERN_ERR PFX "io parameter must be specified\n");
+ ret = -EINVAL;
+ goto out_clean;
+ }
+
+ if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
+ printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
+ ret = -EBUSY;
+ goto out_pnp;
+ }
+
+ ret = sc1200wdt_probe();
+ if (ret)
+ goto out_io;
+
+ ret = register_reboot_notifier(&sc1200wdt_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret);
+ goto out_io;
+ }
+
+ ret = misc_register(&sc1200wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
+ goto out_rbt;
+ }
+
+ /* ret = 0 */
+
+out_clean:
+ return ret;
+
+out_rbt:
+ unregister_reboot_notifier(&sc1200wdt_notifier);
+
+out_io:
+ release_region(io, io_len);
+
+out_pnp:
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if (isapnp && wdt_dev)
+ wdt_dev->deactivate(wdt_dev);
+#endif
+ goto out_clean;
+}
+
+
+static void __exit sc1200wdt_exit(void)
+{
+ misc_deregister(&sc1200wdt_miscdev);
+ unregister_reboot_notifier(&sc1200wdt_notifier);
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if(isapnp && wdt_dev)
+ wdt_dev->deactivate(wdt_dev);
+#endif
+
+ release_region(io, io_len);
+}
+
+
+#ifndef MODULE
+static int __init sc1200wdt_setup(char *str)
+{
+ int ints[4];
+
+ str = get_options (str, ARRAY_SIZE(ints), ints);
+
+ if (ints[0] > 0) {
+ io = ints[1];
+ if (ints[0] > 1)
+ timeout = ints[2];
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if (ints[0] > 2)
+ isapnp = ints[3];
+#endif
+ }
+
+ return 1;
+}
+
+__setup("sc1200wdt=", sc1200wdt_setup);
+#endif /* MODULE */
+
+
+module_init(sc1200wdt_init);
+module_exit(sc1200wdt_exit);
+
+MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
+MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
--- /dev/null
+/*
+ * AMD Elan SC520 processor Watchdog Timer driver for Linux 2.4.x
+ *
+ * Based on acquirewdt.c by Alan Cox,
+ * and sbc60xxwdt.c by Jakob Oestergaard <jakob@ostenfeld.dk>
+ *
+ * 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.
+ *
+ * The authors do NOT admit liability nor provide warranty for
+ * any of this software. This material is provided "AS-IS" in
+ * the hope that it may be useful for others.
+ *
+ * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
+ * 9/27 - 2001 [Initial release]
+ *
+ * Additional fixes Alan Cox
+ * - Fixed formatting
+ * - Removed debug printks
+ * - Fixed SMP built kernel deadlock
+ * - Switched to private locks not lock_kernel
+ * - Used ioremap/writew/readw
+ * - Added NOWAYOUT support
+ *
+ * Theory of operation:
+ * A Watchdog Timer (WDT) is a hardware circuit that can
+ * reset the computer system in case of a software fault.
+ * You probably knew that already.
+ *
+ * Usually a userspace daemon will notify the kernel WDT driver
+ * via the /proc/watchdog special device file that userspace is
+ * still alive, at regular intervals. When such a notification
+ * occurs, the driver will usually tell the hardware watchdog
+ * that everything is in order, and that the watchdog should wait
+ * for yet another little while to reset the system.
+ * If userspace fails (RAM error, kernel bug, whatever), the
+ * notifications cease to occur, and the hardware watchdog will
+ * reset the system (causing a reboot) after the timeout occurs.
+ *
+ * This WDT driver is different from most other Linux WDT
+ * drivers in that the driver will ping the watchdog by itself,
+ * because this particular WDT has a very short timeout (1.6
+ * seconds) and it would be insane to count on any userspace
+ * daemon always getting scheduled within that time frame.
+ *
+ * This driver uses memory mapped IO, and spinlock.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+
+/*
+ * The SC520 can timeout anywhere from 492us to 32.21s.
+ * If we reset the watchdog every ~250ms we should be safe.
+ */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ */
+
+#define WDT_HEARTBEAT (HZ * 30)
+
+/*
+ * AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
+ *
+ * 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s
+ * 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s
+ */
+
+#define TIMEOUT_EXPONENT ( 1 << 3 ) /* 0x08 = 2.01s */
+
+/* #define MMCR_BASE_DEFAULT 0xfffef000 */
+#define MMCR_BASE_DEFAULT ((__u16 *)0xffffe)
+#define OFFS_WDTMRCTL ((unsigned int)0xcb0)
+#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */
+#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */
+
+#define OUR_NAME "sc520_wdt"
+
+#define WRT_DOG(data) *wdtmrctl=data
+
+static __u16 *wdtmrctl;
+
+static void wdt_timer_ping(unsigned long);
+static struct timer_list timer;
+static unsigned long next_heartbeat;
+static unsigned long wdt_is_open;
+static int wdt_expect_close;
+
+static spinlock_t wdt_spinlock;
+/*
+ * Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+ /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+ * we agree to ping the WDT
+ */
+ if(time_before(jiffies, next_heartbeat))
+ {
+ /* Ping the WDT */
+ spin_lock(&wdt_spinlock);
+ writew(0xAAAA, wdtmrctl);
+ writew(0x5555, wdtmrctl);
+ spin_unlock(&wdt_spinlock);
+
+ /* Re-set the timer interval */
+ timer.expires = jiffies + WDT_INTERVAL;
+ add_timer(&timer);
+ } else {
+ printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
+ }
+}
+
+/*
+ * Utility routines
+ */
+
+static void wdt_config(int writeval)
+{
+ __u16 dummy;
+ unsigned long flags;
+
+ /* buy some time (ping) */
+ spin_lock_irqsave(&wdt_spinlock, flags);
+ dummy=readw(wdtmrctl); /* ensure write synchronization */
+ writew(0xAAAA, wdtmrctl);
+ writew(0x5555, wdtmrctl);
+ /* make WDT configuration register writable one time */
+ writew(0x3333, wdtmrctl);
+ writew(0xCCCC, wdtmrctl);
+ /* write WDT configuration register */
+ writew(writeval, wdtmrctl);
+ spin_unlock_irqrestore(&wdt_spinlock, flags);
+}
+
+static void wdt_startup(void)
+{
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+
+ /* Start the timer */
+ timer.expires = jiffies + WDT_INTERVAL;
+ add_timer(&timer);
+
+ wdt_config(WDT_ENB | WDT_WRST_ENB | TIMEOUT_EXPONENT);
+ printk(OUR_NAME ": Watchdog timer is now enabled.\n");
+}
+
+static void wdt_turnoff(void)
+{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ /* Stop the timer */
+ del_timer(&timer);
+ wdt_config(0);
+ printk(OUR_NAME ": Watchdog timer is now disabled...\n");
+#endif
+}
+
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
+{
+ /* We can't seek */
+ if(ppos != &file->f_pos)
+ return -ESPIPE;
+
+ /* See if we got the magic character */
+ if(count)
+ {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character
+ * five months ago... */
+ wdt_expect_close = 0;
+
+ /* now scan */
+ for(ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if(c == 'V')
+ wdt_expect_close = 1;
+ }
+
+ /* Well, anyhow someone wrote to us, we should return that favour */
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+ return 1;
+ }
+ return 0;
+}
+
+static int fop_open(struct inode * inode, struct file * file)
+{
+ switch(minor(inode->i_rdev))
+ {
+ case WATCHDOG_MINOR:
+ /* Just in case we're already talking to someone... */
+ if(test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /* Good, fire up the show */
+ wdt_startup();
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+ default:
+ return -ENODEV;
+ }
+}
+
+static int fop_close(struct inode * inode, struct file * file)
+{
+ if(minor(inode->i_rdev) == WATCHDOG_MINOR)
+ {
+ if(wdt_expect_close)
+ wdt_turnoff();
+ else {
+ del_timer(&timer);
+ printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
+ }
+ }
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+static long long fop_llseek(struct file *file, long long offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ static struct watchdog_info ident=
+ {
+ 0,
+ 1,
+ "SC520"
+ };
+
+ switch(cmd)
+ {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
+ case WDIOC_KEEPALIVE:
+ next_heartbeat = jiffies + WDT_HEARTBEAT;
+ return 0;
+ }
+}
+
+static struct file_operations wdt_fops = {
+ owner: THIS_MODULE,
+ llseek: fop_llseek,
+ write: fop_write,
+ open: fop_open,
+ release: fop_close,
+ ioctl: fop_ioctl
+};
+
+static struct miscdevice wdt_miscdev = {
+ WATCHDOG_MINOR,
+ "watchdog",
+ &wdt_fops
+};
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if(code==SYS_DOWN || code==SYS_HALT)
+ wdt_turnoff();
+ return NOTIFY_DONE;
+}
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier=
+{
+ wdt_notify_sys,
+ 0,
+ 0
+};
+
+static void __exit sc520_wdt_unload(void)
+{
+ wdt_turnoff();
+
+ /* Deregister */
+ misc_deregister(&wdt_miscdev);
+ iounmap(wdtmrctl);
+ unregister_reboot_notifier(&wdt_notifier);
+}
+
+static int __init sc520_wdt_init(void)
+{
+ int rc = -EBUSY;
+ unsigned long cbar;
+
+ spin_lock_init(&wdt_spinlock);
+
+ init_timer(&timer);
+ timer.function = wdt_timer_ping;
+ timer.data = 0;
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc)
+ goto err_out_region2;
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc)
+ goto err_out_miscdev;
+
+ /* get the Base Address Register */
+ cbar = inl_p(0xfffc);
+ printk(OUR_NAME ": CBAR: 0x%08lx\n", cbar);
+ /* check if MMCR aliasing bit is set */
+ if (cbar & 0x80000000) {
+ printk(OUR_NAME ": MMCR Aliasing enabled.\n");
+ wdtmrctl = (__u16 *)(cbar & 0x3fffffff);
+ } else {
+ printk(OUR_NAME "!!! WARNING !!!\n"
+ "\t MMCR Aliasing found NOT enabled!\n"
+ "\t Using default value of: %p\n"
+ "\t This has not been tested!\n"
+ "\t Please email Scott Jennings <smj@oro.net>\n"
+ "\t and Bill Jennings <bj@oro.net> if it works!\n"
+ , MMCR_BASE_DEFAULT
+ );
+ wdtmrctl = MMCR_BASE_DEFAULT;
+ }
+
+ wdtmrctl = (__u16 *)((char *)wdtmrctl + OFFS_WDTMRCTL);
+ wdtmrctl = ioremap((unsigned long)wdtmrctl, 2);
+ printk(KERN_INFO OUR_NAME ": WDT driver for SC520 initialised.\n");
+
+ return 0;
+
+err_out_miscdev:
+ misc_deregister(&wdt_miscdev);
+err_out_region2:
+ return rc;
+}
+
+module_init(sc520_wdt_init);
+module_exit(sc520_wdt_unload);
+
+MODULE_AUTHOR("Scott and Bill Jennings");
+MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
--- /dev/null
+/*
+ * ICP Wafer 5823 Single Board Computer WDT driver for Linux 2.4.x
+ * http://www.icpamerica.com/wafer_5823.php
+ * May also work on other similar models
+ *
+ * (c) Copyright 2002 Justin Cormack <justin@street-vision.com>
+ *
+ * Release 0.02
+ *
+ * Based on advantechwdt.c which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ * http://www.redhat.com
+ *
+ * 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.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+
+static unsigned long wafwdt_is_open;
+static spinlock_t wafwdt_lock;
+
+/*
+ * You must set these - there is no sane way to probe for this board.
+ *
+ * To enable, write the timeout value in seconds (1 to 255) to I/O
+ * port WDT_START, then read the port to start the watchdog. To pat
+ * the dog, read port WDT_STOP to stop the timer, then read WDT_START
+ * to restart it again.
+ */
+
+#define WDT_START 0x443
+#define WDT_STOP 0x843
+
+#define WD_TIMO 60 /* 1 minute */
+static int wd_margin = WD_TIMO;
+
+static void wafwdt_ping(void)
+{
+ /* pat watchdog */
+ spin_lock(&wafwdt_lock);
+ inb_p(WDT_STOP);
+ inb_p(WDT_START);
+ spin_unlock(&wafwdt_lock);
+}
+
+static void wafwdt_start(void)
+{
+ /* start up watchdog */
+ outb_p(wd_margin, WDT_START);
+ inb_p(WDT_START);
+}
+
+static void
+wafwdt_stop(void)
+{
+ /* stop watchdog */
+ inb_p(WDT_STOP);
+}
+
+static ssize_t wafwdt_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
+{
+ /* Can't seek (pwrite) on this device */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ if (count) {
+ wafwdt_ping();
+ return 1;
+ }
+ return 0;
+}
+
+static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_margin;
+ static struct watchdog_info ident = {
+ WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+ 1, "Wafer 5823 WDT"
+ };
+ int one=1;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user
+ ((struct watchdog_info *) arg, &ident, sizeof (ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ if (copy_to_user((int *) arg, &one, sizeof (int)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wafwdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int *)arg))
+ return -EFAULT;
+ if ((new_margin < 1) || (new_margin > 255))
+ return -EINVAL;
+ wd_margin = new_margin;
+ wafwdt_stop();
+ wafwdt_start();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(wd_margin, (int *)arg);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int wafwdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wafwdt_is_open))
+ return -EBUSY;
+ wafwdt_start();
+ return 0;
+}
+
+static int
+wafwdt_close(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &wafwdt_is_open);
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ wafwdt_stop();
+#endif
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT) {
+ /* Turn the WDT off */
+ wafwdt_stop();
+ }
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static struct file_operations wafwdt_fops = {
+ owner:THIS_MODULE,
+ write:wafwdt_write,
+ ioctl:wafwdt_ioctl,
+ open:wafwdt_open,
+ release:wafwdt_close,
+};
+
+static struct miscdevice wafwdt_miscdev = {
+ WATCHDOG_MINOR,
+ "watchdog",
+ &wafwdt_fops
+};
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wafwdt_notifier = {
+ wafwdt_notify_sys,
+ NULL,
+ 0
+};
+
+static int __init wafwdt_init(void)
+{
+ printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n");
+
+ spin_lock_init(&wafwdt_lock);
+ if(!request_region(WDT_STOP, 1, "Wafer 5823 WDT"))
+ goto error;
+ if(!request_region(WDT_START, 1, "Wafer 5823 WDT"))
+ goto error2;
+ if(misc_register(&wafwdt_miscdev)<0)
+ goto error3;
+ register_reboot_notifier(&wafwdt_notifier);
+ return 0;
+error3:
+ release_region(WDT_START, 1);
+error2:
+ release_region(WDT_STOP, 1);
+error:
+ return -ENODEV;
+}
+
+static void __exit wafwdt_exit(void)
+{
+ misc_deregister(&wafwdt_miscdev);
+ unregister_reboot_notifier(&wafwdt_notifier);
+ release_region(WDT_STOP, 1);
+ release_region(WDT_START, 1);
+}
+
+module_init(wafwdt_init);
+module_exit(wafwdt_exit);
+
+MODULE_AUTHOR("Justin Cormack");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+/* end of wafer5823wdt.c */