2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-03-05 18:48:45 +03:00
/*
* Watchdog Timer Driver
* for ITE IT87xx Environment Control - Low Pin Count Input / Output
*
* ( c ) Copyright 2007 Oliver Schuster < olivers137 @ aol . com >
*
* Based on softdog . c by Alan Cox ,
* 83977f _wdt . c by Jose Goncalves ,
* it87 . c by Chris Gauthron , Jean Delvare
*
* Data - sheets : Publicly available at the ITE website
* http : //www.ite.com.tw/
*
* Support of the watchdog timers , which are available on
2017-06-11 07:04:36 +03:00
* IT8607 , IT8620 , IT8622 , IT8625 , IT8628 , IT8655 , IT8665 , IT8686 ,
* IT8702 , IT8712 , IT8716 , IT8718 , IT8720 , IT8721 , IT8726 , IT8728 ,
2020-09-05 00:16:39 +03:00
* IT8772 , IT8783 and IT8784 .
2008-03-05 18:48:45 +03:00
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2017-06-11 07:04:35 +03:00
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
2008-03-05 18:48:45 +03:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/watchdog.h>
# define WATCHDOG_NAME "IT87 WDT"
/* Defaults for Module Parameter */
2011-02-23 23:04:38 +03:00
# define DEFAULT_TIMEOUT 60
2008-03-05 18:48:45 +03:00
# define DEFAULT_TESTMODE 0
# define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
/* IO Ports */
# define REG 0x2e
# define VAL 0x2f
/* Logical device Numbers LDN */
# define GPIO 0x07
/* Configuration Registers and Functions */
# define LDNREG 0x07
# define CHIPID 0x20
2011-02-23 23:04:38 +03:00
# define CHIPREV 0x22
2008-03-05 18:48:45 +03:00
/* Chip Id numbers */
# define NO_DEV_ID 0xffff
2017-06-11 07:04:36 +03:00
# define IT8607_ID 0x8607
2016-12-16 01:52:36 +03:00
# define IT8620_ID 0x8620
2017-06-11 07:04:36 +03:00
# define IT8622_ID 0x8622
# define IT8625_ID 0x8625
# define IT8628_ID 0x8628
# define IT8655_ID 0x8655
# define IT8665_ID 0x8665
# define IT8686_ID 0x8686
2010-09-14 04:54:16 +04:00
# define IT8702_ID 0x8702
2008-03-05 18:48:45 +03:00
# define IT8705_ID 0x8705
# define IT8712_ID 0x8712
# define IT8716_ID 0x8716
# define IT8718_ID 0x8718
2010-09-14 04:47:28 +04:00
# define IT8720_ID 0x8720
2011-01-21 01:37:51 +03:00
# define IT8721_ID 0x8721
2008-03-05 18:48:45 +03:00
# define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
2012-03-14 23:49:04 +04:00
# define IT8728_ID 0x8728
2020-08-27 13:59:40 +03:00
# define IT8772_ID 0x8772
2014-10-19 23:39:33 +04:00
# define IT8783_ID 0x8783
2020-09-05 00:16:39 +03:00
# define IT8784_ID 0x8784
2020-01-23 17:05:44 +03:00
# define IT8786_ID 0x8786
2008-03-05 18:48:45 +03:00
/* GPIO Configuration Registers LDN=0x07 */
2011-02-23 23:04:38 +03:00
# define WDTCTRL 0x71
2008-03-05 18:48:45 +03:00
# define WDTCFG 0x72
# define WDTVALLSB 0x73
# define WDTVALMSB 0x74
/* GPIO Bits WDTCFG */
# define WDT_TOV1 0x80
# define WDT_KRST 0x40
# define WDT_TOVE 0x20
2011-01-21 01:37:51 +03:00
# define WDT_PWROK 0x10 /* not in it8721 */
2008-03-05 18:48:45 +03:00
# define WDT_INT_MASK 0x0f
2017-06-11 07:04:34 +03:00
static unsigned int max_units , chip_type ;
2008-03-05 18:48:45 +03:00
2017-06-11 07:04:33 +03:00
static unsigned int timeout = DEFAULT_TIMEOUT ;
2017-06-11 07:04:34 +03:00
static int testmode = DEFAULT_TESTMODE ;
static bool nowayout = DEFAULT_NOWAYOUT ;
2008-03-05 18:48:45 +03:00
module_param ( timeout , int , 0 ) ;
MODULE_PARM_DESC ( timeout , " Watchdog timeout in seconds, default= "
__MODULE_STRING ( DEFAULT_TIMEOUT ) ) ;
module_param ( testmode , int , 0 ) ;
MODULE_PARM_DESC ( testmode , " Watchdog test mode (1 = no reboot), default= "
__MODULE_STRING ( DEFAULT_TESTMODE ) ) ;
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2008-03-05 18:48:45 +03:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started, default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) ) ;
/* Superio Chip */
2011-05-09 22:45:07 +04:00
static inline int superio_enter ( void )
2008-03-05 18:48:45 +03:00
{
2011-05-09 22:45:07 +04:00
/*
* Try to reserve REG and REG + 1 for exclusive access .
*/
if ( ! request_muxed_region ( REG , 2 , WATCHDOG_NAME ) )
return - EBUSY ;
2008-03-05 18:48:45 +03:00
outb ( 0x87 , REG ) ;
outb ( 0x01 , REG ) ;
outb ( 0x55 , REG ) ;
outb ( 0x55 , REG ) ;
2011-05-09 22:45:07 +04:00
return 0 ;
2008-03-05 18:48:45 +03:00
}
static inline void superio_exit ( void )
{
outb ( 0x02 , REG ) ;
outb ( 0x02 , VAL ) ;
2011-05-09 22:45:07 +04:00
release_region ( REG , 2 ) ;
2008-03-05 18:48:45 +03:00
}
static inline void superio_select ( int ldn )
{
outb ( LDNREG , REG ) ;
outb ( ldn , VAL ) ;
}
static inline int superio_inb ( int reg )
{
outb ( reg , REG ) ;
return inb ( VAL ) ;
}
static inline void superio_outb ( int val , int reg )
{
2009-03-18 11:35:09 +03:00
outb ( reg , REG ) ;
outb ( val , VAL ) ;
2008-03-05 18:48:45 +03:00
}
static inline int superio_inw ( int reg )
{
int val ;
outb ( reg + + , REG ) ;
val = inb ( VAL ) < < 8 ;
outb ( reg , REG ) ;
val | = inb ( VAL ) ;
return val ;
}
2010-09-14 04:54:16 +04:00
/* Internal function, should be called after superio_select(GPIO) */
2017-06-11 07:04:34 +03:00
static void _wdt_update_timeout ( unsigned int t )
2010-09-14 04:54:16 +04:00
{
2011-01-21 01:37:51 +03:00
unsigned char cfg = WDT_KRST ;
2010-09-14 04:54:16 +04:00
if ( testmode )
cfg = 0 ;
2017-06-11 07:04:34 +03:00
if ( t < = max_units )
2010-09-14 04:54:16 +04:00
cfg | = WDT_TOV1 ;
else
2017-06-11 07:04:34 +03:00
t / = 60 ;
2010-09-14 04:54:16 +04:00
2011-01-21 01:37:51 +03:00
if ( chip_type ! = IT8721_ID )
cfg | = WDT_PWROK ;
2010-09-14 04:54:16 +04:00
superio_outb ( cfg , WDTCFG ) ;
2017-06-11 07:04:34 +03:00
superio_outb ( t , WDTVALLSB ) ;
2010-09-14 04:54:16 +04:00
if ( max_units > 255 )
2017-06-11 07:04:34 +03:00
superio_outb ( t > > 8 , WDTVALMSB ) ;
2010-09-14 04:54:16 +04:00
}
2017-06-11 07:04:34 +03:00
static int wdt_update_timeout ( unsigned int t )
2017-06-11 07:04:33 +03:00
{
int ret ;
ret = superio_enter ( ) ;
if ( ret )
return ret ;
superio_select ( GPIO ) ;
2017-06-11 07:04:34 +03:00
_wdt_update_timeout ( t ) ;
2017-06-11 07:04:33 +03:00
superio_exit ( ) ;
return 0 ;
}
2010-09-14 04:54:16 +04:00
static int wdt_round_time ( int t )
{
t + = 59 ;
t - = t % 60 ;
return t ;
}
2008-03-05 18:48:45 +03:00
/* watchdog timer handling */
2017-06-11 07:04:33 +03:00
static int wdt_start ( struct watchdog_device * wdd )
2008-03-05 18:48:45 +03:00
{
2017-06-11 07:04:34 +03:00
return wdt_update_timeout ( wdd - > timeout ) ;
2008-03-05 18:48:45 +03:00
}
2017-06-11 07:04:33 +03:00
static int wdt_stop ( struct watchdog_device * wdd )
2008-03-05 18:48:45 +03:00
{
2017-06-11 07:04:34 +03:00
return wdt_update_timeout ( 0 ) ;
2008-03-05 18:48:45 +03:00
}
/**
* wdt_set_timeout - set a new timeout value with watchdog ioctl
* @ t : timeout value in seconds
*
2010-09-14 04:54:16 +04:00
* The hardware device has a 8 or 16 bit watchdog timer ( depends on
* chip version ) that can be configured to count seconds or minutes .
2008-03-05 18:48:45 +03:00
*
* Used within WDIOC_SETTIMEOUT watchdog device ioctl .
*/
2017-06-11 07:04:33 +03:00
static int wdt_set_timeout ( struct watchdog_device * wdd , unsigned int t )
2008-03-05 18:48:45 +03:00
{
2017-06-11 07:04:33 +03:00
int ret = 0 ;
2008-03-05 18:48:45 +03:00
2010-09-14 04:54:16 +04:00
if ( t > max_units )
2017-06-11 07:04:33 +03:00
t = wdt_round_time ( t ) ;
2008-03-05 18:48:45 +03:00
2017-06-11 07:04:33 +03:00
wdd - > timeout = t ;
2008-03-05 18:48:45 +03:00
2017-06-11 07:04:33 +03:00
if ( watchdog_hw_running ( wdd ) )
2017-06-11 07:04:34 +03:00
ret = wdt_update_timeout ( t ) ;
2008-03-05 18:48:45 +03:00
2017-06-11 07:04:33 +03:00
return ret ;
2008-03-05 18:48:45 +03:00
}
2009-12-26 21:55:22 +03:00
static const struct watchdog_info ident = {
2008-03-05 18:48:45 +03:00
. options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING ,
2017-06-11 07:04:33 +03:00
. firmware_version = 1 ,
2008-03-05 18:48:45 +03:00
. identity = WATCHDOG_NAME ,
} ;
2017-07-08 03:23:21 +03:00
static const struct watchdog_ops wdt_ops = {
2017-06-11 07:04:33 +03:00
. owner = THIS_MODULE ,
. start = wdt_start ,
. stop = wdt_stop ,
. set_timeout = wdt_set_timeout ,
} ;
2008-03-05 18:48:45 +03:00
2017-06-11 07:04:33 +03:00
static struct watchdog_device wdt_dev = {
. info = & ident ,
. ops = & wdt_ops ,
. min_timeout = 1 ,
} ;
2008-03-05 18:48:45 +03:00
static int __init it87_wdt_init ( void )
{
u8 chip_rev ;
2017-06-11 07:04:34 +03:00
int rc ;
2010-09-14 04:54:16 +04:00
2011-05-09 22:45:07 +04:00
rc = superio_enter ( ) ;
if ( rc )
return rc ;
2008-03-05 18:48:45 +03:00
chip_type = superio_inw ( CHIPID ) ;
chip_rev = superio_inb ( CHIPREV ) & 0x0f ;
superio_exit ( ) ;
switch ( chip_type ) {
2010-09-14 04:54:16 +04:00
case IT8702_ID :
max_units = 255 ;
break ;
case IT8712_ID :
max_units = ( chip_rev < 8 ) ? 255 : 65535 ;
break ;
2008-03-05 18:48:45 +03:00
case IT8716_ID :
case IT8726_ID :
2010-09-14 04:54:16 +04:00
max_units = 65535 ;
2008-03-05 18:48:45 +03:00
break ;
2017-06-11 07:04:36 +03:00
case IT8607_ID :
2016-12-16 01:52:36 +03:00
case IT8620_ID :
2017-06-11 07:04:36 +03:00
case IT8622_ID :
case IT8625_ID :
case IT8628_ID :
case IT8655_ID :
case IT8665_ID :
case IT8686_ID :
2010-09-14 04:47:28 +04:00
case IT8718_ID :
case IT8720_ID :
2011-01-21 01:37:51 +03:00
case IT8721_ID :
2012-03-14 23:49:04 +04:00
case IT8728_ID :
2020-08-27 13:59:40 +03:00
case IT8772_ID :
2014-10-19 23:39:33 +04:00
case IT8783_ID :
2020-09-05 00:16:39 +03:00
case IT8784_ID :
2020-01-23 17:05:44 +03:00
case IT8786_ID :
2010-09-14 04:54:16 +04:00
max_units = 65535 ;
2010-09-14 04:47:28 +04:00
break ;
2008-03-05 18:48:45 +03:00
case IT8705_ID :
2012-02-16 03:06:19 +04:00
pr_err ( " Unsupported Chip found, Chip %04x Revision %02x \n " ,
2008-03-05 18:48:45 +03:00
chip_type , chip_rev ) ;
return - ENODEV ;
case NO_DEV_ID :
2012-02-16 03:06:19 +04:00
pr_err ( " no device \n " ) ;
2008-03-05 18:48:45 +03:00
return - ENODEV ;
default :
2012-02-16 03:06:19 +04:00
pr_err ( " Unknown Chip found, Chip %04x Revision %04x \n " ,
2008-03-05 18:48:45 +03:00
chip_type , chip_rev ) ;
return - ENODEV ;
}
2011-05-09 22:45:07 +04:00
rc = superio_enter ( ) ;
if ( rc )
return rc ;
2008-03-05 18:48:45 +03:00
superio_select ( GPIO ) ;
superio_outb ( WDT_TOV1 , WDTCFG ) ;
superio_outb ( 0x00 , WDTCTRL ) ;
2017-06-11 07:04:34 +03:00
superio_exit ( ) ;
2008-03-05 18:48:45 +03:00
2010-09-14 04:54:16 +04:00
if ( timeout < 1 | | timeout > max_units * 60 ) {
2008-03-05 18:48:45 +03:00
timeout = DEFAULT_TIMEOUT ;
2012-02-16 03:06:19 +04:00
pr_warn ( " Timeout value out of range, use default %d sec \n " ,
DEFAULT_TIMEOUT ) ;
2008-03-05 18:48:45 +03:00
}
2010-09-14 04:54:16 +04:00
if ( timeout > max_units )
timeout = wdt_round_time ( timeout ) ;
2017-06-11 07:04:33 +03:00
wdt_dev . timeout = timeout ;
wdt_dev . max_timeout = max_units * 60 ;
2017-06-11 07:04:35 +03:00
watchdog_stop_on_reboot ( & wdt_dev ) ;
2017-06-11 07:04:33 +03:00
rc = watchdog_register_device ( & wdt_dev ) ;
if ( rc ) {
pr_err ( " Cannot register watchdog device (err=%d) \n " , rc ) ;
2017-06-11 07:04:35 +03:00
return rc ;
2017-06-11 07:04:33 +03:00
}
2017-06-11 07:04:34 +03:00
pr_info ( " Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d) \n " ,
chip_type , chip_rev , timeout , nowayout , testmode ) ;
2008-03-05 18:48:45 +03:00
return 0 ;
}
static void __exit it87_wdt_exit ( void )
{
2017-06-11 07:04:33 +03:00
watchdog_unregister_device ( & wdt_dev ) ;
2008-03-05 18:48:45 +03:00
}
module_init ( it87_wdt_init ) ;
module_exit ( it87_wdt_exit ) ;
MODULE_AUTHOR ( " Oliver Schuster " ) ;
MODULE_DESCRIPTION ( " Hardware Watchdog Device Driver for IT87xx EC-LPC I/O " ) ;
MODULE_LICENSE ( " GPL " ) ;