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 ) + * based on wdt977 driver by Woody Suwalski * * (c) Copyright 1998 Rebel.com (Woody Suwalski ) * @@ -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 - * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT */ #include @@ -23,17 +21,25 @@ #include #include #include +#include +#include +#include +#include #include #include -#include +#include +#include #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;