patch-2.4.19 linux-2.4.19/drivers/char/shwdt.c

Next file: linux-2.4.19/drivers/char/softdog.c
Previous file: linux-2.4.19/drivers/char/sh-sci.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/char/shwdt.c linux-2.4.19/drivers/char/shwdt.c
@@ -9,6 +9,12 @@
  * 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.
+ *
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ *     Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ * 19-Apr-2002 Rob Radez <rob@osinvestor.com>
+ *     Added expect close support, made emulated timeout runtime changeable
+ *     general cleanups, add some ioctls
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -19,7 +25,6 @@
 #include <linux/watchdog.h>
 #include <linux/reboot.h>
 #include <linux/notifier.h>
-#include <linux/smp_lock.h>
 #include <linux/ioport.h>
 
 #include <asm/io.h>
@@ -47,18 +52,55 @@
 #define WTCSR_CKS1	0x02
 #define WTCSR_CKS0	0x01
 
-#define WTCSR_CKS	0x07
-#define WTCSR_CKS_1	0x00
-#define WTCSR_CKS_4	0x01
-#define WTCSR_CKS_16	0x02
-#define WTCSR_CKS_32	0x03
-#define WTCSR_CKS_64	0x04
-#define WTCSR_CKS_256	0x05
-#define WTCSR_CKS_1024	0x06
+/*
+ * CKS0-2 supports a number of clock division ratios. At the time the watchdog
+ * is enabled, it defaults to a 41 usec overflow period .. we overload this to
+ * something a little more reasonable, and really can't deal with anything
+ * lower than WTCSR_CKS_1024, else we drop back into the usec range.
+ *
+ * Clock Division Ratio         Overflow Period
+ * --------------------------------------------
+ *     1/32 (initial value)       41 usecs
+ *     1/64                       82 usecs
+ *     1/128                     164 usecs
+ *     1/256                     328 usecs
+ *     1/512                     656 usecs
+ *     1/1024                   1.31 msecs
+ *     1/2048                   2.62 msecs
+ *     1/4096                   5.25 msecs
+ */
+#define WTCSR_CKS_32	0x00
+#define WTCSR_CKS_64	0x01
+#define WTCSR_CKS_128	0x02
+#define WTCSR_CKS_256	0x03
+#define WTCSR_CKS_512	0x04
+#define WTCSR_CKS_1024	0x05
+#define WTCSR_CKS_2048	0x06
 #define WTCSR_CKS_4096	0x07
 
-static int sh_is_open = 0;
+/*
+ * Default clock division ratio is 5.25 msecs. Overload this at module load
+ * time. Any value not in the msec range will default to a timeout of one
+ * jiffy, which exceeds the usec overflow periods.
+ */
+static int clock_division_ratio = WTCSR_CKS_4096;
+
+#define msecs_to_jiffies(msecs)	(jiffies + ((HZ * msecs + 999) / 1000))
+#define next_ping_period(cks)	msecs_to_jiffies(cks - 4)
+
+static unsigned long sh_is_open;
 static struct watchdog_info sh_wdt_info;
+static char shwdt_expect_close;
+
+static struct timer_list timer;
+static unsigned long next_heartbeat;
+static int sh_heartbeat = 30;
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+static int nowayout = 1;
+#else
+static int nowayout = 0;
+#endif
 
 /**
  *	sh_wdt_write_cnt - Write to Counter
@@ -93,6 +135,10 @@
  */
 static void sh_wdt_start(void)
 {
+	timer.expires = next_ping_period(clock_division_ratio);
+	next_heartbeat = jiffies + (sh_heartbeat * HZ);
+	add_timer(&timer);
+
 	sh_wdt_write_csr(WTCSR_WT | WTCSR_CKS_4096);
 	sh_wdt_write_cnt(0);
 	sh_wdt_write_csr((ctrl_inb(WTCSR) | WTCSR_TME));
@@ -105,6 +151,8 @@
  */
 static void sh_wdt_stop(void)
 {
+	del_timer(&timer);
+
 	sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_TME));
 }
 
@@ -117,8 +165,13 @@
  */
 static void sh_wdt_ping(unsigned long data)
 {
-	sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_IOVF));
-	sh_wdt_write_cnt(0);
+	if (time_before(jiffies, next_heartbeat)) {
+		sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_IOVF));
+		sh_wdt_write_cnt(0);
+
+		timer.expires = next_ping_period(clock_division_ratio);
+		add_timer(&timer);
+	}
 }
 
 /**
@@ -131,19 +184,10 @@
  */
 static int sh_wdt_open(struct inode *inode, struct file *file)
 {
-	switch (MINOR(inode->i_rdev)) {
-		case WATCHDOG_MINOR:
-			if (sh_is_open) {
-				return -EBUSY;
-			}
-
-			sh_is_open = 1;
-			sh_wdt_start();
+	if (test_and_set_bit(0, &sh_is_open))
+		return -EBUSY;
 
-			return 0;
-		default:
-			return -ENODEV;
-	}
+	sh_wdt_start();
 
 	return 0;
 }
@@ -158,37 +202,19 @@
  */
 static int sh_wdt_close(struct inode *inode, struct file *file)
 {
-	lock_kernel();
-	
-	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
-#ifndef CONFIG_WATCHDOG_NOWAYOUT
+	if (!nowayout && shwdt_expect_close == 42) {
 		sh_wdt_stop();
-#endif
-		sh_is_open = 0;
+	} else {
+		printk(KERN_CRIT "shwdt: Unexpected close, not stopping watchdog!\n");
+		next_heartbeat = jiffies + (sh_heartbeat * HZ);
 	}
+	clear_bit(0, &sh_is_open);
+	shwdt_expect_close = 0;
 	
-	unlock_kernel();
-
 	return 0;
 }
 
 /**
- * 	sh_wdt_read - Read from Device
- *
- * 	@file: file handle of device
- * 	@buf: buffer to write to
- * 	@count: length of buffer
- * 	@ppos: offset
- *
- * 	Unsupported.
- */
-static ssize_t sh_wdt_read(struct file *file, char *buf,
-			   size_t count, loff_t *ppos)
-{
-	return -EINVAL;
-}
-
-/**
  * 	sh_wdt_write - Write to Device
  *
  * 	@file: file handle of device
@@ -206,11 +232,18 @@
 		return -ESPIPE;
 
 	if (count) {
-		sh_wdt_ping(0);
-		return 1;
+		size_t i;
+
+		shwdt_expect_close = 0;
+
+		for (i = 0; i != count; i++) {
+			if (buf[i] == 'V')
+				shwdt_expect_close = 42;
+		}
+		next_heartbeat = jiffies + (sh_heartbeat * HZ);
 	}
 
-	return 0;
+	return count;
 }
 
 /**
@@ -227,6 +260,8 @@
 static int sh_wdt_ioctl(struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
+	int new_timeout;
+
 	switch (cmd) {
 		case WDIOC_GETSUPPORT:
 			if (copy_to_user((struct watchdog_info *)arg,
@@ -234,20 +269,44 @@
 					  sizeof(sh_wdt_info))) {
 				return -EFAULT;
 			}
-			
+
 			break;
 		case WDIOC_GETSTATUS:
-			if (copy_to_user((int *)arg,
-					 &sh_is_open,
-					 sizeof(int))) {
+		case WDIOC_GETBOOTSTATUS:
+			return put_user(0, (int *)arg);
+		case WDIOC_KEEPALIVE:
+			next_heartbeat = jiffies + (sh_heartbeat * HZ);
+
+			break;
+		case WDIOC_SETTIMEOUT:
+			if (get_user(new_timeout, (int *)arg))
+				return -EFAULT;
+			if (new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */
+				return -EINVAL;
+			sh_heartbeat = new_timeout;
+			next_heartbeat = jiffies + (sh_heartbeat * HZ);
+			/* Fall */
+		case WDIOC_GETTIMEOUT:
+			return put_user(sh_heartbeat, (int *)arg);
+		case WDIOC_SETOPTIONS:
+		{
+			int options, retval = -EINVAL;
+
+			if (get_user(options, (int *)arg))
 				return -EFAULT;
+
+			if (options & WDIOS_DISABLECARD) {
+				sh_wdt_stop();
+				retval = 0;
 			}
 
-			break;
-		case WDIOC_KEEPALIVE:
-			sh_wdt_ping(0);
-			
-			break;
+			if (options & WDIOS_ENABLECARD) {
+				sh_wdt_start();
+				retval = 0;
+			}
+
+			return retval;
+		}
 		default:
 			return -ENOTTY;
 	}
@@ -277,7 +336,7 @@
 
 static struct file_operations sh_wdt_fops = {
 	owner:		THIS_MODULE,
-	read:		sh_wdt_read,
+	llseek:		no_llseek,
 	write:		sh_wdt_write,
 	ioctl:		sh_wdt_ioctl,
 	open:		sh_wdt_open,
@@ -285,9 +344,9 @@
 };
 
 static struct watchdog_info sh_wdt_info = {
-	WDIOF_KEEPALIVEPING,
-	1,
-	"SH WDT",
+	options:		WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+	firmware_version:	0,
+	identity:		"SH WDT",
 };
 
 static struct notifier_block sh_wdt_notifier = {
@@ -297,9 +356,9 @@
 };
 
 static struct miscdevice sh_wdt_miscdev = {
-	WATCHDOG_MINOR,
-	"watchdog",
-	&sh_wdt_fops,
+	minor:		WATCHDOG_MINOR,
+	name:		"watchdog",
+	fops:		&sh_wdt_fops,
 };
 
 /**
@@ -336,6 +395,10 @@
 		return -EINVAL;
 	}
 
+	init_timer(&timer);
+	timer.function = sh_wdt_ping;
+	timer.data = 0;
+
 	return 0;
 }
 
@@ -355,6 +418,11 @@
 
 EXPORT_NO_SYMBOLS;
 
+MODULE_PARM(nowayout,"i");
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
+MODULE_PARM(clock_division_ratio, "i");
+MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
+
 MODULE_AUTHOR("Paul Mundt <lethal@chaoticdreams.org>");
 MODULE_DESCRIPTION("SH 3/4 watchdog driver");
 MODULE_LICENSE("GPL");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)