diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /drivers/char/wdt.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'drivers/char/wdt.c')
-rw-r--r-- | drivers/char/wdt.c | 278 |
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 |