Commit 2ac7e7a9 authored by Dave Jones's avatar Dave Jones

[WATCHDOG] Final 2.4 bits for advantechwdt

parent d75013dd
...@@ -13,16 +13,15 @@ ...@@ -13,16 +13,15 @@
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
* warranty for any of this software. This material is provided * warranty for any of this software. This material is provided
* "AS-IS" and at no charge. * "AS-IS" and at no charge.
* *
* (c) Copyright 1995 Alan Cox <alan@redhat.com> * (c) Copyright 1995 Alan Cox <alan@redhat.com>
* *
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
* Added timeout module option to override default
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -40,31 +39,24 @@ ...@@ -40,31 +39,24 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
static int advwdt_is_open; static unsigned long advwdt_is_open;
static char adv_expect_close; static char adv_expect_close;
static spinlock_t advwdt_lock;
/* /*
* You must set these - there is no sane way to probe for this board. * You must set these - there is no sane way to probe for this board.
* *
* To enable or restart, write the timeout value in seconds (1 to 63) * To enable or restart, write the timeout value in seconds (1 to 63)
* to I/O port WDT_START. To disable, read I/O port WDT_STOP. * to I/O port wdt_start. To disable, read I/O port wdt_stop.
* Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but * Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
* check your manual (at least the PCA-6159 seems to be different - * check your manual (at least the PCA-6159 seems to be different -
* the manual says WDT_STOP is 0x43, not 0x443). * the manual says wdt_stop is 0x43, not 0x443).
* (0x43 is also a write-only control register for the 8254 timer!) * (0x43 is also a write-only control register for the 8254 timer!)
*
* TODO: module parameters to set the I/O port addresses
*/ */
#define WDT_STOP 0x443 static int wdt_stop = 0x443;
#define WDT_START 0x443 static int wdt_start = 0x443;
#define WD_TIMO 60 /* 1 minute */ static int wd_margin = 60; /* 60 sec default timeout */
static int timeout = WD_TIMO; /* in seconds */
MODULE_PARM(timeout,"i");
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=60)");
#ifdef CONFIG_WATCHDOG_NOWAYOUT #ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1; static int nowayout = 1;
...@@ -78,12 +70,44 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON ...@@ -78,12 +70,44 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
/* /*
* Kernel methods. * Kernel methods.
*/ */
#ifndef MODULE
static int __init adv_setup(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if(ints[0] > 0){
wdt_stop = ints[1];
if(ints[0] > 1)
wdt_start = ints[2];
}
return 1;
}
__setup("advwdt=", adv_setup);
#endif /* !MODULE */
MODULE_PARM(wdt_stop, "i");
MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
MODULE_PARM(wdt_start, "i");
MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
static void static void
advwdt_ping(void) advwdt_ping(void)
{ {
/* Write a watchdog value */ /* Write a watchdog value */
outb_p(timeout, WDT_START); outb_p(wd_margin, wdt_start);
}
static void
advwdt_disable(void)
{
inb_p(wdt_stop);
} }
static ssize_t static ssize_t
...@@ -112,16 +136,11 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) ...@@ -112,16 +136,11 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return count; return count;
} }
static ssize_t
advwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
return -EINVAL;
}
static int static int
advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
int new_margin;
static struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1, .firmware_version = 1,
...@@ -133,16 +152,47 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -133,16 +152,47 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
return -EFAULT; return -EFAULT;
break; break;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
if (copy_to_user((int *)arg, &advwdt_is_open, sizeof(int))) case WDIOC_GETBOOTSTATUS:
return -EFAULT; return put_user(0, (int *)arg);
break;
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
advwdt_ping(); advwdt_ping();
break; break;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, (int *)arg))
return -EFAULT;
if ((new_margin < 1) || (new_margin > 63))
return -EINVAL;
wd_margin = new_margin;
advwdt_ping();
/* Fall */
case WDIOC_GETTIMEOUT:
return put_user(wd_margin, (int *)arg);
case WDIOC_SETOPTIONS:
{
int options, retval = -EINVAL;
if (get_user(options, (int *)arg))
return -EFAULT;
if (options & WDIOS_DISABLECARD) {
advwdt_disable();
retval = 0;
}
if (options & WDIOS_ENABLECARD) {
advwdt_ping();
retval = 0;
}
return retval;
}
default: default:
return -ENOTTY; return -ENOTTY;
} }
...@@ -152,37 +202,27 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -152,37 +202,27 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int static int
advwdt_open(struct inode *inode, struct file *file) advwdt_open(struct inode *inode, struct file *file)
{ {
if (minor(inode->i_rdev) == WATCHDOG_MINOR) { if (test_and_set_bit(0, &advwdt_is_open))
spin_lock(&advwdt_lock); return -EBUSY;
if (advwdt_is_open) { /*
spin_unlock(&advwdt_lock); * Activate
return -EBUSY; */
}
if (nowayout) advwdt_ping();
MOD_INC_USE_COUNT; return 0;
/* Activate */
advwdt_is_open = 1;
advwdt_ping();
spin_unlock(&advwdt_lock);
return 0;
} else {
return -ENODEV;
}
} }
static int static int
advwdt_close(struct inode *inode, struct file *file) advwdt_close(struct inode *inode, struct file *file)
{ {
if (minor(inode->i_rdev) == WATCHDOG_MINOR) { if (adv_expect_close == 42) {
spin_lock(&advwdt_lock); advwdt_disable();
if (!nowayout) } else {
inb_p(WDT_STOP); printk(KERN_CRIT "advancetechwdt: Unexpected close, not stopping watchdog!\n");
advwdt_ping();
advwdt_is_open = 0;
spin_unlock(&advwdt_lock);
} }
clear_bit(0, &advwdt_is_open);
adv_expect_close = 0;
return 0; return 0;
} }
...@@ -194,10 +234,10 @@ static int ...@@ -194,10 +234,10 @@ static int
advwdt_notify_sys(struct notifier_block *this, unsigned long code, advwdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused) void *unused)
{ {
if (code == SYS_DOWN || code == SYS_HALT) if (code == SYS_DOWN || code == SYS_HALT) {
/* Turn the WDT off */ /* Turn the WDT off */
inb_p(WDT_STOP); advwdt_disable();
}
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -207,7 +247,6 @@ advwdt_notify_sys(struct notifier_block *this, unsigned long code, ...@@ -207,7 +247,6 @@ advwdt_notify_sys(struct notifier_block *this, unsigned long code,
static struct file_operations advwdt_fops = { static struct file_operations advwdt_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = advwdt_read,
.write = advwdt_write, .write = advwdt_write,
.ioctl = advwdt_ioctl, .ioctl = advwdt_ioctl,
.open = advwdt_open, .open = advwdt_open,
...@@ -231,35 +270,22 @@ static struct notifier_block advwdt_notifier = { ...@@ -231,35 +270,22 @@ static struct notifier_block advwdt_notifier = {
.priority = 0 .priority = 0
}; };
static void __init
advwdt_validate_timeout(void)
{
if (timeout < 1 || timeout > 63) {
timeout = WD_TIMO;
printk(KERN_INFO "advantechwdt: timeout value must be 1 <= x <= 63, using %d\n", timeout);
}
}
static int __init static int __init
advwdt_init(void) advwdt_init(void)
{ {
printk("WDT driver for Advantech single board computer initialising.\n"); printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n");
advwdt_validate_timeout();
spin_lock_init(&advwdt_lock);
if (misc_register(&advwdt_miscdev)) if (misc_register(&advwdt_miscdev))
return -ENODEV; return -ENODEV;
#if WDT_START != WDT_STOP if (wdt_stop != wdt_start)
if (!request_region(WDT_STOP, 1, "Advantech WDT")) { if (!request_region(wdt_stop, 1, "Advantech WDT")) {
misc_deregister(&advwdt_miscdev); misc_deregister(&advwdt_miscdev);
return -EIO; return -EIO;
} }
#endif if (!request_region(wdt_start, 1, "Advantech WDT")) {
if (!request_region(WDT_START, 1, "Advantech WDT")) {
misc_deregister(&advwdt_miscdev); misc_deregister(&advwdt_miscdev);
#if WDT_START != WDT_STOP if (wdt_stop != wdt_start)
release_region(WDT_STOP, 1); release_region(wdt_stop, 1);
#endif
return -EIO; return -EIO;
} }
register_reboot_notifier(&advwdt_notifier); register_reboot_notifier(&advwdt_notifier);
...@@ -271,14 +297,15 @@ advwdt_exit(void) ...@@ -271,14 +297,15 @@ advwdt_exit(void)
{ {
misc_deregister(&advwdt_miscdev); misc_deregister(&advwdt_miscdev);
unregister_reboot_notifier(&advwdt_notifier); unregister_reboot_notifier(&advwdt_notifier);
#if WDT_START != WDT_STOP if(wdt_stop != wdt_start)
release_region(WDT_STOP,1); release_region(wdt_stop,1);
#endif release_region(wdt_start,1);
release_region(WDT_START,1);
} }
module_init(advwdt_init); module_init(advwdt_init);
module_exit(advwdt_exit); module_exit(advwdt_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment