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
- Lines: 332
- Date:
Fri Aug 2 17:39:43 2002
- Orig file:
linux-2.4.18/drivers/char/shwdt.c
- Orig date:
Mon Feb 25 11:37:57 2002
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)