[PATCH] 2.6.0-test4 - Watchdog patches

From: Wim Van Sebroeck (wim_at_iguana.be)
Date: 08/31/03

  • Next message: Linus Torvalds: "Re: [PATCHSET][2.6-test4][0/6]Support for HPET based timer - Take 2"
    Date:	Sun, 31 Aug 2003 22:52:36 +0200
    To: torvalds@osdl.org
    
    

    Hi Linus,

    please do a

            bk pull http://linux-watchdog.bkbits.net/linux-2.5-watchdog

    This will update the following files:

     drivers/char/watchdog/Kconfig | 12
     drivers/char/watchdog/Makefile | 1
     drivers/char/watchdog/acquirewdt.c | 230 ++++++++++-------
     drivers/char/watchdog/advantechwdt.c | 2
     drivers/char/watchdog/alim1535_wdt.c | 465 +++++++++++++++++++++++++++++++++++
     drivers/char/watchdog/wafer5823wdt.c | 181 +++++++++----
     6 files changed, 754 insertions(+), 137 deletions(-)

    through these ChangeSets:

    <wim@iguana.be> (03/08/30 1.1292)
       [WATCHDOG] advantechwdt.c - patch
       
       small clean-up (add trivial comma)

    <wim@iguana.be> (03/08/30 1.1293)
       [WATCHDOG] wafer5823wdt.c - patch
       
       general clean-up (comments, trailing spaces, ...)
       Added WATCHDOG_NAME and PFX defines for easier printk's.
       clean-up printk's.

    <wim@iguana.be> (03/08/30 1.1294)
       [WATCHDOG] wafer5823wdt.c - patch2
       
       fix possible wafwdt_is_open race
       make wdt_stop and wdt_start module params
       change wd_margin to timeout and make it a module_param
       make expect_close the same system as in advantechwdt.c
       clean-up ioctl handling
       added extra printk's to report what problem occured
       add MODULE_DESCRIPTION info

    <wim@iguana.be> (03/08/31 1.1295)
       [WATCHDOG] wafer5823wdt.c - patch3
       
       fix MODULE_PARM_DESC for timeout
       add WDIOC_SETOPTIONS functionality

    <wim@iguana.be> (03/08/31 1.1296)
       [WATCHDOG] acquirewdt.c - patch
       
       clean-up of comments, trailing spaces, includes, ...
       removed unnecessary spinlocking
       added WATCHDOG_NAME + PFX defines for easier printk's
       clean-up expect_close / acq_is_open
       made wdt_stop and wdt_start a module_param
       clean-up ioctl handling
       clean-up init and exit routines
       added MODULE_AUTHOR + MODULE_DESCRIPTION info

    <wim@iguana.be> (03/08/31 1.1297)
       [WATCHDOG] alim1535_wdt.c
       
       Add "ALi M1535 PMU Watchdog Timer" driver

    The ChangeSets can also be looked at on:
            http://linux-watchdog.bkbits.net:8080/linux-2.5-watchdog

    For completeness, I added the patches below.

    Greetings,
    Wim.

    ================================================================================
    diff -Nru a/drivers/char/watchdog/advantechwdt.c b/drivers/char/watchdog/advantechwdt.c
    --- a/drivers/char/watchdog/advantechwdt.c Sun Aug 31 22:46:08 2003
    +++ b/drivers/char/watchdog/advantechwdt.c Sun Aug 31 22:46:08 2003
    @@ -133,7 +133,7 @@
             static struct watchdog_info ident = {
                     .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
                     .firmware_version = 1,
    - .identity = "Advantech WDT"
    + .identity = "Advantech WDT",
             };
     
             switch (cmd) {
    diff -Nru a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c
    --- a/drivers/char/watchdog/wafer5823wdt.c Sun Aug 31 22:46:28 2003
    +++ b/drivers/char/watchdog/wafer5823wdt.c Sun Aug 31 22:46:28 2003
    @@ -1,5 +1,5 @@
     /*
    - * ICP Wafer 5823 Single Board Computer WDT driver for Linux 2.4.x
    + * ICP Wafer 5823 Single Board Computer WDT driver
      * http://www.icpamerica.com/wafer_5823.php
      * May also work on other similar models
      *
    @@ -17,10 +17,10 @@
      * 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.
    + *
    + * 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.
      *
      * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
      *
    @@ -39,6 +39,10 @@
     #include <asm/io.h>
     #include <asm/uaccess.h>
     
    +#define WATCHDOG_NAME "Wafer 5823 WDT"
    +#define PFX WATCHDOG_NAME ": "
    +#define WD_TIMO 60 /* 60 sec default timeout */
    +
     static unsigned long wafwdt_is_open;
     static spinlock_t wafwdt_lock;
     static int expect_close = 0;
    @@ -55,7 +59,6 @@
     #define WDT_START 0x443
     #define WDT_STOP 0x843
     
    -#define WD_TIMO 60 /* 1 minute */
     static int wd_margin = WD_TIMO;
     
     #ifdef CONFIG_WATCHDOG_NOWAYOUT
    @@ -124,7 +127,7 @@
             static struct watchdog_info ident = {
                     .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
                     .firmware_version = 1,
    - .identity = "Wafer 5823 WDT"
    + .identity = "Wafer 5823 WDT",
             };
             int one=1;
     
    @@ -174,10 +177,10 @@
     wafwdt_close(struct inode *inode, struct file *file)
     {
             clear_bit(0, &wafwdt_is_open);
    - if (expect_close) {
    + if (expect_close) {
                     wafwdt_stop();
             } else {
    - printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
    + printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n");
             }
             return 0;
     }
    @@ -201,6 +204,7 @@
     
     static struct file_operations wafwdt_fops = {
             .owner = THIS_MODULE,
    + .llseek = no_llseek,
             .write = wafwdt_write,
             .ioctl = wafwdt_ioctl,
             .open = wafwdt_open,
    @@ -210,23 +214,23 @@
     static struct miscdevice wafwdt_miscdev = {
             .minor = WATCHDOG_MINOR,
             .name = "watchdog",
    - .fops = &wafwdt_fops
    + .fops = &wafwdt_fops,
     };
     
     /*
      * The WDT needs to learn about soft shutdowns in order to
    - * turn the timebomb registers off.
    + * turn the timebomb registers off.
      */
     
     static struct notifier_block wafwdt_notifier = {
             .notifier_call = wafwdt_notify_sys,
             .next = NULL,
    - .priority = 0
    + .priority = 0,
     };
     
     static int __init wafwdt_init(void)
     {
    - printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n");
    + printk(KERN_INFO PFX "WDT driver for Wafer 5823 single board computer initialising.\n");
     
             spin_lock_init(&wafwdt_lock);
             if(!request_region(WDT_STOP, 1, "Wafer 5823 WDT"))
    diff -Nru a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c
    --- a/drivers/char/watchdog/wafer5823wdt.c Sun Aug 31 22:46:47 2003
    +++ b/drivers/char/watchdog/wafer5823wdt.c Sun Aug 31 22:46:47 2003
    @@ -44,8 +44,8 @@
     #define WD_TIMO 60 /* 60 sec default timeout */
     
     static unsigned long wafwdt_is_open;
    +static char expect_close;
     static spinlock_t wafwdt_lock;
    -static int expect_close = 0;
     
     /*
      * You must set these - there is no sane way to probe for this board.
    @@ -56,10 +56,17 @@
      * to restart it again.
      */
     
    -#define WDT_START 0x443
    -#define WDT_STOP 0x843
    -
    -static int wd_margin = WD_TIMO;
    +static int wdt_stop = 0x843;
    +module_param(wdt_stop, int, 0);
    +MODULE_PARM_DESC(wdt_stop, "Wafer 5823 WDT 'stop' io port (default 0x843)");
    +
    +static int wdt_start = 0x443;
    +module_param(wdt_start, int, 0);
    +MODULE_PARM_DESC(wdt_start, "Wafer 5823 WDT 'start' io port (default 0x443)");
    +
    +static int timeout = WD_TIMO; /* in seconds */
    +module_param(timeout, int, 0);
    +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
     
     #ifdef CONFIG_WATCHDOG_NOWAYOUT
     static int nowayout = 1;
    @@ -73,24 +80,24 @@
     static void wafwdt_ping(void)
     {
             /* pat watchdog */
    - spin_lock(&wafwdt_lock);
    - inb_p(WDT_STOP);
    - inb_p(WDT_START);
    - spin_unlock(&wafwdt_lock);
    + spin_lock(&wafwdt_lock);
    + inb_p(wdt_stop);
    + inb_p(wdt_start);
    + spin_unlock(&wafwdt_lock);
     }
     
     static void wafwdt_start(void)
     {
             /* start up watchdog */
    - outb_p(wd_margin, WDT_START);
    - inb_p(WDT_START);
    + outb_p(timeout, wdt_start);
    + inb_p(wdt_start);
     }
     
     static void
     wafwdt_stop(void)
     {
             /* stop watchdog */
    - inb_p(WDT_STOP);
    + inb_p(wdt_stop);
     }
     
     static ssize_t wafwdt_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
    @@ -99,6 +106,7 @@
             if (ppos != &file->f_pos)
                     return -ESPIPE;
     
    + /* See if we got the magic character 'V' and reload the timer */
             if (count) {
                     if (!nowayout) {
                             size_t i;
    @@ -106,30 +114,30 @@
                             /* In case it was set long ago */
                             expect_close = 0;
     
    + /* scan to see wether or not we got the magic character */
                             for (i = 0; i != count; i++) {
                                     char c;
                                     if (get_user(c, buf + i))
                                             return -EFAULT;
                                     if (c == 'V')
    - expect_close = 1;
    + expect_close = 42;
                             }
                     }
    + /* Well, anyhow someone wrote to us, we should return that favour */
                     wafwdt_ping();
    - return 1;
             }
    - return 0;
    + return count;
     }
     
     static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                  unsigned long arg)
     {
    - int new_margin;
    + int new_timeout;
             static struct watchdog_info ident = {
                     .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
                     .firmware_version = 1,
                     .identity = "Wafer 5823 WDT",
             };
    - int one=1;
     
             switch (cmd) {
             case WDIOC_GETSUPPORT:
    @@ -139,25 +147,24 @@
                     break;
     
             case WDIOC_GETSTATUS:
    - if (copy_to_user((int *) arg, &one, sizeof (int)))
    - return -EFAULT;
    - break;
    + case WDIOC_GETBOOTSTATUS:
    + return put_user(0, (int *)arg);
     
             case WDIOC_KEEPALIVE:
                     wafwdt_ping();
                     break;
     
             case WDIOC_SETTIMEOUT:
    - if (get_user(new_margin, (int *)arg))
    + if (get_user(new_timeout, (int *)arg))
                             return -EFAULT;
    - if ((new_margin < 1) || (new_margin > 255))
    + if ((new_timeout < 1) || (new_timeout > 255))
                             return -EINVAL;
    - wd_margin = new_margin;
    + timeout = new_timeout;
                     wafwdt_stop();
                     wafwdt_start();
                     /* Fall */
             case WDIOC_GETTIMEOUT:
    - return put_user(wd_margin, (int *)arg);
    + return put_user(timeout, (int *)arg);
     
             default:
                     return -ENOTTY;
    @@ -169,6 +176,10 @@
     {
             if (test_and_set_bit(0, &wafwdt_is_open))
                     return -EBUSY;
    +
    + /*
    + * Activate
    + */
             wafwdt_start();
             return 0;
     }
    @@ -176,12 +187,14 @@
     static int
     wafwdt_close(struct inode *inode, struct file *file)
     {
    - clear_bit(0, &wafwdt_is_open);
    - if (expect_close) {
    + if (expect_close == 42) {
                     wafwdt_stop();
             } else {
                     printk(KERN_CRIT PFX "WDT device closed unexpectedly. WDT will not stop!\n");
    + wafwdt_ping();
             }
    + clear_bit(0, &wafwdt_is_open);
    + expect_close = 0;
             return 0;
     }
     
    @@ -230,37 +243,77 @@
     
     static int __init wafwdt_init(void)
     {
    - printk(KERN_INFO PFX "WDT driver for Wafer 5823 single board computer initialising.\n");
    + int ret;
    +
    + printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n");
     
             spin_lock_init(&wafwdt_lock);
    - if(!request_region(WDT_STOP, 1, "Wafer 5823 WDT"))
    - goto error;
    - if(!request_region(WDT_START, 1, "Wafer 5823 WDT"))
    +
    + if (timeout < 1 || timeout > 63) {
    + timeout = WD_TIMO;
    + printk (KERN_INFO PFX "timeout value must be 1<=x<=255, using %d\n",
    + timeout);
    + }
    +
    + if (wdt_stop != wdt_start) {
    + if(!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
    + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
    + wdt_stop);
    + ret = -EIO;
    + goto error;
    + }
    + }
    +
    + if(!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
    + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
    + wdt_start);
    + ret = -EIO;
                     goto error2;
    - if(misc_register(&wafwdt_miscdev)<0)
    + }
    +
    + ret = register_reboot_notifier(&wafwdt_notifier);
    + if (ret != 0) {
    + printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
    + ret);
                     goto error3;
    - register_reboot_notifier(&wafwdt_notifier);
    - return 0;
    + }
    +
    + ret = misc_register(&wafwdt_miscdev);
    + if (ret != 0) {
    + printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
    + WATCHDOG_MINOR, ret);
    + goto error4;
    + }
    +
    + printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
    + timeout, nowayout);
    +
    + return ret;
    +error4:
    + unregister_reboot_notifier(&wafwdt_notifier);
     error3:
    - release_region(WDT_START, 1);
    + release_region(wdt_start, 1);
     error2:
    - release_region(WDT_STOP, 1);
    + if (wdt_stop != wdt_start)
    + release_region(wdt_stop, 1);
     error:
    - return -ENODEV;
    + return ret;
     }
     
     static void __exit wafwdt_exit(void)
     {
             misc_deregister(&wafwdt_miscdev);
             unregister_reboot_notifier(&wafwdt_notifier);
    - release_region(WDT_STOP, 1);
    - release_region(WDT_START, 1);
    + if(wdt_stop != wdt_start)
    + release_region(wdt_stop, 1);
    + release_region(wdt_start, 1);
     }
     
     module_init(wafwdt_init);
     module_exit(wafwdt_exit);
     
     MODULE_AUTHOR("Justin Cormack");
    +MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
     MODULE_LICENSE("GPL");
     
     /* end of wafer5823wdt.c */
    diff -Nru a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c
    --- a/drivers/char/watchdog/wafer5823wdt.c Sun Aug 31 22:47:06 2003
    +++ b/drivers/char/watchdog/wafer5823wdt.c Sun Aug 31 22:47:06 2003
    @@ -66,7 +66,7 @@
     
     static int timeout = WD_TIMO; /* in seconds */
     module_param(timeout, int, 0);
    -MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
    +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WD_TIMO) ".");
     
     #ifdef CONFIG_WATCHDOG_NOWAYOUT
     static int nowayout = 1;
    @@ -165,6 +165,26 @@
                     /* Fall */
             case WDIOC_GETTIMEOUT:
                     return put_user(timeout, (int *)arg);
    +
    + case WDIOC_SETOPTIONS:
    + {
    + int options, retval = -EINVAL;
    +
    + if (get_user(options, (int *)arg))
    + return -EFAULT;
    +
    + if (options & WDIOS_DISABLECARD) {
    + wafwdt_start();
    + retval = 0;
    + }
    +
    + if (options & WDIOS_ENABLECARD) {
    + wafwdt_stop();
    + retval = 0;
    + }
    +
    + return retval;
    + }
     
             default:
                     return -ENOTTY;
    diff -Nru a/drivers/char/watchdog/acquirewdt.c b/drivers/char/watchdog/acquirewdt.c
    --- a/drivers/char/watchdog/acquirewdt.c Sun Aug 31 22:47:25 2003
    +++ b/drivers/char/watchdog/acquirewdt.c Sun Aug 31 22:47:25 2003
    @@ -1,5 +1,5 @@
     /*
    - * Acquire Single Board Computer Watchdog Timer driver for Linux 2.1.x
    + * Acquire Single Board Computer Watchdog Timer driver
      *
      * Based on wdt.c. Original copyright messages:
      *
    @@ -10,10 +10,10 @@
      * 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.
    + *
    + * 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.
      *
      * (c) Copyright 1995 Alan Cox <alan@redhat.com>
      *
    @@ -22,33 +22,39 @@
      * Can't add timeout - driver doesn't allow changing value
      */
     
    -#include <linux/config.h>
     #include <linux/module.h>
     #include <linux/moduleparam.h>
     #include <linux/types.h>
     #include <linux/miscdevice.h>
     #include <linux/watchdog.h>
    +#include <linux/fs.h>
     #include <linux/ioport.h>
     #include <linux/notifier.h>
    -#include <linux/fs.h>
     #include <linux/reboot.h>
     #include <linux/init.h>
    -#include <linux/spinlock.h>
     
     #include <asm/io.h>
     #include <asm/uaccess.h>
     #include <asm/system.h>
     
    -static int acq_is_open;
    -static spinlock_t acq_lock;
    -static int expect_close = 0;
    +#define WATCHDOG_NAME "Acquire WDT"
    +#define PFX WATCHDOG_NAME ": "
    +#define WATCHDOG_TIMEOUT 0 /* ??? Is the timeout hardcoded to 1 minute ??? */
    +
    +static unsigned long acq_is_open;
    +static char expect_close;
     
     /*
      * You must set these - there is no sane way to probe for this board.
      */
    -
    -#define WDT_STOP 0x43
    -#define WDT_START 0x443
    +
    +static int wdt_stop = 0x43;
    +module_param(wdt_stop, int, 0);
    +MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)");
    +
    +static int wdt_start = 0x443;
    +module_param(wdt_start, int, 0);
    +MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)");
     
     #ifdef CONFIG_WATCHDOG_NOWAYOUT
     static int nowayout = 1;
    @@ -62,38 +68,52 @@
     /*
      * Kernel methods.
      */
    -
     
     static void acq_ping(void)
     {
             /* Write a watchdog value */
    - inb_p(WDT_START);
    + inb_p(wdt_start);
     }
     
    +static void acq_stop(void)
    +{
    + /* Turn the card off */
    + inb_p(wdt_stop);
    +}
    +
    +/*
    + * /dev/watchdog handling.
    + */
    +
     static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
     {
             /* Can't seek (pwrite) on this device */
             if (ppos != &file->f_pos)
                     return -ESPIPE;
     
    + /* See if we got the magic character 'V' and reload the timer */
             if(count) {
                     if (!nowayout) {
                             size_t i;
     
    + /* note: just in case someone wrote the magic character
    + * five months ago... */
                             expect_close = 0;
     
    + /* scan to see wether or not we got the magic character */
                             for (i = 0; i != count; i++) {
                                     char c;
                                     if (get_user(c, buf + i))
                                             return -EFAULT;
                                     if (c == 'V')
    - expect_close = 1;
    + expect_close = 42;
                             }
                     }
    +
    + /* Well, anyhow someone wrote to us, we should return that favour */
                     acq_ping();
    - return 1;
             }
    - return 0;
    + return count;
     }
     
     static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
    @@ -103,65 +123,75 @@
             {
                     .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
                     .firmware_version = 1,
    - .identity = "Acquire WDT"
    + .identity = "Acquire WDT",
             };
    -
    +
             switch(cmd)
             {
             case WDIOC_GETSUPPORT:
    - if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
    - return -EFAULT;
    - break;
    -
    + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)) ? -EFAULT : 0;
    +
             case WDIOC_GETSTATUS:
    - if (copy_to_user((int *)arg, &acq_is_open, sizeof(int)))
    - return -EFAULT;
    - break;
    + case WDIOC_GETBOOTSTATUS:
    + return put_user(0, (int *)arg);
     
             case WDIOC_KEEPALIVE:
               acq_ping();
    - break;
    + return 0;
    +
    + case WDIOC_GETTIMEOUT:
    + return put_user(WATCHDOG_TIMEOUT, (int *)arg);
    +
    + case WDIOC_SETOPTIONS:
    + {
    + int options, retval = -EINVAL;
    +
    + if (get_user(options, (int *)arg))
    + return -EFAULT;
    +
    + if (options & WDIOS_DISABLECARD)
    + {
    + acq_stop();
    + retval = 0;
    + }
    +
    + if (options & WDIOS_ENABLECARD)
    + {
    + acq_ping();
    + retval = 0;
    + }
    +
    + return retval;
    + }
     
             default:
    - return -ENOTTY;
    + return -ENOIOCTLCMD;
             }
    - return 0;
     }
     
     static int acq_open(struct inode *inode, struct file *file)
     {
    - if ((minor(inode->i_rdev) == WATCHDOG_MINOR)) {
    - spin_lock(&acq_lock);
    - if(acq_is_open) {
    - spin_unlock(&acq_lock);
    - return -EBUSY;
    - }
    - if (nowayout)
    - __module_get(THIS_MODULE);
    + if (test_and_set_bit(0, &acq_is_open))
    + return -EBUSY;
     
    - /* Activate */
    - acq_is_open=1;
    - inb_p(WDT_START);
    - spin_unlock(&acq_lock);
    - return 0;
    + if (nowayout)
    + __module_get(THIS_MODULE);
     
    - } else {
    - return -ENODEV;
    - }
    + /* Activate */
    + acq_ping();
    + return 0;
     }
     
     static int acq_close(struct inode *inode, struct file *file)
     {
    - if(minor(inode->i_rdev)==WATCHDOG_MINOR) {
    - spin_lock(&acq_lock);
    - if (expect_close)
    - inb_p(WDT_STOP);
    - else
    - printk(KERN_CRIT "WDT closed unexpectedly. WDT will not stop!\n");
    -
    - acq_is_open=0;
    - spin_unlock(&acq_lock);
    + if (expect_close == 42) {
    + acq_stop();
    + } else {
    + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
    + acq_ping();
             }
    + clear_bit(0, &acq_is_open);
    + expect_close = 0;
             return 0;
     }
     
    @@ -172,20 +202,20 @@
     static int acq_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_STOP);
    -
    + if(code==SYS_DOWN || code==SYS_HALT) {
    + /* Turn the WDT off */
    + acq_stop();
    + }
             return NOTIFY_DONE;
     }
    -
    +
     /*
      * Kernel Interfaces
      */
    -
    -
    +
     static struct file_operations acq_fops = {
             .owner = THIS_MODULE,
    + .llseek = no_llseek,
             .write = acq_write,
             .ioctl = acq_ioctl,
             .open = acq_open,
    @@ -196,52 +226,84 @@
     {
             .minor = WATCHDOG_MINOR,
             .name = "watchdog",
    - .fops = &acq_fops
    + .fops = &acq_fops,
     };
     
    -
     /*
      * The WDT card needs to learn about soft shutdowns in order to
    - * turn the timebomb registers off.
    + * turn the timebomb registers off.
      */
    -
    +
     static struct notifier_block acq_notifier =
     {
             .notifier_call = acq_notify_sys,
             .next = NULL,
    - .priority = 0
    + .priority = 0,
     };
     
     static int __init acq_init(void)
     {
    + int ret;
    +
             printk(KERN_INFO "WDT driver for Acquire single board computer initialising.\n");
     
    - spin_lock_init(&acq_lock);
    - if (misc_register(&acq_miscdev))
    - return -ENODEV;
    - if (!request_region(WDT_STOP, 1, "Acquire WDT")) {
    - misc_deregister(&acq_miscdev);
    - return -EIO;
    - }
    - if (!request_region(WDT_START, 1, "Acquire WDT")) {
    - release_region(WDT_STOP, 1);
    - misc_deregister(&acq_miscdev);
    - return -EIO;
    + if (wdt_stop != wdt_start) {
    + if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
    + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
    + wdt_stop);
    + ret = -EIO;
    + goto out;
    + }
             }
     
    - register_reboot_notifier(&acq_notifier);
    - return 0;
    + if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
    + printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
    + wdt_start);
    + ret = -EIO;
    + goto unreg_stop;
    + }
    +
    + ret = register_reboot_notifier(&acq_notifier);
    + if (ret != 0) {
    + printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
    + ret);
    + goto unreg_regions;
    + }
    +
    + ret = misc_register(&acq_miscdev);
    + if (ret != 0) {
    + printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
    + WATCHDOG_MINOR, ret);
    + goto unreg_reboot;
    + }
    +
    + printk (KERN_INFO PFX "initialized. (nowayout=%d)\n",
    + nowayout);
    +
    +out:
    + return ret;
    +unreg_reboot:
    + unregister_reboot_notifier(&acq_notifier);
    +unreg_regions:
    + release_region(wdt_start, 1);
    +unreg_stop:
    + if (wdt_stop != wdt_start)
    + release_region(wdt_stop, 1);
    + goto out;
     }
    -
    +
     static void __exit acq_exit(void)
     {
             misc_deregister(&acq_miscdev);
             unregister_reboot_notifier(&acq_notifier);
    - release_region(WDT_STOP,1);
    - release_region(WDT_START,1);
    + if(wdt_stop != wdt_start)
    + release_region(wdt_stop,1);
    + release_region(wdt_start,1);
     }
     
     module_init(acq_init);
     module_exit(acq_exit);
     
     MODULE_LICENSE("GPL");
    +MODULE_AUTHOR("Unkown");
    +MODULE_DESCRIPTION("Acquire Single Board Computer Watchdog Timer driver");
    diff -Nru a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
    --- a/drivers/char/watchdog/Kconfig Sun Aug 31 22:47:44 2003
    +++ b/drivers/char/watchdog/Kconfig Sun Aug 31 22:47:44 2003
    @@ -346,6 +346,18 @@
               module, say M here and read <file:Documentation/modules.txt>. Most
               people will say N.
     
    +config ALIM1535_WDT
    + tristate "ALi M1535 PMU Watchdog Timer"
    + depends on WATCHDOG
    + ---help---
    + This is the driver for the hardware watchdog on the ALi M1535 PMU.
    +
    + This driver is also available as a module ( = code which can be
    + inserted in and removed from the running kernel whenever you want).
    + The module is called alim1535_wdt. If you want to compile it as a
    + module, say M here and read <file:Documentation/modules.txt>. Most
    + people will say N.
    +
     config SC1200_WDT
             tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
             depends on WATCHDOG
    diff -Nru a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
    --- a/drivers/char/watchdog/Makefile Sun Aug 31 22:47:44 2003
    +++ b/drivers/char/watchdog/Makefile Sun Aug 31 22:47:44 2003
    @@ -27,6 +27,7 @@
     obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
     obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
     obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
    +obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
     obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
     obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
     obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
    diff -Nru a/drivers/char/watchdog/alim1535_wdt.c b/drivers/char/watchdog/alim1535_wdt.c
    --- /dev/null Wed Dec 31 16:00:00 1969
    +++ b/drivers/char/watchdog/alim1535_wdt.c Sun Aug 31 22:47:44 2003
    @@ -0,0 +1,465 @@
    +/*
    + * Watchdog for the 7101 PMU version found in the ALi M1535 chipsets
    + *
    + * 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.
    + */
    +
    +#include <linux/module.h>
    +#include <linux/moduleparam.h>
    +#include <linux/types.h>
    +#include <linux/miscdevice.h>
    +#include <linux/watchdog.h>
    +#include <linux/ioport.h>
    +#include <linux/notifier.h>
    +#include <linux/reboot.h>
    +#include <linux/init.h>
    +#include <linux/pci.h>
    +
    +#include <asm/uaccess.h>
    +#include <asm/io.h>
    +
    +#define WATCHDOG_NAME "ALi_M1535"
    +#define PFX WATCHDOG_NAME ": "
    +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
    +
    +/* internal variables */
    +static unsigned long ali_is_open;
    +static char ali_expect_release;
    +static struct pci_dev *ali_pci;
    +static u32 ali_timeout_bits; /* stores the computed timeout */
    +static spinlock_t ali_lock; /* Guards the hardware */
    +
    +/* module parameters */
    +static int timeout = WATCHDOG_TIMEOUT;
    +module_param(timeout, int, 0);
    +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (0<timeout<18000, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
    +
    +#ifdef CONFIG_WATCHDOG_NOWAYOUT
    +static int nowayout = 1;
    +#else
    +static int nowayout = 0;
    +#endif
    +
    +module_param(nowayout, int, 0);
    +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
    +
    +/*
    + * ali_start - start watchdog countdown
    + *
    + * Starts the timer running providing the timer has a counter
    + * configuration set.
    + */
    +
    +static void ali_start(void)
    +{
    + u32 val;
    +
    + spin_lock(&ali_lock);
    +
    + pci_read_config_dword(ali_pci, 0xCC, &val);
    + val &= ~0x3F; /* Mask count */
    + val |= (1<<25) | ali_timeout_bits;
    + pci_write_config_dword(ali_pci, 0xCC, val);
    +
    + spin_unlock(&ali_lock);
    +}
    +
    +/*
    + * ali_stop - stop the timer countdown
    + *
    + * Stop the ALi watchdog countdown
    + */
    +
    +static void ali_stop(void)
    +{
    + u32 val;
    +
    + spin_lock(&ali_lock);
    +
    + pci_read_config_dword(ali_pci, 0xCC, &val);
    + val &= ~0x3F; /* Mask count to zero (disabled) */
    + val &= ~(1<<25);/* and for safety mask the reset enable */
    + pci_write_config_dword(ali_pci, 0xCC, val);
    +
    + spin_unlock(&ali_lock);
    +}
    +
    +/*
    + * ali_keepalive - send a keepalive to the watchdog
    + *
    + * Send a keepalive to the timer (actually we restart the timer).
    + */
    +
    +static void ali_keepalive(void)
    +{
    + ali_start();
    +}
    +
    +/*
    + * ali_settimer - compute the timer reload value
    + * @t: time in seconds
    + *
    + * Computes the timeout values needed
    + */
    +
    +static int ali_settimer(int t)
    +{
    + if(t < 0)
    + return -EINVAL;
    + else if(t < 60)
    + ali_timeout_bits = t|(1<<6);
    + else if(t < 3600)
    + ali_timeout_bits = (t/60)|(1<<7);
    + else if(t < 18000)
    + ali_timeout_bits = (t/300)|(1<<6)|(1<<7);
    + else return -EINVAL;
    +
    + timeout = t;
    + return 0;
    +}
    +
    +/*
    + * /dev/watchdog handling
    + */
    +
    +/*
    + * ali_write - writes to ALi watchdog
    + * @file: file from VFS
    + * @data: user address of data
    + * @len: length of data
    + * @ppos: pointer to the file offset
    + *
    + * Handle a write to the ALi watchdog. Writing to the file pings
    + * the watchdog and resets it. Writing the magic 'V' sequence allows
    + * the next close to turn off the watchdog.
    + */
    +
    +static ssize_t ali_write(struct file *file, const char *data,
    + size_t len, loff_t * ppos)
    +{
    + /* Can't seek (pwrite) on this device */
    + if (ppos != &file->f_pos)
    + return -ESPIPE;
    +
    + /* See if we got the magic character 'V' and reload the timer */
    + if (len) {
    + if (!nowayout) {
    + size_t i;
    +
    + /* note: just in case someone wrote the magic character
    + * five months ago... */
    + ali_expect_release = 0;
    +
    + /* scan to see wether or not we got the magic character */
    + for (i = 0; i != len; i++) {
    + char c;
    + if(get_user(c, data+i))
    + return -EFAULT;
    + if (c == 'V')
    + ali_expect_release = 42;
    + }
    + }
    +
    + /* someone wrote to us, we should reload the timer */
    + ali_start();
    + }
    + return len;
    +}
    +
    +/*
    + * ali_ioctl - handle watchdog ioctls
    + * @inode: VFS inode
    + * @file: VFS file pointer
    + * @cmd: ioctl number
    + * @arg: arguments to the ioctl
    + *
    + * Handle the watchdog ioctls supported by the ALi driver. Really
    + * we want an extension to enable irq ack monitoring and the like
    + */
    +
    +static int ali_ioctl(struct inode *inode, struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + static struct watchdog_info ident = {
    + .options = WDIOF_KEEPALIVEPING |
    + WDIOF_SETTIMEOUT |
    + WDIOF_MAGICCLOSE,
    + .firmware_version = 0,
    + .identity = "ALi M1535 WatchDog Timer",
    + };
    +
    + switch (cmd) {
    + case WDIOC_GETSUPPORT:
    + return copy_to_user((struct watchdog_info *) arg, &ident,
    + sizeof (ident)) ? -EFAULT : 0;
    +
    + case WDIOC_GETSTATUS:
    + case WDIOC_GETBOOTSTATUS:
    + return put_user(0, (int *) arg);
    +
    + case WDIOC_KEEPALIVE:
    + ali_keepalive();
    + return 0;
    +
    + case WDIOC_SETOPTIONS:
    + {
    + int new_options, retval = -EINVAL;
    +
    + if (get_user (new_options, (int *) arg))
    + return -EFAULT;
    +
    + if (new_options & WDIOS_DISABLECARD) {
    + ali_stop();
    + retval = 0;
    + }
    +
    + if (new_options & WDIOS_ENABLECARD) {
    + ali_start();
    + retval = 0;
    + }
    +
    + return retval;
    + }
    +
    + case WDIOC_SETTIMEOUT:
    + {
    + int new_timeout;
    +
    + if (get_user(new_timeout, (int *) arg))
    + return -EFAULT;
    +
    + if (ali_settimer(new_timeout))
    + return -EINVAL;
    +
    + ali_keepalive();
    + /* Fall */
    + }
    +
    + case WDIOC_GETTIMEOUT:
    + return put_user(timeout, (int *)arg);
    +
    + default:
    + return -ENOIOCTLCMD;
    + }
    +}
    +
    +/*
    + * ali_open - handle open of ali watchdog
    + * @inode: inode from VFS
    + * @file: file from VFS
    + *
    + * Open the ALi watchdog device. Ensure only one person opens it
    + * at a time. Also start the watchdog running.
    + */
    +
    +static int ali_open(struct inode *inode, struct file *file)
    +{
    + /* /dev/watchdog can only be opened once */
    + if (test_and_set_bit(0, &ali_is_open))
    + return -EBUSY;
    +
    + /* Activate */
    + ali_start();
    + return 0;
    +}
    +
    +/*
    + * ali_release - close an ALi watchdog
    + * @inode: inode from VFS
    + * @file: file from VFS
    + *
    + * Close the ALi watchdog device. Actual shutdown of the timer
    + * only occurs if the magic sequence has been set.
    + */
    +
    +static int ali_release(struct inode *inode, struct file *file)
    +{
    + /*
    + * Shut off the timer.
    + */
    + if (ali_expect_release == 42) {
    + ali_stop();
    + } else {
    + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
    + ali_keepalive();
    + }
    + clear_bit(0, &ali_is_open);
    + ali_expect_release = 0;
    + return 0;
    +}
    +
    +/*
    + * ali_notify_sys - System down notifier
    + *
    + * Notifier for system down
    + */
    +
    +
    +static int ali_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
    +{
    + if (code==SYS_DOWN || code==SYS_HALT) {
    + /* Turn the WDT off */
    + ali_stop();
    + }
    +
    + return NOTIFY_DONE;
    +}
    +
    +/*
    + * Data for PCI driver interface
    + *
    + * This data only exists for exporting the supported
    + * PCI ids via MODULE_DEVICE_TABLE. We do not actually
    + * register a pci_driver, because someone else might one day
    + * want to register another driver on the same PCI id.
    + */
    +
    +static struct pci_device_id ali_pci_tbl[] __initdata = {
    + { PCI_VENDOR_ID_AL, 1535, PCI_ANY_ID, PCI_ANY_ID,},
    + { 0, },
    +};
    +MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
    +
    +/*
    + * ali_find_watchdog - find a 1535 and 7101
    + *
    + * Scans the PCI hardware for a 1535 series bridge and matching 7101
    + * watchdog device. This may be overtight but it is better to be safe
    + */
    +
    +static int __init ali_find_watchdog(void)
    +{
    + struct pci_dev *pdev;
    + u32 wdog;
    +
    + /* Check for a 1535 series bridge */
    + pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
    + if(pdev == NULL)
    + return -ENODEV;
    +
    + /* Check for the a 7101 PMU */
    + pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
    + if(pdev == NULL)
    + return -ENODEV;
    +
    + if(pci_enable_device(pdev))
    + return -EIO;
    +
    + ali_pci = pdev;
    +
    + /*
    + * Initialize the timer bits
    + */
    + pci_read_config_dword(pdev, 0xCC, &wdog);
    +
    + wdog &= ~0x3F; /* Timer bits */
    + wdog &= ~((1<<27)|(1<<26)|(1<<25)|(1<<24)); /* Issued events */
    + wdog &= ~((1<<16)|(1<<13)|(1<<12)|(1<<11)|(1<<10)|(1<<9)); /* No monitor bits */
    +
    + pci_write_config_dword(pdev, 0xCC, wdog);
    +
    + return 0;
    +}
    +
    +/*
    + * Kernel Interfaces
    + */
    +
    +static struct file_operations ali_fops = {
    + .owner = THIS_MODULE,
    + .llseek = no_llseek,
    + .write = ali_write,
    + .ioctl = ali_ioctl,
    + .open = ali_open,
    + .release = ali_release,
    +};
    +
    +static struct miscdevice ali_miscdev = {
    + .minor = WATCHDOG_MINOR,
    + .name = "watchdog",
    + .fops = &ali_fops,
    +};
    +
    +static struct notifier_block ali_notifier = {
    + .notifier_call = ali_notify_sys,
    + .next = NULL,
    + .priority = 0,
    +};
    +
    +/*
    + * watchdog_init - module initialiser
    + *
    + * Scan for a suitable watchdog and if so initialize it. Return an error
    + * if we cannot, the error causes the module to unload
    + */
    +
    +static int __init watchdog_init(void)
    +{
    + int ret;
    +
    + spin_lock_init(&ali_lock);
    +
    + /* Check wether or not the hardware watchdog is there */
    + if (ali_find_watchdog() != 0) {
    + return -ENODEV;
    + }
    +
    + /* Check that the timeout value is within it's range ; if not reset to the default */
    + if (timeout < 1 || timeout >= 18000) {
    + timeout = WATCHDOG_TIMEOUT;
    + printk(KERN_INFO PFX "timeout value must be 0<timeout<18000, using %d\n",
    + timeout);
    + }
    +
    + /* Calculate the watchdog's timeout */
    + ali_settimer(timeout);
    +
    + ret = misc_register(&ali_miscdev);
    + if (ret != 0) {
    + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
    + WATCHDOG_MINOR, ret);
    + goto out;
    + }
    +
    + ret = register_reboot_notifier(&ali_notifier);
    + if (ret != 0) {
    + printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
    + ret);
    + goto unreg_miscdev;
    + }
    +
    + printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
    + timeout, nowayout);
    +
    +out:
    + return ret;
    +unreg_miscdev:
    + misc_deregister(&ali_miscdev);
    + goto out;
    +}
    +
    +/*
    + * watchdog_exit - module de-initialiser
    + *
    + * Called while unloading a successfully installed watchdog module.
    + */
    +
    +static void __exit watchdog_exit(void)
    +{
    + /* Stop the timer before we leave */
    + ali_stop();
    +
    + /* Deregister */
    + unregister_reboot_notifier(&ali_notifier);
    + misc_deregister(&ali_miscdev);
    +}
    +
    +module_init(watchdog_init);
    +module_exit(watchdog_exit);
    +
    +MODULE_AUTHOR("Alan Cox");
    +MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
    +MODULE_LICENSE("GPL");
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/


  • Next message: Linus Torvalds: "Re: [PATCHSET][2.6-test4][0/6]Support for HPET based timer - Take 2"

    Relevant Pages