patch-2.1.15 linux/drivers/char/wdt.c

Next file: linux/drivers/net/3c509.c
Previous file: linux/drivers/char/wd501p.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.14/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
@@ -1,23 +1,27 @@
 /*
- *	Industrial Computer Source WDT500/501 driver for Linux 1.3.x
+ *	Industrial Computer Source WDT500/501 driver for Linux 2.1.x
  *
- *	(c) Copyright 1995	CymruNET Ltd
- *				Innovation Centre
- *				Singleton Park
- *				Swansea
- *				Wales
- *				UK
- *				SA2 8PP
+ *	(c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ *				http://www.cymru.net
  *
- *	http://www.cymru.net
+ *	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.	
  *
- *	This driver is provided under the GNU public license, incorporated
- *	herein by reference. The driver is provided without warranty or 
- *	support.
+ *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
- *	Release 0.05.
+ *	Release 0.06.
  *
- *	Some changes by Dave Gregorich to fix modularisation and minor bugs.
+ *	Fixes
+ *		Dave Gregorich	:	Modularisation and minor bugs
+ *		Alan Cox	:	Added the watchdog ioctl() stuff
+ *		Alan Cox	:	Fixed the reboot problem (as noted by
+ *					Matt Crocker).
  */
 
 #include <linux/config.h>
@@ -28,6 +32,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/miscdevice.h>
+#include <linux/watchdog.h>
 #include "wd501p.h"
 #include <linux/malloc.h>
 #include <linux/ioport.h>
@@ -35,6 +40,7 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <linux/notifier.h>
 
 static int wdt_is_open=0;
 
@@ -69,7 +75,33 @@
  *	Kernel methods.
  */
  
-static void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static int wdt_status(void)
+{
+	/*
+	 *	Status register to bit flags
+	 */
+	 
+	int flag=0;
+	unsigned char status=inb_p(WDT_SR);
+	status|=FEATUREMAP1;
+	status&=~FEATUREMAP2;	
+	
+	if(!(status&WDC_SR_TGOOD))
+		flag|=WDIOF_OVERHEAT;
+	if(!(status&WDC_SR_PSUOVER))
+		flag|=WDIOF_POWEROVER;
+	if(!(status&WDC_SR_PSUUNDR))
+		flag|=WDIOF_POWERUNDER;
+	if(!(status&WDC_SR_FANGOOD))
+		flag|=WDIOF_FANFAULT;
+	if(status&WDC_SR_ISOI0)
+		flag|=WDIOF_EXTERN1;
+	if(status&WDC_SR_ISII1)
+		flag|=WDIOF_EXTERN2;
+	return flag;
+}
+
+void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	/*
 	 *	Read the status register see what is up and
@@ -111,14 +143,23 @@
 	return -ESPIPE;
 }
 
-static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static void wdt_ping(void)
 {
 	/* 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;
+}
+
+static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+{
+	if(count)
+	{
+		wdt_ping();
+		return 1;
+	}
+	return 0;
 }
 
 /*
@@ -150,7 +191,41 @@
 static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	unsigned long arg)
 {
-	return -EINVAL;
+	int i;
+	static struct watchdog_info ident=
+	{
+		WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
+			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
+		1,
+		"WDT500/501"
+	};
+	
+	ident.options&=WDT_OPTION_MASK;	/* Mask down to the card we have */
+	switch(cmd)
+	{
+		default:
+			return -ENOIOCTLCMD;
+		case WDIOC_GETSUPPORT:
+			i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info));
+			if (i)
+				return i;
+			else
+				return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
+
+		case WDIOC_GETSTATUS:
+			i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
+			if (i)
+				return i;
+			else 
+			{
+				return put_user(wdt_status(),(int *)arg);
+			}
+		case WDIOC_GETBOOTSTATUS:
+			return put_user(0, (int *)arg);
+		case WDIOC_KEEPALIVE:
+			wdt_ping();
+			return 0;
+	}
 }
 
 static int wdt_open(struct inode *inode, struct file *file)
@@ -197,6 +272,22 @@
 }
 
 /*
+ *	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)
+	{
+		/* Turn the card off */
+		inb_p(WDT_DC);
+		wdt_ctr_load(2,0);
+	}
+	return NOTIFY_DONE;
+}
+ 
+/*
  *	Kernel Interfaces
  */
  
@@ -229,6 +320,18 @@
 };
 #endif
 
+/*
+ *	The WDT card needs to learn about soft shutdowns in order to
+ *	turn the timebomb registers off. 
+ */
+ 
+static struct notifier_block wdt_notifier=
+{
+	wdt_notify_sys,
+	NULL,
+	0
+};
+
 #ifdef MODULE
 
 int init_module(void)
@@ -244,6 +347,7 @@
 	misc_register(&temp_miscdev);
 #endif	
 	request_region(io, 8, "wdt501");
+	notifier_chain_register(&boot_notifier_list, &wdt_notifier);
 	return 0;
 }
 
@@ -253,6 +357,7 @@
 #ifdef CONFIG_WDT_501	
 	misc_deregister(&temp_miscdev);
 #endif	
+	notifier_chain_unregister(&boot_notifier_list, &wdt_notifier);
 	release_region(io,8);
 	free_irq(irq, NULL);
 }
@@ -272,6 +377,7 @@
 	misc_register(&temp_miscdev);
 #endif	
 	request_region(io, 8, "wdt501");
+	notifier_chain_register(&boot_notifier_list, &wdt_notifier);
 	return 0;
 }
 

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