patch-2.1.22 linux/drivers/char/pcwd.c

Next file: linux/drivers/char/tpqic02.c
Previous file: linux/drivers/char/misc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.21/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
@@ -19,6 +19,15 @@
  *		typedefs to replace them.  Made heartbeat reset only available
  *		via ioctl, and removed the write routine.
  * 960828	Added new items for PC Watchdog Rev.C card.
+ * 960829	Changed around all of the IOCTLs, added new features,
+ *		added watchdog disable/re-enable routines.  Added firmware
+ *		version reporting.  Added read routine for temperature.
+ *		Removed some extra defines, added an autodetect Revision
+ *		routine.
+ * 961006       Revised some documentation, fixed some cosmetic bugs.  Made
+ *              drivers to panic the system if it's overheating at bootup.
+ * 961118	Changed some verbiage on some of the output, tidied up
+ *		code bits, and added compatibility to 2.1.x.
  */
 
 #include <linux/module.h>
@@ -39,65 +48,60 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/watchdog.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
 
-typedef struct pcwd_ioports {
-	int first_port;
-	int range;
-} IOPS;
+#include <asm/uaccess.h>
+#include <asm/io.h>
 
 /*
-** These are the auto-probe addresses available for the Rev.A version of the
-** PC Watchdog card.
-*/
-
-static IOPS pcwd_ioports[] = {
-	{ 0x270, 3 },
-	{ 0x350, 3 },
-	{ 0x370, 3 },
-	{ 0x000, 0 }
-};
+ * These are the auto-probe addresses available.
+ *
+ * Revision A only uses ports 0x270 and 0x370.  Revision C introduced 0x350.
+ * Revision A has an address range of 2 addresses, while Revision C has 3.
+ */
+static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
 
-#ifdef DEBUG
-#define dprintk(x)	printk(x)
-#else
-#define dprintk(x)
+#define WD_VER                  "1.0 (11/18/96)"
+#define	WD_MINOR		130	/* Minor device number */
+#ifndef	TEMP_MINOR
+#define	TEMP_MINOR		131	/* Uses the same as WDT */
 #endif
 
-#ifdef CONFIG_PCWD_REV_A
-#define CARD_REV	"A"
-#define PORT_OFFSET	0
-#define PORT_RANGE	2
+/*
+ * It should be noted that PCWD_REVISION_B was removed because A and B
+ * are essentially the same types of card, with the exception that B
+ * has temperature reporting.  Since I didn't receive a Rev.B card,
+ * the Rev.B card is not supported.  (It's a good thing too, as they
+ * are no longer in production.)
+ */
+#define	PCWD_REVISION_A		1
+#define	PCWD_REVISION_C		2
+
+#define	WD_TIMEOUT		3	/* 1 1/2 seconds for a timeout */
+
+/*
+ * These are the defines for the PC Watchdog card, revision A.
+ */
 #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 */
-#endif
-#ifdef CONFIG_PCWD_REV_C
-#define CARD_REV	"C"
-#define PORT_OFFSET	1
-#define PORT_RANGE	4
-#define WD_WDRST                0x01	/* Previously reset state */
-#define WD_T110                 0x04	/* Temperature overheat sense */
-#endif
 
-#define WD_VER                  "0.52 (08/28/96)"
-#define	WD_MINOR		130	/* Minor device number */
-
-#define	WD_TIMEOUT		3	/* 1 1/2 seconds for a timeout */
-
-
-static int current_readport;
+static int current_readport, revision, temp_panic;
 static int is_open, initial_status, supports_temp, mode_debug;
 
-int pcwd_checkcard(void)
+/*
+ * PCWD_CHECKCARD
+ *
+ * This routine checks the "current_readport" to see if the card lies there.
+ * If it does, it returns accordingly.
+ */
+static 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_readport, PORT_RANGE)) {
+	/* As suggested by Alan Cox - this is a safety measure. */
+	if (check_region(current_readport, 4)) {
 		printk("pcwd: Port 0x%x unavailable.\n", current_readport);
 		return 0;
 	}
@@ -106,13 +110,10 @@
 	prev_card_dat = 0x00;
 
 	prev_card_dat = inb(current_readport);
-	if (prev_card_dat == 0xFF) {
-		dprintk(("pcwd: No card detected at 0x%03x\n", current_readport));
+	if (prev_card_dat == 0xFF)
 		return 0;
-	}
 
 	while(count < WD_TIMEOUT) {
-		dprintk(("pcwd: Run #%d on port 0x%03x\n", count, current_readport));
 
 	/* Read the raw card data from the port, and strip off the
 	   first 4 bits */
@@ -125,17 +126,6 @@
 		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;
-
-			dprintk(("pcwd: I show nothing on this port.\n"));
-		}
-
 	/* 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. */
@@ -144,8 +134,6 @@
 			(!done)) {
 			found = 1;
 			done = 1;
-			
-			dprintk(("pcwd: I show alternate heart beats.  Card detected.\n"));
 			break;
 		}
 
@@ -159,7 +147,6 @@
 
 		if ((card_dat == prev_card_dat) && (!done)) {
 			count++;
-			dprintk(("pcwd: The card data is exactly the same (possibility).\n"));
 			done = 1;
 		}
 
@@ -169,7 +156,6 @@
 		if ((card_dat != prev_card_dat) && (!done)) {
 			done = 1;
 			found = 1;
-			dprintk(("pcwd: I show alternate heart beats.  Card detected.\n"));
 			break;
 		}
 
@@ -186,18 +172,36 @@
 {
 	int card_status = 0x0000;
 
-	initial_status = card_status = inb(current_readport + PORT_OFFSET);
-
-	if (card_status & WD_WDRST)
-		printk("pcwd: Previous reboot was caused by the card.\n");
+	if (revision == PCWD_REVISION_A)
+		initial_status = card_status = inb(current_readport);
+	else
+		initial_status = card_status = inb(current_readport + 1);
+
+	if (revision == PCWD_REVISION_A) {
+		if (card_status & WD_WDRST)
+			printk("pcwd: Previous reboot was caused by the card.\n");
+
+		if (card_status & WD_T110) {
+			printk("pcwd: Card senses a CPU Overheat.  Panicking!\n");
+			panic("pcwd: CPU Overheat.\n");
+		}
 
-	if (supports_temp)
-		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");
+	} else {
+		if (card_status & 0x01)
+			printk("pcwd: Previous reboot was caused by the card.\n");
+
+		if (card_status & 0x04) {
+			printk("pcwd: Card senses a CPU Overheat.  Panicking!\n");
+			panic("pcwd: CPU Overheat.\n");
+		}
 
-	if ((!(card_status & WD_WDRST)) &&
-	    (!(card_status & WD_T110)))
-		printk("pcwd: Cold boot sense.\n");
+		if ((!(card_status & 0x01)) &&
+		    (!(card_status & 0x04)))
+			printk("pcwd: Cold boot sense.\n");
+	}
 }
 
 static void pcwd_send_heartbeat(void)
@@ -207,14 +211,15 @@
 	if (!is_open)
 		return;
 
-	dprintk(("pcwd: heartbeat\n"));
-
 	wdrst_stat = inb_p(current_readport);
 	wdrst_stat &= 0x0F;
 
 	wdrst_stat |= WD_WDRST;
 
-	outb_p(wdrst_stat, current_readport + PORT_OFFSET);
+	if (revision == PCWD_REVISION_A)
+		outb_p(wdrst_stat, current_readport + 1);
+	else
+		outb_p(wdrst_stat, current_readport);
 }
 
 static int pcwd_ioctl(struct inode *inode, struct file *file,
@@ -223,15 +228,12 @@
 	int i, cdat, rv;
 	static struct watchdog_info ident=
 	{
+		/* FIXME: should set A/C here */
 		WDIOF_OVERHEAT|WDIOF_CARDRESET,
-#ifdef CONFIG_PCWD_REV_A	
 		1,
-#else
-		3,
-#endif				
-		"PCWD revision "CARD_REV"."
+		"PCWD."
 	};
-		
+
 	switch(cmd) {
 	default:
 		return -ENOIOCTLCMD;
@@ -244,33 +246,112 @@
 		cdat = inb(current_readport);
 		rv = 0;
 
-		if (cdat & WD_WDRST)
-			rv |= WDIOF_CARDRESET;
-
-		if (cdat & WD_T110)
-			rv |= WDIOF_OVERHEAT;
+		if (revision == PCWD_REVISION_A) 
+		{
+			if (cdat & WD_WDRST)
+				rv |= WDIOF_CARDRESET;
+
+			if (cdat & WD_T110) 
+			{
+				rv |= WDIOF_OVERHEAT;
+
+				if (temp_panic)
+					panic("pcwd: Temperature overheat trip!\n");
+			}
+		}
+		else 
+		{
+			if (cdat & 0x01)
+				rv |= WDIOF_CARDRESET;
+
+			if (cdat & 0x04) 
+			{
+				rv |= WDIOF_OVERHEAT;
+
+				if (temp_panic)
+					panic("pcwd: Temperature overheat trip!\n");
+			}
+		}
 
-		return put_user(rv, (int *) arg);
-		break;
+		if(put_user(rv, (int *) arg))
+			return -EFAULT;
+		return 0;
 
 	case WDIOC_GETBOOTSTATUS:
 		rv = 0;
 
-		if (initial_status & WD_WDRST)
-			rv |= WDIOF_CARDRESET;
+		if (revision == PCWD_REVISION_A) 
+		{
+			if (initial_status & WD_WDRST)
+				rv |= WDIOF_CARDRESET;
 
-		if (initial_status & WD_T110)
-			rv |= WDIOF_OVERHEAT;
-		return put_user(rv, (int *) arg);
+			if (initial_status & WD_T110)
+				rv |= WDIOF_OVERHEAT;
+		}
+		else
+		{
+			if (initial_status & 0x01)
+				rv |= WDIOF_CARDRESET;
+
+			if (initial_status & 0x04)
+				rv |= WDIOF_OVERHEAT;
+		}
+
+		if(put_user(rv, (int *) arg))
+			return -EFAULT;
+		return 0;
 
 	case WDIOC_GETTEMP:
+
 		rv = 0;
-		if ((supports_temp) && (mode_debug == 0)) {
+		if ((supports_temp) && (mode_debug == 0)) 
+		{
 			rv = inb(current_readport);
-			return put_user(rv, (int*) arg);
-		} else
-			return put_user(rv, (int*) arg);
+			if(put_user(rv, (int*) arg))
+				return -EFAULT;
+		} else if(put_user(rv, (int*) arg))
+				return -EFAULT;
+		return 0;
 
+	case WDIOC_SETOPTIONS:
+		if (revision == PCWD_REVISION_C) 
+		{
+			if(copy_from_user(&rv, (int*) arg, sizeof(int)))
+				return -EFAULT;
+
+			if (rv & WDIOS_DISABLECARD) 
+			{
+				outb_p(0xA5, current_readport + 3);
+				outb_p(0xA5, current_readport + 3);
+				cdat = inb_p(current_readport + 2);
+				if ((cdat & 0x10) == 0) 
+				{
+					printk("pcwd: Could not disable card.\n");
+					return -EIO;
+				}
+
+				return 0;
+			}
+
+			if (rv & WDIOS_ENABLECARD) 
+			{
+				outb_p(0x00, current_readport + 3);
+				cdat = inb_p(current_readport + 2);
+				if (cdat & 0x10) 
+				{
+					printk("pcwd: Could not enable card.\n");
+					return -EIO;
+				}
+				return 0;
+			}
+
+			if (rv & WDIOS_TEMPPANIC) 
+			{
+				temp_panic = 1;
+			}
+		}
+		return -EINVAL;
+		
 	case WDIOC_KEEPALIVE:
 		pcwd_send_heartbeat();
 		return 0;
@@ -279,7 +360,7 @@
 	return 0;
 }
 
-static long pcwd_write(struct file *file, struct inode *inode, const char *buf, unsigned long len)
+static long pcwd_write(struct inode *inode, struct file *file, const char *buf, unsigned long len)
 {
 	if (len)
 	{
@@ -291,30 +372,107 @@
 
 static int pcwd_open(struct inode *ino, struct file *filep)
 {
-	dprintk(("pcwd: open request\n"));
-
 	MOD_INC_USE_COUNT;
 	return(0);
 }
 
-static void pcwd_close(struct inode *ino, struct file *filep)
+static long pcwd_read(struct inode *inode, struct file *file, char *buf,
+	unsigned long count)
 {
-	dprintk(("pcwd: close request\n"));
+	unsigned short c = inb(current_readport);
+	unsigned char cp;
+
+	switch(MINOR(inode->i_rdev)) 
+	{
+		case TEMP_MINOR:
+			cp = c;
+			if(copy_to_user(buf, &cp, 1))
+				return -EFAULT;
+			return 1;
+		default:
+			return -EINVAL;
+	}
+}
 
+static void pcwd_close(struct inode *ino, struct file *filep)
+{
 	MOD_DEC_USE_COUNT;
 }
 
 static void get_support(void)
 {
-#ifdef CONFIG_PCWD_REV_C
 	if (inb(current_readport) != 0xF0)
-#endif	
 		supports_temp = 1;
 }
 
+static int get_revision(void)
+{
+	if ((inb(current_readport + 2) == 0xFF) ||
+	    (inb(current_readport + 3) == 0xFF))
+		return(PCWD_REVISION_A);
+
+	return(PCWD_REVISION_C);
+}
+
+static int send_command(int cmd)
+{
+	int i;
+
+	outb_p(cmd, current_readport + 2);
+	udelay(1000L);
+
+	i = inb(current_readport);
+	i = inb(current_readport);
+
+	return(i);
+}
+
+static char *get_firmware(void)
+{
+	int i, found = 0, count = 0, one, ten, hund, minor;
+	char *ret;
+
+	ret = kmalloc(6, GFP_KERNEL);
+
+	while((count < 3) && (!found)) {
+		outb_p(0x80, current_readport + 2);
+		i = inb(current_readport);
+
+		if (i == 0x00)
+			found = 1;
+		else if (i == 0xF3)
+			outb_p(0x00, current_readport + 2);
+
+		udelay(400L);
+		count++;
+	}
+
+	if (found) {
+		mode_debug = 1;
+
+		one = send_command(0x81);
+		ten = send_command(0x82);
+		hund = send_command(0x83);
+		minor = send_command(0x84);
+	}
+
+	if (found)
+		sprintf(ret, "%c.%c%c%c", one, ten, hund, minor);
+	else
+		sprintf(ret, "ERROR");
+
+	return(ret);
+}
+
+static void debug_off(void)
+{
+	outb_p(0x00, current_readport + 2);
+	mode_debug = 0;
+}
+
 static struct file_operations pcwd_fops = {
 	NULL,		/* Seek */
-	NULL,		/* Read */
+	pcwd_read,	/* Read */
 	pcwd_write,	/* Write */
 	NULL,		/* Readdir */
 	NULL,		/* Select */
@@ -329,6 +487,12 @@
 	"pcwatchdog",
 	&pcwd_fops
 };
+
+static struct miscdevice temp_miscdev = {
+	TEMP_MINOR,
+	"temperature",
+	&pcwd_fops
+};
  
 #ifdef	MODULE
 int init_module(void)
@@ -338,24 +502,21 @@
 {
 	int i, found = 0;
 
-	dprintk(("pcwd: Success.\n"));
-	printk(KERN_INFO "pcwd: v%s Ken Hollis (khollis@bitgate.com)\n", WD_VER);
+	revision = PCWD_REVISION_A;
 
-	dprintk(("pcwd: About to perform card autosense loop.\n"));
+	printk("pcwd: v%s Ken Hollis (khollis@nurk.org)\n", WD_VER);
 
 	/* Initial variables */
 	is_open = 0;
 	supports_temp = 0;
 	mode_debug = 0;
+	temp_panic = 0;
 	initial_status = 0x0000;
 
-	dprintk(("pcwd: Revision " CARD_REV " support defined.\n"));
+#ifndef	PCWD_BLIND
+	for (i = 0; pcwd_ioports[i] != 0; i++) {
+		current_readport = pcwd_ioports[i];
 
-	for (i = 0; pcwd_ioports[i].first_port != 0; i++) {
-		current_readport = pcwd_ioports[i].first_port;
-
-	if (!pcwd_checkcard()) {
-		dprintk(("pcwd: Trying port 0x%03x.\n", pcwd_ioports[i].first_port));
 		if (pcwd_checkcard()) {
 			found = 1;
 			break;
@@ -363,33 +524,45 @@
 	}
 
 	if (!found) {
-		printk("pcwd: No card detected.\n");
+		printk("pcwd: No card detected, or port not available.\n");
 		return(-EIO);
 	}
+#endif
 
 	is_open = 1;
 
+#ifdef	PCWD_BLIND
+	current_readport = PCWD_BLIND;
+#endif
+
 	get_support();
+	revision = get_revision();
 
-#ifdef	CONFIG_PCWD_REV_A
-	printk("pcwd: PC Watchdog (REV.A) detected at port 0x%03x\n", current_readport);
-#endif
-#ifdef	CONFIG_PCWD_REV_C
-	printk("pcwd: PC Watchdog (REV.C) detected at port 0x%03x -%stemp. support\n",
-		current_readport, (supports_temp) ? " Has " : " No ");
-#endif
+	if (revision == PCWD_REVISION_A)
+		printk("pcwd: PC Watchdog (REV.A) detected at port 0x%03x\n", current_readport);
+	else if (revision == PCWD_REVISION_C)
+		printk("pcwd: PC Watchdog (REV.C) detected at port 0x%03x (Firmware version: %s)\n",
+			current_readport, get_firmware());
+	else {
+		/* Should NEVER happen, unless get_revision() fails. */
+		printk("pcwd: Unable to get revision.\n");
+		return -1;
+	}
 
-#ifdef	CONFIG_PCWD_SHOW_PREVSTAT
-  	pcwd_showprevstate();
-#endif
-	dprintk(("pcwd: Requesting region entry\n"));
+	debug_off();
 
-	request_region(current_readport, PORT_RANGE, "PCWD Rev." CARD_REV "(Berkshire)");
+	pcwd_showprevstate();
 
-	dprintk(("pcwd: character device creation.\n"));
+	if (revision == PCWD_REVISION_A)
+		request_region(current_readport, 2, "PCWD Rev.A (Berkshire)");
+	else
+		request_region(current_readport, 4, "PCWD Rev.C (Berkshire)");
 
 	misc_register(&pcwd_miscdev);
 
+	if (supports_temp)
+		misc_register(&temp_miscdev);
+
 	return 0;
 }
 
@@ -397,23 +570,9 @@
 void cleanup_module(void)
 {
 	misc_deregister(&pcwd_miscdev);
-	release_region(current_readport, PORT_RANGE);
+	if (supports_temp)
+		misc_deregister(&temp_miscdev);
 
-	dprintk(("pcwd: Cleanup successful.\n"));
+	release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4);
 }
 #endif
-
-/*
-** TODO:
-**
-**	Both Revisions:
-**	o) Implement the rest of the IOCTLs as discussed with Alan Cox
-**	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?)
-*/

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov