2005-04-17 02:20:36 +04:00
/*
2007-03-25 19:34:39 +04:00
* w83627hf / thf WDT driver
*
2013-10-29 06:43:57 +04:00
* ( c ) Copyright 2013 Guenter Roeck
* converted to watchdog infrastructure
*
2007-03-25 19:34:39 +04:00
* ( c ) Copyright 2007 Vlad Drukker < vlad @ storewiz . com >
* added support for W83627THF .
2005-04-17 02:20:36 +04:00
*
2011-12-30 02:09:01 +04:00
* ( c ) Copyright 2003 , 2007 Pádraig Brady < P @ draigBrady . com >
2005-04-17 02:20:36 +04:00
*
* Based on advantechwdt . c which is based on wdt . c .
* Original copyright messages :
*
* ( c ) Copyright 2000 - 2001 Marek Michalkiewicz < marekm @ linux . org . pl >
*
2008-10-27 18:17:56 +03:00
* ( c ) Copyright 1996 Alan Cox < alan @ lxorguk . ukuu . org . uk > ,
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
* 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 .
*
* 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 .
*
2008-10-27 18:17:56 +03:00
* ( c ) Copyright 1995 Alan Cox < alan @ lxorguk . ukuu . org . uk >
2005-04-17 02:20:36 +04:00
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/watchdog.h>
# include <linux/ioport.h>
# include <linux/notifier.h>
# include <linux/reboot.h>
# include <linux/init.h>
2008-05-19 17:09:18 +04:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
2010-11-11 18:08:41 +03:00
# define WATCHDOG_NAME "w83627hf / thf / hg / dhg WDT"
2005-04-17 02:20:36 +04:00
# define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
/* You must set this - there is no sane way to probe for this board. */
static int wdt_io = 0x2E ;
module_param ( wdt_io , int , 0 ) ;
2007-03-25 19:34:39 +04:00
MODULE_PARM_DESC ( wdt_io , " w83627hf/thf WDT io port (default 0x2E) " ) ;
2005-04-17 02:20:36 +04:00
2013-10-29 06:43:57 +04:00
static int timeout ; /* in seconds */
2005-04-17 02:20:36 +04:00
module_param ( timeout , int , 0 ) ;
2008-05-19 17:09:18 +04:00
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in seconds. 1 <= timeout <= 255, default= "
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " . " ) ;
2005-04-17 02:20:36 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:09:18 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2005-04-17 02:20:36 +04:00
/*
* Kernel methods .
*/
# define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
2008-05-19 17:09:18 +04:00
# define WDT_EFIR (wdt_io+0) / * Extended Function Index Register
( same as EFER ) */
2005-04-17 02:20:36 +04:00
# define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
2008-05-19 17:09:18 +04:00
static void w83627hf_select_wd_register ( void )
2005-04-17 02:20:36 +04:00
{
2007-03-25 19:34:39 +04:00
unsigned char c ;
2005-04-17 02:20:36 +04:00
outb_p ( 0x87 , WDT_EFER ) ; /* Enter extended function mode */
outb_p ( 0x87 , WDT_EFER ) ; /* Again according to manual */
2007-07-24 14:49:27 +04:00
outb ( 0x20 , WDT_EFER ) ; /* check chip version */
2007-03-25 19:34:39 +04:00
c = inb ( WDT_EFDR ) ;
2007-07-24 14:49:27 +04:00
if ( c = = 0x82 ) { /* W83627THF */
2007-03-25 19:34:39 +04:00
outb_p ( 0x2b , WDT_EFER ) ; /* select GPIO3 */
c = ( ( inb_p ( WDT_EFDR ) & 0xf7 ) | 0x04 ) ; /* select WDT0 */
outb_p ( 0x2b , WDT_EFER ) ;
outb_p ( c , WDT_EFDR ) ; /* set GPIO3 to WDT0 */
2010-11-11 18:08:41 +03:00
} else if ( c = = 0x88 | | c = = 0xa0 ) { /* W83627EHF / W83627DHG */
2009-06-28 23:20:36 +04:00
outb_p ( 0x2d , WDT_EFER ) ; /* select GPIO5 */
c = inb_p ( WDT_EFDR ) & ~ 0x01 ; /* PIN77 -> WDT0# */
outb_p ( 0x2d , WDT_EFER ) ;
outb_p ( c , WDT_EFDR ) ; /* set GPIO5 to WDT0 */
2007-03-25 19:34:39 +04:00
}
2005-04-17 02:20:36 +04:00
outb_p ( 0x07 , WDT_EFER ) ; /* point to logical device number reg */
outb_p ( 0x08 , WDT_EFDR ) ; /* select logical device 8 (GPIO2) */
outb_p ( 0x30 , WDT_EFER ) ; /* select CR30 */
outb_p ( 0x01 , WDT_EFDR ) ; /* set bit 0 to activate GPIO2 */
}
2008-05-19 17:09:18 +04:00
static void w83627hf_unselect_wd_register ( void )
2005-04-17 02:20:36 +04:00
{
outb_p ( 0xAA , WDT_EFER ) ; /* Leave extended function mode */
}
/* tyan motherboards seem to set F5 to 0x4C ?
* So explicitly init to appropriate value . */
2008-05-19 17:09:18 +04:00
2013-10-29 06:43:57 +04:00
static void w83627hf_init ( struct watchdog_device * wdog )
2005-04-17 02:20:36 +04:00
{
unsigned char t ;
w83627hf_select_wd_register ( ) ;
2005-08-17 11:06:07 +04:00
outb_p ( 0xF6 , WDT_EFER ) ; /* Select CRF6 */
2008-05-19 17:09:18 +04:00
t = inb_p ( WDT_EFDR ) ; /* read CRF6 */
2005-08-17 11:06:07 +04:00
if ( t ! = 0 ) {
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog already running. Resetting timeout to %d sec \n " ,
2013-10-29 06:43:57 +04:00
wdog - > timeout ) ;
outb_p ( wdog - > timeout , WDT_EFDR ) ; /* Write back to CRF6 */
2005-08-17 11:06:07 +04:00
}
2007-07-24 14:49:27 +04:00
2005-04-17 02:20:36 +04:00
outb_p ( 0xF5 , WDT_EFER ) ; /* Select CRF5 */
2008-05-19 17:09:18 +04:00
t = inb_p ( WDT_EFDR ) ; /* read CRF5 */
t & = ~ 0x0C ; /* set second mode & disable keyboard
turning off watchdog */
2010-12-06 23:24:56 +03:00
t | = 0x02 ; /* enable the WDTO# output low pulse
to the KBRST # pin ( PIN60 ) */
2005-04-17 02:20:36 +04:00
outb_p ( t , WDT_EFDR ) ; /* Write back to CRF5 */
2007-07-24 14:49:27 +04:00
outb_p ( 0xF7 , WDT_EFER ) ; /* Select CRF7 */
2008-05-19 17:09:18 +04:00
t = inb_p ( WDT_EFDR ) ; /* read CRF7 */
t & = ~ 0xC0 ; /* disable keyboard & mouse turning off
watchdog */
2007-07-24 14:49:27 +04:00
outb_p ( t , WDT_EFDR ) ; /* Write back to CRF7 */
2005-04-17 02:20:36 +04:00
w83627hf_unselect_wd_register ( ) ;
}
2013-10-29 06:43:57 +04:00
static int wdt_set_time ( unsigned int timeout )
2005-04-17 02:20:36 +04:00
{
w83627hf_select_wd_register ( ) ;
outb_p ( 0xF6 , WDT_EFER ) ; /* Select CRF6 */
outb_p ( timeout , WDT_EFDR ) ; /* Write Timeout counter to CRF6 */
w83627hf_unselect_wd_register ( ) ;
2006-09-02 20:50:20 +04:00
2013-10-29 06:43:57 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 06:43:57 +04:00
static int wdt_start ( struct watchdog_device * wdog )
2005-04-17 02:20:36 +04:00
{
2013-10-29 06:43:57 +04:00
return wdt_set_time ( wdog - > timeout ) ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 06:43:57 +04:00
static int wdt_stop ( struct watchdog_device * wdog )
2005-04-17 02:20:36 +04:00
{
2013-10-29 06:43:57 +04:00
return wdt_set_time ( 0 ) ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 06:43:57 +04:00
static int wdt_set_timeout ( struct watchdog_device * wdog , unsigned int timeout )
2005-04-17 02:20:36 +04:00
{
2013-10-29 06:43:57 +04:00
wdog - > timeout = timeout ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2013-10-29 06:43:57 +04:00
static unsigned int wdt_get_time ( struct watchdog_device * wdog )
2011-09-13 04:28:46 +04:00
{
2013-10-29 06:43:57 +04:00
unsigned int timeleft ;
2011-09-13 04:28:46 +04:00
w83627hf_select_wd_register ( ) ;
outb_p ( 0xF6 , WDT_EFER ) ; /* Select CRF6 */
timeleft = inb_p ( WDT_EFDR ) ; /* Read Timeout counter to CRF6 */
w83627hf_unselect_wd_register ( ) ;
return timeleft ;
}
2005-04-17 02:20:36 +04:00
/*
* Notifier for system down
*/
2008-05-19 17:09:18 +04:00
static int wdt_notify_sys ( struct notifier_block * this , unsigned long code ,
2005-04-17 02:20:36 +04:00
void * unused )
{
2008-08-07 00:19:41 +04:00
if ( code = = SYS_DOWN | | code = = SYS_HALT )
2013-10-29 06:43:57 +04:00
wdt_set_time ( 0 ) ; /* Turn the WDT off */
2008-08-07 00:19:41 +04:00
2005-04-17 02:20:36 +04:00
return NOTIFY_DONE ;
}
/*
* Kernel Interfaces
*/
2013-10-29 06:43:57 +04:00
static struct watchdog_info wdt_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. identity = " W83627HF Watchdog " ,
2005-04-17 02:20:36 +04:00
} ;
2013-10-29 06:43:57 +04:00
static struct watchdog_ops wdt_ops = {
. owner = THIS_MODULE ,
. start = wdt_start ,
. stop = wdt_stop ,
. set_timeout = wdt_set_timeout ,
. get_timeleft = wdt_get_time ,
} ;
static struct watchdog_device wdt_dev = {
. info = & wdt_info ,
. ops = & wdt_ops ,
. timeout = WATCHDOG_TIMEOUT ,
. min_timeout = 1 ,
. max_timeout = 255 ,
2005-04-17 02:20:36 +04:00
} ;
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off .
*/
static struct notifier_block wdt_notifier = {
. notifier_call = wdt_notify_sys ,
} ;
2008-05-19 17:09:18 +04:00
static int __init wdt_init ( void )
2005-04-17 02:20:36 +04:00
{
int ret ;
2012-02-16 03:06:19 +04:00
pr_info ( " WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising \n " ) ;
2005-04-17 02:20:36 +04:00
if ( ! request_region ( wdt_io , 1 , WATCHDOG_NAME ) ) {
2012-02-16 03:06:19 +04:00
pr_err ( " I/O address 0x%04x already in use \n " , wdt_io ) ;
2013-10-29 06:43:57 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 06:43:57 +04:00
watchdog_init_timeout ( & wdt_dev , timeout , NULL ) ;
watchdog_set_nowayout ( & wdt_dev , nowayout ) ;
w83627hf_init ( & wdt_dev ) ;
2005-04-17 02:20:36 +04:00
ret = register_reboot_notifier ( & wdt_notifier ) ;
if ( ret ! = 0 ) {
2012-02-16 03:06:19 +04:00
pr_err ( " cannot register reboot notifier (err=%d) \n " , ret ) ;
2005-04-17 02:20:36 +04:00
goto unreg_regions ;
}
2013-10-29 06:43:57 +04:00
ret = watchdog_register_device ( & wdt_dev ) ;
if ( ret )
2005-04-17 02:20:36 +04:00
goto unreg_reboot ;
2012-02-16 03:06:19 +04:00
pr_info ( " initialized. timeout=%d sec (nowayout=%d) \n " ,
2013-10-29 06:43:57 +04:00
wdt_dev . timeout , nowayout ) ;
2005-04-17 02:20:36 +04:00
return ret ;
2013-10-29 06:43:57 +04:00
2005-04-17 02:20:36 +04:00
unreg_reboot :
unregister_reboot_notifier ( & wdt_notifier ) ;
unreg_regions :
release_region ( wdt_io , 1 ) ;
2013-10-29 06:43:57 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 17:09:18 +04:00
static void __exit wdt_exit ( void )
2005-04-17 02:20:36 +04:00
{
2013-10-29 06:43:57 +04:00
watchdog_unregister_device ( & wdt_dev ) ;
2005-04-17 02:20:36 +04:00
unregister_reboot_notifier ( & wdt_notifier ) ;
2008-05-19 17:09:18 +04:00
release_region ( wdt_io , 1 ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( wdt_init ) ;
module_exit ( wdt_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-12-30 02:09:01 +04:00
MODULE_AUTHOR ( " Pádraig Brady <P@draigBrady.com> " ) ;
2007-03-25 19:34:39 +04:00
MODULE_DESCRIPTION ( " w83627hf/thf WDT driver " ) ;