summaryrefslogtreecommitdiffstats
path: root/drivers/char/wdt.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /drivers/char/wdt.c
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'drivers/char/wdt.c')
-rw-r--r--drivers/char/wdt.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c
new file mode 100644
index 000000000..62129edd3
--- /dev/null
+++ b/drivers/char/wdt.c
@@ -0,0 +1,278 @@
+/*
+ * Industrial Computer Source WDT500/501 driver for Linux 1.3.x
+ *
+ * (c) Copyright 1995 CymruNET Ltd
+ * Innovation Centre
+ * Singleton Park
+ * Swansea
+ * Wales
+ * UK
+ * SA2 8PP
+ *
+ * http://www.cymru.net
+ *
+ * This driver is provided under the GNU public license, incorporated
+ * herein by reference. The driver is provided without warranty or
+ * support.
+ *
+ * Release 0.05.
+ *
+ * Some changes by Dave Gregorich to fix modularisation and minor bugs.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include "wd501p.h"
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+static int wdt_is_open=0;
+
+/*
+ * You must set these - there is no sane way to probe for this board.
+ */
+
+int io=0x240;
+int irq=14;
+
+#define WD_TIMO (100*60) /* 1 minute */
+
+/*
+ * Programming support
+ */
+
+static void wdt_ctr_mode(int ctr, int mode)
+{
+ ctr<<=6;
+ ctr|=0x30;
+ ctr|=(mode<<1);
+ outb_p(ctr, WDT_CR);
+}
+
+static void wdt_ctr_load(int ctr, int val)
+{
+ outb_p(val&0xFF, WDT_COUNT0+ctr);
+ outb_p(val>>8, WDT_COUNT0+ctr);
+}
+
+/*
+ * Kernel methods.
+ */
+
+static void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /*
+ * Read the status register see what is up and
+ * then printk it.
+ */
+
+ unsigned char status=inb_p(WDT_SR);
+
+ status|=FEATUREMAP1;
+ status&=~FEATUREMAP2;
+
+ printk(KERN_CRIT "WDT status %d\n", status);
+
+ if(!(status&WDC_SR_TGOOD))
+ printk(KERN_CRIT "Overheat alarm.(%d)\n",inb_p(WDT_RT));
+ if(!(status&WDC_SR_PSUOVER))
+ printk(KERN_CRIT "PSU over voltage.\n");
+ if(!(status&WDC_SR_PSUUNDR))
+ printk(KERN_CRIT "PSU under voltage.\n");
+ if(!(status&WDC_SR_FANGOOD))
+ printk(KERN_CRIT "Possible fan fault.\n");
+ if(!(status&WDC_SR_WCCR))
+#ifdef SOFTWARE_REBOOT
+#ifdef ONLY_TESTING
+ printk(KERN_CRIT "Would Reboot.\n");
+#else
+ printk(KERN_CRIT "Initiating system reboot.\n");
+ hard_reset_now();
+#endif
+#else
+ printk(KERN_CRIT "Reset in 5ms.\n");
+#endif
+}
+
+
+static long long wdt_llseek(struct inode *inode, struct file *file, long long offset,
+ int origin)
+{
+ return -ESPIPE;
+}
+
+static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+{
+ /* Write a watchdog value */
+ inb_p(WDT_DC);
+ wdt_ctr_mode(1,2);
+ wdt_ctr_load(1,WD_TIMO); /* Timeout */
+ outb_p(0, WDT_DC);
+ return count;
+}
+
+/*
+ * Read reports the temperature in farenheit
+ */
+
+static long wdt_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ unsigned short c=inb_p(WDT_RT);
+ unsigned char cp;
+ int err;
+
+ switch(MINOR(inode->i_rdev))
+ {
+ case TEMP_MINOR:
+ err=verify_area(VERIFY_WRITE, buf, 1);
+ if(err)
+ return err;
+ c*=11;
+ c/=15;
+ cp=c+7;
+ copy_to_user(buf,&cp,1);
+ return 1;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return -EINVAL;
+}
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ switch(MINOR(inode->i_rdev))
+ {
+ case WATCHDOG_MINOR:
+ if(wdt_is_open)
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ /*
+ * Activate
+ */
+
+ wdt_is_open=1;
+ inb_p(WDT_DC); /* Disable */
+ wdt_ctr_mode(0,3);
+ wdt_ctr_mode(1,2);
+ wdt_ctr_mode(2,0);
+ wdt_ctr_load(0, 8948); /* count at 100Hz */
+ wdt_ctr_load(1,WD_TIMO); /* Timeout 120 seconds */
+ wdt_ctr_load(2,65535);
+ outb_p(0, WDT_DC); /* Enable */
+ return 0;
+ case TEMP_MINOR:
+ MOD_INC_USE_COUNT;
+ return 0;
+ default:
+ return -ENODEV;
+ }
+}
+
+static void wdt_release(struct inode *inode, struct file *file)
+{
+ if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
+ {
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ inb_p(WDT_DC); /* Disable counters */
+ wdt_ctr_load(2,0); /* 0 length reset pulses now */
+#endif
+ wdt_is_open=0;
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+
+static struct file_operations wdt_fops = {
+ wdt_llseek,
+ wdt_read,
+ wdt_write,
+ NULL, /* No Readdir */
+ NULL, /* No Select */
+ wdt_ioctl,
+ NULL, /* No mmap */
+ wdt_open,
+ wdt_release
+};
+
+static struct miscdevice wdt_miscdev=
+{
+ WATCHDOG_MINOR,
+ "wdt",
+ &wdt_fops
+};
+
+#ifdef CONFIG_WDT_501
+static struct miscdevice temp_miscdev=
+{
+ TEMP_MINOR,
+ "temperature",
+ &wdt_fops
+};
+#endif
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ printk("WDT501-P module at %X(Interrupt %d)\n", io,irq);
+ if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL))
+ {
+ printk("IRQ %d is not free.\n", irq);
+ return -EIO;
+ }
+ misc_register(&wdt_miscdev);
+#ifdef CONFIG_WDT_501
+ misc_register(&temp_miscdev);
+#endif
+ request_region(io, 8, "wdt501");
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ misc_deregister(&wdt_miscdev);
+#ifdef CONFIG_WDT_501
+ misc_deregister(&temp_miscdev);
+#endif
+ release_region(io,8);
+ free_irq(irq, NULL);
+}
+
+#else
+
+int wdt_init(void)
+{
+ printk("WDT500/501-P driver at %X(Interrupt %d)\n", io,irq);
+ if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL))
+ {
+ printk("IRQ %d is not free.\n", irq);
+ return -EIO;
+ }
+ misc_register(&wdt_miscdev);
+#ifdef CONFIG_WDT_501
+ misc_register(&temp_miscdev);
+#endif
+ request_region(io, 8, "wdt501");
+ return 0;
+}
+
+#endif