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 */
2013-08-18 00:58:42 +04:00
# define W83627HF_LD_WDT 0x08
static void superio_outb ( int reg , int val )
{
outb ( reg , WDT_EFER ) ;
outb ( val , WDT_EFDR ) ;
}
static inline int superio_inb ( int reg )
{
outb ( reg , WDT_EFER ) ;
return inb ( WDT_EFDR ) ;
}
static int superio_enter ( void )
2005-04-17 02:20:36 +04:00
{
2013-08-18 00:58:42 +04:00
if ( ! request_muxed_region ( wdt_io , 2 , WATCHDOG_NAME ) )
return - EBUSY ;
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 */
2013-08-18 00:58:42 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2013-08-18 00:58:42 +04:00
static void superio_select ( int ld )
{
superio_outb ( 0x07 , ld ) ;
}
static void superio_exit ( void )
2005-04-17 02:20:36 +04:00
{
outb_p ( 0xAA , WDT_EFER ) ; /* Leave extended function mode */
2013-08-18 00:58:42 +04:00
release_region ( wdt_io , 2 ) ;
2005-04-17 02:20:36 +04:00
}
/* tyan motherboards seem to set F5 to 0x4C ?
* So explicitly init to appropriate value . */
2008-05-19 17:09:18 +04:00
2013-08-18 00:58:42 +04:00
static int w83627hf_init ( struct watchdog_device * wdog )
2005-04-17 02:20:36 +04:00
{
2013-08-18 00:58:42 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
unsigned char t ;
2013-08-18 00:58:42 +04:00
ret = superio_enter ( ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
2013-08-18 00:58:42 +04:00
superio_select ( W83627HF_LD_WDT ) ;
t = superio_inb ( 0x20 ) ; /* check chip version */
2013-08-18 00:58:40 +04:00
if ( t = = 0x82 ) { /* W83627THF */
2013-08-18 00:58:42 +04:00
t = ( superio_inb ( 0x2b ) & 0xf7 ) ;
superio_outb ( 0x2b , t | 0x04 ) ; /* set GPIO3 to WDT0 */
2013-08-18 00:58:40 +04:00
} else if ( t = = 0x88 | | t = = 0xa0 ) { /* W83627EHF / W83627DHG */
2013-08-18 00:58:42 +04:00
t = superio_inb ( 0x2d ) ;
superio_outb ( 0x2d , t & ~ 0x01 ) ; /* set GPIO5 to WDT0 */
2013-08-18 00:58:40 +04:00
}
2013-08-18 00:58:42 +04:00
/* set CR30 bit 0 to activate GPIO2 */
t = superio_inb ( 0x30 ) ;
2013-08-18 00:58:41 +04:00
if ( ! ( t & 0x01 ) )
2013-08-18 00:58:42 +04:00
superio_outb ( 0x30 , t | 0x01 ) ;
2013-08-18 00:58:40 +04:00
2013-08-18 00:58:42 +04:00
t = superio_inb ( 0xF6 ) ;
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 ) ;
2013-08-18 00:58:42 +04:00
superio_outb ( 0xF6 , wdog - > timeout ) ;
2005-08-17 11:06:07 +04:00
}
2007-07-24 14:49:27 +04:00
2013-08-18 00:58:42 +04:00
/* set second mode & disable keyboard turning off watchdog */
t = superio_inb ( 0xF5 ) & ~ 0x0C ;
/* enable the WDTO# output low pulse to the KBRST# pin */
t | = 0x02 ;
superio_outb ( 0xF5 , t ) ;
/* disable keyboard & mouse turning off watchdog */
t = superio_inb ( 0xF7 ) & ~ 0xC0 ;
superio_outb ( 0xF7 , t ) ;
superio_exit ( ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2013-10-29 06:43:57 +04:00
static int wdt_set_time ( unsigned int timeout )
2005-04-17 02:20:36 +04:00
{
2013-08-18 00:58:42 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
2013-08-18 00:58:42 +04:00
ret = superio_enter ( ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
2013-08-18 00:58:42 +04:00
superio_select ( W83627HF_LD_WDT ) ;
superio_outb ( 0xF6 , timeout ) ;
superio_exit ( ) ;
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 ;
2013-08-18 00:58:42 +04:00
int ret ;
2011-09-13 04:28:46 +04:00
2013-08-18 00:58:42 +04:00
ret = superio_enter ( ) ;
if ( ret )
return 0 ;
2011-09-13 04:28:46 +04:00
2013-08-18 00:58:42 +04:00
superio_select ( W83627HF_LD_WDT ) ;
timeleft = superio_inb ( 0xF6 ) ;
superio_exit ( ) ;
2011-09-13 04:28:46 +04:00
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
2013-10-29 06:43:57 +04:00
watchdog_init_timeout ( & wdt_dev , timeout , NULL ) ;
watchdog_set_nowayout ( & wdt_dev , nowayout ) ;
2013-08-18 00:58:42 +04:00
ret = w83627hf_init ( & wdt_dev ) ;
if ( ret ) {
pr_err ( " failed to initialize watchdog (err=%d) \n " , ret ) ;
return ret ;
}
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 ) ;
2013-08-18 00:58:42 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
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 ) ;
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 ) ;
}
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 " ) ;