URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [char/] [pcwd.c] - Rev 1626
Go to most recent revision | Compare with Previous | Blame | View Log
/* * PC Watchdog Driver * by Ken Hollis (khollis@bitgate.com) * * Permission granted from Simon Machell (73244.1270@compuserve.com) * Written for the Linux Kernel, and GPLed by Ken Hollis * * 960107 Added request_region routines, modulized the whole thing. * 960108 Fixed end-of-file pointer (Thanks to Dan Hollis), added * WD_TIMEOUT define. * 960216 Added eof marker on the file, and changed verbose messages. * 960716 Made functional and cosmetic changes to the source for * inclusion in Linux 2.0.x kernels, thanks to Alan Cox. * 960717 Removed read/seek routines, replaced with ioctl. Also, added * check_region command due to Alan's suggestion. * 960821 Made changes to compile in newer 2.0.x kernels. Added * "cold reboot sense" entry. * 970819 Fixed use of is_open flag so pcwd_write does not always return * -EIO; this prevented tickling. Enabled board on open and * disabled on close. */ #include <linux/module.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/tty.h> #include <linux/timer.h> #include <linux/kernel.h> #include <linux/wait.h> #include <linux/string.h> #include <linux/malloc.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/pcwd.h> #include <asm/io.h> #define WD_VER "0.50 (08/21/96)" #define WD_MINOR 130 /* Minor device number */ #define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */ #define WD_TIMERRESET_PORT1 0x270 /* Reset port - first choice */ #define WD_TIMERRESET_PORT2 0x370 /* Reset port - second choice */ #define WD_CTLSTAT_PORT1 0x271 /* Control port - first choice */ #define WD_CTLSTAT_PORT2 0x371 /* Control port - second choice */ #define WD_CTLSTAT2_PORT1 0x272 /* Control port#2 - first choice */ #define WD_CTLSTAT2_PORT2 0x372 /* Control port#2 - second choice */ #define WD_DISABLE_PORT1 0x273 /* Disable port - first choice */ #define WD_DISABLE_PORT2 0x373 /* Disable port - second choice */ #define WD_PORT_EXTENT 4 /* Takes up four addresses */ #define WD_WDRST 0x01 /* Previously reset state */ #define WD_T110 0x02 /* Temperature overheat sense */ #define WD_HRTBT 0x04 /* Heartbeat sense */ #define WD_RLY2 0x08 /* External relay triggered */ #define WD_SRLY2 0x80 /* Software external relay triggered */ static int current_ctlport, current_readport; static int current_ctlport2, current_disport; static int is_open, is_eof; int pcwd_checkcard(void) { int card_dat, prev_card_dat, found = 0, count = 0, done = 0; /* As suggested by Alan Cox */ if (check_region(current_ctlport, WD_PORT_EXTENT)) { printk("pcwd: Port 0x%x unavailable.\n", current_ctlport); return 0; } card_dat = 0x00; prev_card_dat = 0x00; prev_card_dat = inb(current_readport); while(count < WD_TIMEOUT) { #ifdef DEBUG printk("pcwd: Run #%d on port 0x%03x\n", count, current_readport); #endif /* Read the raw card data from the port, and strip off the first 4 bits */ card_dat = inb_p(current_readport); card_dat &= 0x000F; /* Sleep 1/2 second (or 500000 microseconds :) */ udelay(500000L); done = 0; /* 0x0F usually means that no card data is present, or the card is not installed on this port. If 0x0F is present here, it's normally safe to assume there's no card at that base address. */ if (card_dat == 0x0F) { count++; done = 1; #ifdef DEBUG printk("pcwd: I show nothing on this port.\n"); #endif } /* If there's a heart beat in both instances, then this means we found our card. This also means that either the card was previously reset, or the computer was power-cycled. */ if ((card_dat & WD_HRTBT) && (prev_card_dat & WD_HRTBT) && (!done)) { found = 1; done = 1; #ifdef DEBUG printk("pcwd: I show alternate heart beats. Card detected.\n"); #endif break; } /* If the card data is exactly the same as the previous card data, it's safe to assume that we should check again. The manual says that the heart beat will change every second (or the bit will toggle), and this can be used to see if the card is there. If the card was powered up with a cold boot, then the card will not start blinking until 2.5 minutes after a reboot, so this bit will stay at 1. */ if ((card_dat == prev_card_dat) && (!done)) { count++; #ifdef DEBUG printk("pcwd: The card data is exactly the same (possibility).\n"); #endif done = 1; } /* If the card data is toggling any bits, this means that the heart beat was detected, or something else about the card is set. */ if ((card_dat != prev_card_dat) && (!done)) { done = 1; found = 1; #ifdef DEBUG printk("pcwd: I show alternate heart beats. Card detected.\n"); #endif break; } /* Otherwise something else strange happened. */ if (!done) count++; } return((found) ? 1 : 0); } void pcwd_showprevstate(void) { int card_status = 0x0000; card_status = inb(current_readport); if (card_status & WD_WDRST) printk("pcwd: Previous reboot was caused by the card.\n"); if (card_status & WD_T110) printk("pcwd: CPU overheat sense.\n"); if ((!(card_status & WD_WDRST)) && (!(card_status & WD_T110))) printk("pcwd: Cold boot sense.\n"); } static int pcwd_return_data(void) { return(inb(current_readport)); } static int pcwd_write(struct inode *inode, struct file *file, const char *data, int len) { int wdrst_stat; if (!is_open) return -EIO; #ifdef DEBUG printk("pcwd: write request\n"); #endif wdrst_stat = inb_p(current_readport); wdrst_stat &= 0x0F; wdrst_stat |= WD_WDRST; outb_p(wdrst_stat, current_ctlport); return(len); } static int pcwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int i, cdat, rv; switch(cmd) { default: return -ENOIOCTLCMD; case PCWD_GETSTAT: i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int)); if (i) return i; else { cdat = pcwd_return_data(); rv = 0; if (cdat & WD_WDRST) rv |= 0x01; if (cdat & WD_T110) rv |= 0x02; put_user(rv, (int *) arg); return 0; } break; case PCWD_PING: pcwd_write(NULL, NULL, NULL, 1); /* Is this legal? */ break; } return 0; } static int pcwd_open(struct inode *ino, struct file *filep) { #ifdef DEBUG int before, after; before = inb_p (current_ctlport2); #endif if (is_open) return -EIO; /* Enable the port */ outb_p (0, current_disport); #ifdef DEBUG after = inb_p (current_ctlport2); printk ("pcwd: open: control status #2 (b,a): %x, %x\n", before,after); #endif MOD_INC_USE_COUNT; is_open = 1; is_eof = 0; return(0); } static void pcwd_close(struct inode *ino, struct file *filep) { #ifdef DEBUG printk("pcwd: close request\n"); #endif is_open = 0; /* Disable the board */ outb_p (0xa5, current_disport); outb_p (0xa5, current_disport); MOD_DEC_USE_COUNT; } static struct file_operations pcwd_fops = { NULL, /* Seek */ NULL, /* Read */ pcwd_write, /* Write */ NULL, /* Readdir */ NULL, /* Select */ pcwd_ioctl, /* IOctl */ NULL, /* MMAP */ pcwd_open, /* Open */ pcwd_close /* Close */ }; static struct miscdevice pcwd_miscdev = { WD_MINOR, "pcwatchdog", &pcwd_fops }; #ifdef MODULE int init_module(void) #else int pcwatchdog_init(void) #endif { #ifdef DEBUG printk("pcwd: Success.\n"); #endif printk("pcwd: v%s Ken Hollis (khollis@bitgate.com)\n", WD_VER); #ifdef DEBUG printk("pcwd: About to perform card autosense loop.\n"); #endif is_eof = 0; is_open = 0; current_ctlport = WD_TIMERRESET_PORT1; current_readport = WD_CTLSTAT_PORT1; current_ctlport2 = WD_CTLSTAT2_PORT1; current_disport = WD_DISABLE_PORT1; if (!pcwd_checkcard()) { #ifdef DEBUG printk("pcwd: Trying port 0x370.\n"); #endif current_ctlport = WD_TIMERRESET_PORT2; current_readport = WD_CTLSTAT_PORT2; current_ctlport2 = WD_CTLSTAT2_PORT2; current_disport = WD_DISABLE_PORT2; if (!pcwd_checkcard()) { printk("pcwd: No card detected, or wrong port assigned.\n"); return(-EIO); } else printk("pcwd: Watchdog Rev.A detected at port 0x370\n"); } else printk("pcwd: Watchdog Rev.A detected at port 0x270\n"); pcwd_showprevstate(); #ifdef DEBUG printk("pcwd: Requesting region entry\n"); #endif request_region(current_ctlport, WD_PORT_EXTENT, "PCWD Rev.A (Berkshire)"); #ifdef DEBUG printk("pcwd: character device creation.\n"); #endif misc_register(&pcwd_miscdev); return 0; } #ifdef MODULE void cleanup_module(void) { /* Disable the board */ outb_p (0xa5, current_disport); outb_p (0xa5, current_disport); misc_deregister(&pcwd_miscdev); release_region(current_ctlport, WD_PORT_EXTENT); #ifdef DEBUG printk("pcwd: Cleanup successful.\n"); #endif } #endif /* ** TODO: ** ** Both Revisions: ** o) Support for revision B of the Watchdog Card ** o) Implement the rest of the IOCTLs as discussed with Alan Cox ** o) Implement only card heartbeat reset via IOCTL, not via write ** o) Faster card detection routines ** o) /proc device creation ** ** Revision B functions: ** o) /dev/temp device creation for temperature device (possibly use ** the one from the WDT drivers?) ** o) Direct Motorola controller chip access via read/write routines ** o) Autoprobe IO Ports for autodetection (possibly by chip detect?) */
Go to most recent revision | Compare with Previous | Blame | View Log