lkml.org 
[lkml]   [2007]   [Apr]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[2.4] Watchdog wdt977 (Winbond W83977EF) driver
    Hello,

    This is my first submitted kernel patch, please be gentle.

    Tested and working on AAEON GENE-6310B Subcompact Board
    (also configured for same by default, should work elsewhere)
    patch is against kernel 2.4.34.2

    Changes/Features:

    Added ioctl support
    Disables watchdog on driver load
    Supports timeout in seconds
    Timeout defaults to 2 minutes
    No longer under NetWinder arch
    Configurable output GP (defaults to GP16)
    Configurable base IO address
    Non standard read only proc interface for status (/proc/watchdog)

    Caveats:

    No idea if this breaks netwinder, although it really shouldn't
    Only tested with GP16
    Utterly ignores inability to get its IO port, mostly because it's
    already taken. I didn't know a way around that.
    release_region is called regardless of having acquired the region, this
    might be trouble.

    --
    Tal Kelrich
    PGP fingerprint: 3EDF FCC5 60BB 4729 AB2F CAE6 FEC1 9AAC 12B9 AA69
    Key Available at: http://www.hasturkun.com/pub.txt
    ----
    To err is human, to forgive is against company policy.
    ----

    diff -udr linux-2.4.34.2/drivers/char/Config.in linux-2.4.34.2-wdt977/drivers/char/Config.in
    --- linux-2.4.34.2/drivers/char/Config.in Sat Mar 24 08:44:54 2007
    +++ linux-2.4.34.2-wdt977/drivers/char/Config.in Wed Mar 28 10:49:02 2007
    @@ -247,10 +247,8 @@
    tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
    if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
    tristate ' DC21285 watchdog' CONFIG_21285_WATCHDOG
    - if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then
    - tristate ' NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG
    - fi
    fi
    + tristate ' Winbond W83977EF Watchdog Timer' CONFIG_977_WATCHDOG
    tristate ' Eurotech CPU-1220/1410 Watchdog Timer' CONFIG_EUROTECH_WDT
    tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT
    tristate ' ICP ELectronics Wafer 5823 Watchdog' CONFIG_WAFER_WDT
    --- linux-2.4.34.2/drivers/char/wdt977.c Sat Mar 24 08:44:54 2007
    +++ linux-2.4.34.2-wdt977/drivers/char/wdt977.c Wed Mar 28 10:52:23 2007
    @@ -1,5 +1,7 @@
    /*
    - * Wdt977 0.02: A Watchdog Device for Netwinder W83977AF chip
    + * Wdt83977 0.03: A Watchdog Device for Winbond W83977EF chip
    + * (c) Copyright 2007 Orpak Systems Ltd. (Tal Kelrich <tal@orpak.com>)
    + * based on wdt977 driver by Woody Suwalski <woody@netwinder.org>
    *
    * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>)
    *
    @@ -9,10 +11,6 @@
    * 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.
    - *
    - * -----------------------
    - * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
    - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
    */

    #include <linux/module.h>
    @@ -23,17 +21,25 @@
    #include <linux/miscdevice.h>
    #include <linux/init.h>
    #include <linux/smp_lock.h>
    +#include <linux/ioport.h>
    +#include <linux/spinlock.h>
    +#include <linux/watchdog.h>
    +#include <linux/proc_fs.h>

    #include <asm/io.h>
    #include <asm/system.h>
    -#include <asm/mach-types.h>
    +#include <asm/uaccess.h>
    +#include <asm/page.h>

    #define WATCHDOG_MINOR 130

    -static int timeout = 3;
    +static int timeout = 120;
    static int timer_alive;
    -static int testmode;
    static int expect_close = 0;
    +static spinlock_t wdt_lock;
    +
    +/* port is either 0X370 or 0x3F0. there's probably no way to detect this */
    +static int wdt_io = 0x370;

    #ifdef CONFIG_WATCHDOG_NOWAYOUT
    static int nowayout = 1;
    @@ -41,14 +47,77 @@
    static int nowayout = 0;
    #endif

    +static int whichgp = 16;
    +
    +MODULE_PARM(wdt_io,"i");
    +MODULE_PARM_DESC(wdt_io,"WDT io port base (0x370/0x3F0)");
    +
    +MODULE_PARM(whichgp,"i");
    +MODULE_PARM_DESC(whichgp,"which gp? (12/13/16)");
    +
    +MODULE_PARM(timeout,"i");
    +
    MODULE_PARM(nowayout,"i");
    MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");

    +#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
    +#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register (same as EFER) */
    +#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
    +
    +#define WDT_OUT(reg,data) {outb_p(reg,WDT_EFIR);outb_p(data,WDT_EFDR);}
    +#define WDT_IN(reg,out) {outb_p(reg,WDT_EFIR);out=inb_p(WDT_EFDR);}
    +#define WDT_DEV(device) {WDT_OUT(0x07,device);}
    +#define WDT_ENABLE {outb_p(0x87,WDT_EFER);outb_p(0x87,WDT_EFER);}
    +#define WDT_DISABLE {outb_p(0xAA,WDT_EFER);}
    +
    +static int wdt977_readproc(char *page, char **start, off_t off, int count,
    + int *eof, void *data)
    +{
    + int len;
    + unsigned char remaining;
    + unsigned char fired;
    + spin_lock(&wdt_lock);
    + WDT_ENABLE;
    + WDT_DEV(0x08);
    + WDT_IN(0xF2,remaining); /* get remaining time */
    + WDT_IN(0xF4,fired); /* and some nice status bits */
    + /* and clear the bit we care about */
    + WDT_OUT(0xF4,fired&(~0x01));
    + WDT_DISABLE;
    + spin_unlock(&wdt_lock);
    + fired=fired & 0x01;
    + len=snprintf(page,PAGE_SIZE,
    + "W83977EF device\n"
    + "active=%d\n"
    + "iobase=%0X\n"
    + "gp=%d\n"
    + "nowayout=%d\n"
    + "timeout=%d\n"
    + "remaining=%d\n"
    + "fired=%d\n",
    + timer_alive,wdt_io,whichgp,nowayout,timeout,remaining,fired);
    + *eof=1;
    + return len;
    +}
    +
    +static void wdt977_ctrl(int timeout)
    +{
    + unsigned char status;
    + spin_lock(&wdt_lock);
    + WDT_ENABLE;
    + WDT_DEV(0x08);
    + WDT_OUT(0x30,0x01); /* enable */
    + WDT_OUT(0xF2,timeout);
    + WDT_IN(0xF4, status); /* so we don't set bit 0 */
    + WDT_OUT(0xF4,(status&0x01)|0x40); /* and seconds! */
    + WDT_DISABLE;
    + spin_unlock(&wdt_lock);
    +}

    /*
    * Allow only one person to hold it open
    */
    -
    +
    static int wdt977_open(struct inode *inode, struct file *file)
    {
    if(timer_alive)
    @@ -59,42 +128,12 @@
    timer_alive++;

    //max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog.
    - if (timeout>255)
    + if (timeout>255 || timeout <1)
    timeout = 255;

    printk(KERN_INFO "Watchdog: active, current timeout %d min.\n",timeout);

    - // unlock the SuperIO chip
    - outb(0x87,0x370);
    - outb(0x87,0x370);
    -
    - //select device Aux2 (device=8) and set watchdog regs F2, F3 and F4
    - //F2 has the timeout in minutes
    - //F3 could be set to the POWER LED blink (with GP17 set to PowerLed)
    - // at timeout, and to reset timer on kbd/mouse activity (not now)
    - //F4 is used to just clear the TIMEOUT'ed state (bit 0)
    -
    - outb(0x07,0x370);
    - outb(0x08,0x371);
    - outb(0xF2,0x370);
    - outb(timeout,0x371);
    - outb(0xF3,0x370);
    - outb(0x00,0x371); //another setting is 0E for kbd/mouse/LED
    - outb(0xF4,0x370);
    - outb(0x00,0x371);
    -
    - //at last select device Aux1 (dev=7) and set GP16 as a watchdog output
    - if (!testmode)
    - {
    - outb(0x07,0x370);
    - outb(0x07,0x371);
    - outb(0xE6,0x370);
    - outb(0x08,0x371);
    - }
    -
    - // lock the SuperIO chip
    - outb(0xAA,0x370);
    -
    + wdt977_ctrl(timeout);
    return 0;
    }

    @@ -104,50 +143,23 @@
    * Shut off the timer.
    * Lock it in if it's a module and we set nowayout
    */
    - lock_kernel();
    if (expect_close) {
    -
    - // unlock the SuperIO chip
    - outb(0x87,0x370);
    - outb(0x87,0x370);
    -
    - //select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
    - //F3 is reset to its default state
    - //F4 can clear the TIMEOUT'ed state (bit 0) - back to default
    - //We can not use GP17 as a PowerLed, as we use its usage as a RedLed
    -
    - outb(0x07,0x370);
    - outb(0x08,0x371);
    - outb(0xF2,0x370);
    - outb(0xFF,0x371);
    - outb(0xF3,0x370);
    - outb(0x00,0x371);
    - outb(0xF4,0x370);
    - outb(0x00,0x371);
    - outb(0xF2,0x370);
    - outb(0x00,0x371);
    -
    - //at last select device Aux1 (dev=7) and set GP16 as a watchdog output
    - outb(0x07,0x370);
    - outb(0x07,0x371);
    - outb(0xE6,0x370);
    - outb(0x08,0x371);
    -
    - // lock the SuperIO chip
    - outb(0xAA,0x370);
    + wdt977_ctrl(0); /* disable timer */
    printk(KERN_INFO "Watchdog: shutdown.\n");
    } else {
    printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
    }

    timer_alive=0;
    - unlock_kernel();

    return 0;
    }

    static ssize_t wdt977_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;
    if (!nowayout) {
    size_t i;

    @@ -170,36 +182,79 @@
    /*
    * Refresh the timer.
    */
    -
    - //we have a hw bug somewhere, so each 977 minute is actually only 30sec
    - //as such limit the max timeout to half of max of 255 minutes...
    -// if (timeout>126)
    -// timeout = 126;
    -
    - // unlock the SuperIO chip
    - outb(0x87,0x370);
    - outb(0x87,0x370);
    -
    - //select device Aux2 (device=8) and kicks watchdog reg F2
    - //F2 has the timeout in minutes
    -
    - outb(0x07,0x370);
    - outb(0x08,0x371);
    - outb(0xF2,0x370);
    - outb(timeout,0x371);
    -
    - // lock the SuperIO chip
    - outb(0xAA,0x370);
    +
    + wdt977_ctrl(timeout);

    return 1;
    }

    +static int wdt977_ioctl(struct inode *inode, struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + int new_timeout;
    + static struct watchdog_info ident = {
    + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
    + .firmware_version = 1,
    + .identity = "WDT83977 WDT",
    + };
    + void *argp = (void *)arg;
    + int *p = argp;
    + switch (cmd)
    + {
    + case WDIOC_GETSUPPORT:
    + if(copy_to_user(argp, &ident, sizeof(ident)))
    + return -EFAULT;
    + break;
    +
    + case WDIOC_GETSTATUS:
    + return put_user(timer_alive, p);
    +
    + case WDIOC_KEEPALIVE:
    + wdt977_ctrl(timeout);
    +
    + case WDIOC_SETTIMEOUT:
    + if(get_user(new_timeout, p))
    + return -EFAULT;
    + if(new_timeout>255 || new_timeout < 1)
    + return -EINVAL;
    + timeout=new_timeout;
    + wdt977_ctrl(timeout);
    + /* FALLTHROUGH */
    +
    + case WDIOC_GETTIMEOUT:
    + return put_user(timeout,p);
    +
    + case WDIOC_SETOPTIONS:
    + {
    + int options, retval = -EINVAL;
    + if(get_user(options, p))
    + return -EFAULT;
    +
    + if(options & WDIOS_DISABLECARD)
    + {
    + wdt977_ctrl(0);
    + retval = 0;
    + }
    + if(options & WDIOS_ENABLECARD)
    + {
    + wdt977_ctrl(timeout);
    + retval = 0;
    + }
    + return retval;
    + }
    + default:
    + return -ENOTTY;
    + }
    + return 0;
    +}
    +
    static struct file_operations wdt977_fops=
    {
    owner: THIS_MODULE,
    write: wdt977_write,
    open: wdt977_open,
    release: wdt977_release,
    + ioctl: wdt977_ioctl,
    };

    static struct miscdevice wdt977_miscdev=
    @@ -211,17 +266,80 @@

    static int __init nwwatchdog_init(void)
    {
    - if (!machine_is_netwinder())
    - return -ENODEV;
    -
    - misc_register(&wdt977_miscdev);
    - printk(KERN_INFO "NetWinder Watchdog sleeping.\n");
    + unsigned char status;
    + int ret;
    + spin_lock_init(&wdt_lock);
    + ret = misc_register(&wdt977_miscdev);
    + if(ret != 0)
    + goto out;
    + if(!request_region(wdt_io, 1, "W83977EF"))
    + {
    + /* Failure to get region is because on the board I was working on, wdt_io is
    + * consistently in use. there's probably a better solution to this */
    + printk(KERN_WARNING "W83977EF: IO address %04X already in use, continuing anyway\n",wdt_io);
    + }
    + if(!create_proc_read_entry("watchdog",0,NULL,&wdt977_readproc,NULL))
    + goto unreg_misc;
    + WDT_ENABLE;
    + /* select dev 7 */
    + WDT_DEV(0x07);
    + /* activate it */
    + WDT_OUT(0x30,0x01);
    + switch(whichgp)
    + {
    + case 12:
    + ret=0xE2;
    + break;
    + case 13:
    + ret=0xE3;
    + break;
    + default:
    + whichgp=16;
    + case 16:
    + ret=0xE6;
    + break;
    + }
    + WDT_OUT(ret,0x0A);
    + /* select dev 8 */
    + WDT_DEV(0x08);
    + /* enable it */
    + WDT_OUT(0x30,0x01);
    + /* power led cycle on wdog timeout */
    + WDT_OUT(0xF3,0x08);
    + switch(whichgp)
    + {
    + case 12:
    + WDT_IN(0x2A,ret); /* sets ret */
    + WDT_OUT(0x2A,ret|0x80); /* sets GP12 on */
    + break;
    + case 13:
    + WDT_IN(0x2B,ret);
    + WDT_OUT(0x2B,ret|0x01);
    + break;
    + case 16:
    + WDT_IN(0x2C,ret);
    + WDT_OUT(0x2C,ret|0x10);
    + break;
    + }
    + /* set timer to 0 initially */
    + WDT_OUT(0xF2,0);
    + WDT_IN(0xF4, status); /* so we don't set bit 0 by accident */
    + WDT_OUT(0xF4,(status&0x01)|0x40); /* bit 6 for seconds! */
    + WDT_DISABLE;
    + printk(KERN_INFO "W83977EF: initialized, using port %x, GP%d. timeout=%d nowayout=%d\n",wdt_io,whichgp,timeout,nowayout);
    return 0;
    +unreg_misc:
    + misc_deregister(&wdt977_miscdev);
    +out:
    + return ret;
    }

    static void __exit nwwatchdog_exit(void)
    {
    + remove_proc_entry("watchdog",NULL);
    misc_deregister(&wdt977_miscdev);
    + release_region(wdt_io,2);
    + /* we might not have gotten it... is this safe? */
    }

    EXPORT_NO_SYMBOLS;
    \
     
     \ /
      Last update: 2007-04-01 16:19    [W:0.049 / U:0.020 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site