2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2010-08-01 17:30:55 +04:00
/***************************************************************************
* Copyright ( C ) 2006 by Hans Edgington < hans @ edgington . nl > *
* Copyright ( C ) 2007 - 2009 Hans de Goede < hdegoede @ redhat . com > *
* Copyright ( C ) 2010 Giel van Schijndel < me @ mortis . eu > *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-08-01 17:30:55 +04:00
# include <linux/err.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/module.h>
2021-08-09 19:20:36 +03:00
# include <linux/platform_device.h>
2010-08-01 17:30:55 +04:00
# include <linux/watchdog.h>
# define DRVNAME "f71808e_wdt"
# define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */
# define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
2016-04-25 18:34:47 +03:00
# define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
2010-08-01 17:30:55 +04:00
# define SIO_REG_LDSEL 0x07 /* Logical device select */
# define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
# define SIO_REG_DEVREV 0x22 /* Device revision */
# define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
2019-09-12 20:55:50 +03:00
# define SIO_REG_CLOCK_SEL 0x26 /* Clock select */
2010-09-26 18:25:35 +04:00
# define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
2016-06-08 09:57:50 +03:00
# define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */
2019-09-12 20:55:50 +03:00
# define SIO_REG_TSI_LEVEL_SEL 0x28 /* TSI Level select */
2010-09-26 18:38:20 +04:00
# define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
# define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
# define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
2016-06-08 09:57:50 +03:00
# define SIO_F81866_REG_GPIO1 0x2c /* F81866 GPIO1 Enable Register */
2010-08-01 17:30:55 +04:00
# define SIO_REG_ENABLE 0x30 /* Logical device enable */
# define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
# define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
2010-09-26 18:38:20 +04:00
# define SIO_F71808_ID 0x0901 /* Chipset ID */
# define SIO_F71858_ID 0x0507 /* Chipset ID */
2010-08-01 17:30:55 +04:00
# define SIO_F71862_ID 0x0601 /* Chipset ID */
2017-04-17 23:37:05 +03:00
# define SIO_F71868_ID 0x1106 /* Chipset ID */
2010-12-06 22:53:45 +03:00
# define SIO_F71869_ID 0x0814 /* Chipset ID */
2012-06-11 09:07:58 +04:00
# define SIO_F71869A_ID 0x1007 /* Chipset ID */
2010-08-01 17:30:55 +04:00
# define SIO_F71882_ID 0x0541 /* Chipset ID */
# define SIO_F71889_ID 0x0723 /* Chipset ID */
2019-09-12 20:55:50 +03:00
# define SIO_F81803_ID 0x1210 /* Chipset ID */
2016-04-25 13:28:51 +03:00
# define SIO_F81865_ID 0x0704 /* Chipset ID */
2016-06-08 09:57:50 +03:00
# define SIO_F81866_ID 0x1010 /* Chipset ID */
2021-11-17 05:40:52 +03:00
# define SIO_F81966_ID 0x1502 /* F81804 chipset ID, same for f81966 */
2010-08-01 17:30:55 +04:00
# define F71808FG_REG_WDO_CONF 0xf0
# define F71808FG_REG_WDT_CONF 0xf5
# define F71808FG_REG_WD_TIME 0xf6
# define F71808FG_FLAG_WDOUT_EN 7
2016-04-26 09:44:16 +03:00
# define F71808FG_FLAG_WDTMOUT_STS 6
2010-08-01 17:30:55 +04:00
# define F71808FG_FLAG_WD_EN 5
# define F71808FG_FLAG_WD_PULSE 4
# define F71808FG_FLAG_WD_UNIT 3
2016-04-25 13:28:51 +03:00
# define F81865_REG_WDO_CONF 0xfa
# define F81865_FLAG_WDOUT_EN 0
2010-08-01 17:30:55 +04:00
/* Default values */
# define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */
# define WATCHDOG_MAX_TIMEOUT (60 * 255)
# define WATCHDOG_PULSE_WIDTH 125 / * 125 ms, default pulse width for
watchdog signal */
2010-09-26 18:25:35 +04:00
# define WATCHDOG_F71862FG_PIN 63 / * default watchdog reset output
pin number 63 */
2010-08-01 17:30:55 +04:00
static unsigned short force_id ;
module_param ( force_id , ushort , 0 ) ;
MODULE_PARM_DESC ( force_id , " Override the detected device ID " ) ;
2010-09-26 18:38:20 +04:00
static int timeout = WATCHDOG_TIMEOUT ; /* default timeout in seconds */
2010-08-01 17:30:55 +04:00
module_param ( timeout , int , 0 ) ;
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in seconds. 1<= timeout <= "
__MODULE_STRING ( WATCHDOG_MAX_TIMEOUT ) " (default= "
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " ) " ) ;
static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH ;
module_param ( pulse_width , uint , 0 ) ;
MODULE_PARM_DESC ( pulse_width ,
2017-04-17 23:37:05 +03:00
" Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms "
2010-08-01 17:30:55 +04:00
" (default= " __MODULE_STRING ( WATCHDOG_PULSE_WIDTH ) " ) " ) ;
2010-09-26 18:25:35 +04:00
static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN ;
module_param ( f71862fg_pin , uint , 0 ) ;
MODULE_PARM_DESC ( f71862fg_pin ,
" Watchdog f71862fg reset output pin configuration. Choose pin 56 or 63 "
" (default= " __MODULE_STRING ( WATCHDOG_F71862FG_PIN ) " ) " ) ;
2012-01-13 03:02:20 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2010-08-01 17:30:55 +04:00
module_param ( nowayout , bool , 0444 ) ;
MODULE_PARM_DESC ( nowayout , " Disable watchdog shutdown on close " ) ;
static unsigned int start_withtimeout ;
module_param ( start_withtimeout , uint , 0 ) ;
MODULE_PARM_DESC ( start_withtimeout , " Start watchdog timer on module load with "
" given initial timeout. Zero (default) disables this feature. " ) ;
2017-04-17 23:37:05 +03:00
enum chips { f71808fg , f71858fg , f71862fg , f71868 , f71869 , f71882fg , f71889fg ,
2021-11-17 05:40:52 +03:00
f81803 , f81865 , f81866 , f81966 } ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:34 +03:00
static const char * const fintek_wdt_names [ ] = {
2010-08-01 17:30:55 +04:00
" f71808fg " ,
" f71858fg " ,
" f71862fg " ,
2017-04-17 23:37:05 +03:00
" f71868 " ,
2010-12-06 22:53:45 +03:00
" f71869 " ,
2010-08-01 17:30:55 +04:00
" f71882fg " ,
" f71889fg " ,
2019-09-12 20:55:50 +03:00
" f81803 " ,
2016-04-25 13:28:51 +03:00
" f81865 " ,
2016-06-08 09:57:50 +03:00
" f81866 " ,
2021-11-17 05:40:52 +03:00
" f81966 "
2010-08-01 17:30:55 +04:00
} ;
/* Super-I/O Function prototypes */
static inline int superio_inb ( int base , int reg ) ;
static inline int superio_inw ( int base , int reg ) ;
static inline void superio_outb ( int base , int reg , u8 val ) ;
static inline void superio_set_bit ( int base , int reg , int bit ) ;
static inline void superio_clear_bit ( int base , int reg , int bit ) ;
static inline int superio_enter ( int base ) ;
static inline void superio_select ( int base , int ld ) ;
static inline void superio_exit ( int base ) ;
2021-08-09 19:20:34 +03:00
struct fintek_wdt {
2021-08-09 19:20:35 +03:00
struct watchdog_device wdd ;
2010-08-01 17:30:55 +04:00
unsigned short sioaddr ;
enum chips type ;
struct watchdog_info ident ;
u8 timer_val ; /* content for the wd_time register */
char minutes_mode ;
u8 pulse_val ; /* pulse width flag */
char pulse_mode ; /* enable pulse output mode? */
} ;
2021-08-09 19:20:37 +03:00
struct fintek_wdt_pdata {
enum chips type ;
} ;
2010-08-01 17:30:55 +04:00
/* Super I/O functions */
static inline int superio_inb ( int base , int reg )
{
outb ( reg , base ) ;
return inb ( base + 1 ) ;
}
static int superio_inw ( int base , int reg )
{
int val ;
val = superio_inb ( base , reg ) < < 8 ;
val | = superio_inb ( base , reg + 1 ) ;
return val ;
}
static inline void superio_outb ( int base , int reg , u8 val )
{
outb ( reg , base ) ;
outb ( val , base + 1 ) ;
}
static inline void superio_set_bit ( int base , int reg , int bit )
{
unsigned long val = superio_inb ( base , reg ) ;
__set_bit ( bit , & val ) ;
superio_outb ( base , reg , val ) ;
}
static inline void superio_clear_bit ( int base , int reg , int bit )
{
unsigned long val = superio_inb ( base , reg ) ;
__clear_bit ( bit , & val ) ;
superio_outb ( base , reg , val ) ;
}
static inline int superio_enter ( int base )
{
/* Don't step on other drivers' I/O space by accident */
if ( ! request_muxed_region ( base , 2 , DRVNAME ) ) {
2012-02-16 03:06:19 +04:00
pr_err ( " I/O address 0x%04x already in use \n " , ( int ) base ) ;
2010-08-01 17:30:55 +04:00
return - EBUSY ;
}
2012-06-11 09:07:58 +04:00
/* according to the datasheet the key must be sent twice! */
2010-08-01 17:30:55 +04:00
outb ( SIO_UNLOCK_KEY , base ) ;
outb ( SIO_UNLOCK_KEY , base ) ;
return 0 ;
}
static inline void superio_select ( int base , int ld )
{
outb ( SIO_REG_LDSEL , base ) ;
outb ( ld , base + 1 ) ;
}
static inline void superio_exit ( int base )
{
outb ( SIO_LOCK_KEY , base ) ;
release_region ( base , 2 ) ;
}
2021-08-09 19:20:35 +03:00
static int fintek_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int timeout )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:37 +03:00
struct fintek_wdt * wd = watchdog_get_drvdata ( wdd ) ;
2010-08-01 17:30:55 +04:00
if ( timeout > 0xff ) {
2021-08-09 19:20:37 +03:00
wd - > timer_val = DIV_ROUND_UP ( timeout , 60 ) ;
wd - > minutes_mode = true ;
timeout = wd - > timer_val * 60 ;
2010-08-01 17:30:55 +04:00
} else {
2021-08-09 19:20:37 +03:00
wd - > timer_val = timeout ;
wd - > minutes_mode = false ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:35 +03:00
wdd - > timeout = timeout ;
2010-08-01 17:30:55 +04:00
return 0 ;
}
2021-08-09 19:20:37 +03:00
static int fintek_wdt_set_pulse_width ( struct fintek_wdt * wd , unsigned int pw )
2010-08-01 17:30:55 +04:00
{
2017-04-17 23:37:05 +03:00
unsigned int t1 = 25 , t2 = 125 , t3 = 5000 ;
2021-08-09 19:20:37 +03:00
if ( wd - > type = = f71868 ) {
2017-04-17 23:37:05 +03:00
t1 = 30 ;
t2 = 150 ;
t3 = 6000 ;
}
2010-08-01 17:30:55 +04:00
2017-04-17 23:37:05 +03:00
if ( pw < = 1 ) {
2021-08-09 19:20:37 +03:00
wd - > pulse_val = 0 ;
2017-04-17 23:37:05 +03:00
} else if ( pw < = t1 ) {
2021-08-09 19:20:37 +03:00
wd - > pulse_val = 1 ;
2017-04-17 23:37:05 +03:00
} else if ( pw < = t2 ) {
2021-08-09 19:20:37 +03:00
wd - > pulse_val = 2 ;
2017-04-17 23:37:05 +03:00
} else if ( pw < = t3 ) {
2021-08-09 19:20:37 +03:00
wd - > pulse_val = 3 ;
2010-08-01 17:30:55 +04:00
} else {
2012-02-16 03:06:19 +04:00
pr_err ( " pulse width out of range \n " ) ;
2021-08-09 19:20:35 +03:00
return - EINVAL ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:37 +03:00
wd - > pulse_mode = pw ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:35 +03:00
return 0 ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:35 +03:00
static int fintek_wdt_keepalive ( struct watchdog_device * wdd )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:37 +03:00
struct fintek_wdt * wd = watchdog_get_drvdata ( wdd ) ;
2021-08-09 19:20:35 +03:00
int err ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
err = superio_enter ( wd - > sioaddr ) ;
2010-08-01 17:30:55 +04:00
if ( err )
2021-08-09 19:20:35 +03:00
return err ;
2021-08-09 19:20:37 +03:00
superio_select ( wd - > sioaddr , SIO_F71808FG_LD_WDT ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
if ( wd - > minutes_mode )
2010-08-01 17:30:55 +04:00
/* select minutes for timer units */
2021-08-09 19:20:37 +03:00
superio_set_bit ( wd - > sioaddr , F71808FG_REG_WDT_CONF ,
2010-08-01 17:30:55 +04:00
F71808FG_FLAG_WD_UNIT ) ;
else
/* select seconds for timer units */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , F71808FG_REG_WDT_CONF ,
2010-08-01 17:30:55 +04:00
F71808FG_FLAG_WD_UNIT ) ;
/* Set timer value */
2021-08-09 19:20:37 +03:00
superio_outb ( wd - > sioaddr , F71808FG_REG_WD_TIME ,
wd - > timer_val ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
superio_exit ( wd - > sioaddr ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:35 +03:00
return 0 ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:35 +03:00
static int fintek_wdt_start ( struct watchdog_device * wdd )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:37 +03:00
struct fintek_wdt * wd = watchdog_get_drvdata ( wdd ) ;
2019-03-27 09:42:50 +03:00
int err ;
2019-03-27 09:42:51 +03:00
u8 tmp ;
2019-03-27 09:42:50 +03:00
2010-08-01 17:30:55 +04:00
/* Make sure we don't die as soon as the watchdog is enabled below */
2021-08-09 19:20:35 +03:00
err = fintek_wdt_keepalive ( wdd ) ;
2010-08-01 17:30:55 +04:00
if ( err )
return err ;
2021-08-09 19:20:37 +03:00
err = superio_enter ( wd - > sioaddr ) ;
2010-08-01 17:30:55 +04:00
if ( err )
2021-08-09 19:20:35 +03:00
return err ;
2021-08-09 19:20:37 +03:00
superio_select ( wd - > sioaddr , SIO_F71808FG_LD_WDT ) ;
2010-08-01 17:30:55 +04:00
/* Watchdog pin configuration */
2021-08-09 19:20:37 +03:00
switch ( wd - > type ) {
2010-08-01 17:30:55 +04:00
case f71808fg :
/* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , SIO_REG_MFUNCT2 , 3 ) ;
superio_clear_bit ( wd - > sioaddr , SIO_REG_MFUNCT3 , 3 ) ;
2010-08-01 17:30:55 +04:00
break ;
2010-09-26 18:25:35 +04:00
case f71862fg :
2020-06-11 22:17:46 +03:00
if ( f71862fg_pin = = 63 ) {
/* SPI must be disabled first to use this pin! */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , SIO_REG_ROM_ADDR_SEL , 6 ) ;
superio_set_bit ( wd - > sioaddr , SIO_REG_MFUNCT3 , 4 ) ;
2020-06-11 22:17:46 +03:00
} else if ( f71862fg_pin = = 56 ) {
2021-08-09 19:20:37 +03:00
superio_set_bit ( wd - > sioaddr , SIO_REG_MFUNCT1 , 1 ) ;
2020-06-11 22:17:46 +03:00
}
2010-09-26 18:25:35 +04:00
break ;
2017-04-17 23:37:05 +03:00
case f71868 :
2010-12-06 22:53:45 +03:00
case f71869 :
/* GPIO14 --> WDTRST# */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , SIO_REG_MFUNCT1 , 4 ) ;
2010-12-06 22:53:45 +03:00
break ;
2010-08-01 17:30:55 +04:00
case f71882fg :
/* Set pin 56 to WDTRST# */
2021-08-09 19:20:37 +03:00
superio_set_bit ( wd - > sioaddr , SIO_REG_MFUNCT1 , 1 ) ;
2010-08-01 17:30:55 +04:00
break ;
2010-10-04 12:45:28 +04:00
case f71889fg :
/* set pin 40 to WDTRST# */
2021-08-09 19:20:37 +03:00
superio_outb ( wd - > sioaddr , SIO_REG_MFUNCT3 ,
superio_inb ( wd - > sioaddr , SIO_REG_MFUNCT3 ) & 0xcf ) ;
2010-10-04 12:45:28 +04:00
break ;
2019-09-12 20:55:50 +03:00
case f81803 :
/* Enable TSI Level register bank */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , SIO_REG_CLOCK_SEL , 3 ) ;
2019-09-12 20:55:50 +03:00
/* Set pin 27 to WDTRST# */
2021-08-09 19:20:37 +03:00
superio_outb ( wd - > sioaddr , SIO_REG_TSI_LEVEL_SEL , 0x5f &
superio_inb ( wd - > sioaddr , SIO_REG_TSI_LEVEL_SEL ) ) ;
2019-09-12 20:55:50 +03:00
break ;
2016-04-25 13:28:51 +03:00
case f81865 :
/* Set pin 70 to WDTRST# */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , SIO_REG_MFUNCT3 , 5 ) ;
2016-04-25 13:28:51 +03:00
break ;
2016-06-08 09:57:50 +03:00
case f81866 :
2021-11-17 05:40:52 +03:00
case f81966 :
2016-06-08 09:57:50 +03:00
/*
* GPIO1 Control Register when 27 h BIT3 : 2 = 01 & BIT0 = 0.
* The PIN 70 ( GPIO15 / WDTRST ) is controlled by 2 Ch :
* BIT5 : 0 - > WDTRST #
* 1 - > GPIO15
*/
2021-08-09 19:20:37 +03:00
tmp = superio_inb ( wd - > sioaddr , SIO_F81866_REG_PORT_SEL ) ;
2019-03-27 09:42:51 +03:00
tmp & = ~ ( BIT ( 3 ) | BIT ( 0 ) ) ;
tmp | = BIT ( 2 ) ;
2021-08-09 19:20:37 +03:00
superio_outb ( wd - > sioaddr , SIO_F81866_REG_PORT_SEL , tmp ) ;
2019-03-27 09:42:51 +03:00
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , SIO_F81866_REG_GPIO1 , 5 ) ;
2016-06-08 09:57:50 +03:00
break ;
2010-08-01 17:30:55 +04:00
default :
/*
* ' default ' label to shut up the compiler and catch
* programmer errors
*/
err = - ENODEV ;
goto exit_superio ;
}
2021-08-09 19:20:37 +03:00
superio_select ( wd - > sioaddr , SIO_F71808FG_LD_WDT ) ;
superio_set_bit ( wd - > sioaddr , SIO_REG_ENABLE , 0 ) ;
2016-04-25 13:28:51 +03:00
2021-11-17 05:40:52 +03:00
if ( wd - > type = = f81865 | | wd - > type = = f81866 | | wd - > type = = f81966 )
2021-08-09 19:20:37 +03:00
superio_set_bit ( wd - > sioaddr , F81865_REG_WDO_CONF ,
2016-04-25 13:28:51 +03:00
F81865_FLAG_WDOUT_EN ) ;
else
2021-08-09 19:20:37 +03:00
superio_set_bit ( wd - > sioaddr , F71808FG_REG_WDO_CONF ,
2016-04-25 13:28:51 +03:00
F71808FG_FLAG_WDOUT_EN ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
superio_set_bit ( wd - > sioaddr , F71808FG_REG_WDT_CONF ,
2010-08-01 17:30:55 +04:00
F71808FG_FLAG_WD_EN ) ;
2021-08-09 19:20:37 +03:00
if ( wd - > pulse_mode ) {
2010-08-01 17:30:55 +04:00
/* Select "pulse" output mode with given duration */
2021-08-09 19:20:37 +03:00
u8 wdt_conf = superio_inb ( wd - > sioaddr ,
2010-08-01 17:30:55 +04:00
F71808FG_REG_WDT_CONF ) ;
/* Set WD_PSWIDTH bits (1:0) */
2021-08-09 19:20:37 +03:00
wdt_conf = ( wdt_conf & 0xfc ) | ( wd - > pulse_val & 0x03 ) ;
2010-08-01 17:30:55 +04:00
/* Set WD_PULSE to "pulse" mode */
wdt_conf | = BIT ( F71808FG_FLAG_WD_PULSE ) ;
2021-08-09 19:20:37 +03:00
superio_outb ( wd - > sioaddr , F71808FG_REG_WDT_CONF ,
2010-08-01 17:30:55 +04:00
wdt_conf ) ;
} else {
/* Select "level" output mode */
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , F71808FG_REG_WDT_CONF ,
2010-08-01 17:30:55 +04:00
F71808FG_FLAG_WD_PULSE ) ;
}
exit_superio :
2021-08-09 19:20:37 +03:00
superio_exit ( wd - > sioaddr ) ;
2010-08-01 17:30:55 +04:00
return err ;
}
2021-08-09 19:20:35 +03:00
static int fintek_wdt_stop ( struct watchdog_device * wdd )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:37 +03:00
struct fintek_wdt * wd = watchdog_get_drvdata ( wdd ) ;
2021-08-09 19:20:35 +03:00
int err ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
err = superio_enter ( wd - > sioaddr ) ;
2010-08-01 17:30:55 +04:00
if ( err )
2021-08-09 19:20:35 +03:00
return err ;
2021-08-09 19:20:37 +03:00
superio_select ( wd - > sioaddr , SIO_F71808FG_LD_WDT ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
superio_clear_bit ( wd - > sioaddr , F71808FG_REG_WDT_CONF ,
2010-08-01 17:30:55 +04:00
F71808FG_FLAG_WD_EN ) ;
2021-08-09 19:20:37 +03:00
superio_exit ( wd - > sioaddr ) ;
2010-08-01 17:30:55 +04:00
return 0 ;
}
2021-08-09 19:20:37 +03:00
static bool fintek_wdt_is_running ( struct fintek_wdt * wd , u8 wdt_conf )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:37 +03:00
return ( superio_inb ( wd - > sioaddr , SIO_REG_ENABLE ) & BIT ( 0 ) )
2021-08-09 19:20:35 +03:00
& & ( wdt_conf & BIT ( F71808FG_FLAG_WD_EN ) ) ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:35 +03:00
static const struct watchdog_ops fintek_wdt_ops = {
. owner = THIS_MODULE ,
. start = fintek_wdt_start ,
. stop = fintek_wdt_stop ,
. ping = fintek_wdt_keepalive ,
. set_timeout = fintek_wdt_set_timeout ,
2010-08-01 17:30:55 +04:00
} ;
2021-08-09 19:20:36 +03:00
static int fintek_wdt_probe ( struct platform_device * pdev )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:36 +03:00
struct device * dev = & pdev - > dev ;
2021-08-09 19:20:37 +03:00
struct fintek_wdt_pdata * pdata ;
2021-08-09 19:20:35 +03:00
struct watchdog_device * wdd ;
2021-08-09 19:20:37 +03:00
struct fintek_wdt * wd ;
2010-08-01 17:30:55 +04:00
int wdt_conf , err = 0 ;
2021-08-09 19:20:36 +03:00
struct resource * res ;
int sioaddr ;
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! res )
return - ENXIO ;
sioaddr = res - > start ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
wd = devm_kzalloc ( dev , sizeof ( * wd ) , GFP_KERNEL ) ;
if ( ! wd )
return - ENOMEM ;
pdata = dev - > platform_data ;
wd - > type = pdata - > type ;
wd - > sioaddr = sioaddr ;
wd - > ident . options = WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
| WDIOF_CARDRESET ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
snprintf ( wd - > ident . identity ,
sizeof ( wd - > ident . identity ) , " %s watchdog " ,
fintek_wdt_names [ wd - > type ] ) ;
2010-08-01 17:30:55 +04:00
err = superio_enter ( sioaddr ) ;
if ( err )
return err ;
2021-08-09 19:20:37 +03:00
superio_select ( wd - > sioaddr , SIO_F71808FG_LD_WDT ) ;
2010-08-01 17:30:55 +04:00
wdt_conf = superio_inb ( sioaddr , F71808FG_REG_WDT_CONF ) ;
2020-06-11 22:17:45 +03:00
/*
* We don ' t want WDTMOUT_STS to stick around till regular reboot .
* Write 1 to the bit to clear it to zero .
*/
superio_outb ( sioaddr , F71808FG_REG_WDT_CONF ,
wdt_conf | BIT ( F71808FG_FLAG_WDTMOUT_STS ) ) ;
2021-08-09 19:20:37 +03:00
wdd = & wd - > wdd ;
2021-08-09 19:20:35 +03:00
2021-08-09 19:20:37 +03:00
if ( fintek_wdt_is_running ( wd , wdt_conf ) )
2021-08-09 19:20:35 +03:00
set_bit ( WDOG_HW_RUNNING , & wdd - > status ) ;
2010-08-01 17:30:55 +04:00
superio_exit ( sioaddr ) ;
2021-08-09 19:20:36 +03:00
wdd - > parent = dev ;
2021-08-09 19:20:37 +03:00
wdd - > info = & wd - > ident ;
2021-08-09 19:20:35 +03:00
wdd - > ops = & fintek_wdt_ops ;
wdd - > min_timeout = 1 ;
wdd - > max_timeout = WATCHDOG_MAX_TIMEOUT ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:37 +03:00
watchdog_set_drvdata ( wdd , wd ) ;
2021-08-09 19:20:35 +03:00
watchdog_set_nowayout ( wdd , nowayout ) ;
watchdog_stop_on_unregister ( wdd ) ;
watchdog_stop_on_reboot ( wdd ) ;
watchdog_init_timeout ( wdd , start_withtimeout ? : timeout , NULL ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:35 +03:00
if ( wdt_conf & BIT ( F71808FG_FLAG_WDTMOUT_STS ) )
wdd - > bootstatus = WDIOF_CARDRESET ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:35 +03:00
/*
* WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly
* called without a set_timeout before , so it needs to be done here
* unconditionally .
*/
fintek_wdt_set_timeout ( wdd , wdd - > timeout ) ;
2021-08-09 19:20:37 +03:00
fintek_wdt_set_pulse_width ( wd , pulse_width ) ;
2010-08-01 17:30:55 +04:00
2021-08-09 19:20:35 +03:00
if ( start_withtimeout ) {
err = fintek_wdt_start ( wdd ) ;
2010-08-01 17:30:55 +04:00
if ( err ) {
2021-08-09 19:20:36 +03:00
dev_err ( dev , " cannot start watchdog timer \n " ) ;
2021-08-09 19:20:35 +03:00
return err ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:35 +03:00
set_bit ( WDOG_HW_RUNNING , & wdd - > status ) ;
2021-08-09 19:20:36 +03:00
dev_info ( dev , " watchdog started with initial timeout of %u sec \n " ,
start_withtimeout ) ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:36 +03:00
return devm_watchdog_register_device ( dev , wdd ) ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:34 +03:00
static int __init fintek_wdt_find ( int sioaddr )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:37 +03:00
enum chips type ;
2010-08-01 17:30:55 +04:00
u16 devid ;
int err = superio_enter ( sioaddr ) ;
if ( err )
return err ;
devid = superio_inw ( sioaddr , SIO_REG_MANID ) ;
if ( devid ! = SIO_FINTEK_ID ) {
2012-02-16 03:06:19 +04:00
pr_debug ( " Not a Fintek device \n " ) ;
2010-08-01 17:30:55 +04:00
err = - ENODEV ;
goto exit ;
}
devid = force_id ? force_id : superio_inw ( sioaddr , SIO_REG_DEVID ) ;
switch ( devid ) {
case SIO_F71808_ID :
2021-08-09 19:20:37 +03:00
type = f71808fg ;
2010-08-01 17:30:55 +04:00
break ;
2010-09-26 18:25:35 +04:00
case SIO_F71862_ID :
2021-08-09 19:20:37 +03:00
type = f71862fg ;
2010-09-26 18:25:35 +04:00
break ;
2017-04-17 23:37:05 +03:00
case SIO_F71868_ID :
2021-08-09 19:20:37 +03:00
type = f71868 ;
2017-04-17 23:37:05 +03:00
break ;
2010-12-06 22:53:45 +03:00
case SIO_F71869_ID :
2012-06-11 09:07:58 +04:00
case SIO_F71869A_ID :
2021-08-09 19:20:37 +03:00
type = f71869 ;
2010-12-06 22:53:45 +03:00
break ;
2010-08-01 17:30:55 +04:00
case SIO_F71882_ID :
2021-08-09 19:20:37 +03:00
type = f71882fg ;
2010-08-01 17:30:55 +04:00
break ;
case SIO_F71889_ID :
2021-08-09 19:20:37 +03:00
type = f71889fg ;
2010-10-04 12:45:28 +04:00
break ;
2010-08-01 17:30:55 +04:00
case SIO_F71858_ID :
/* Confirmed (by datasheet) not to have a watchdog. */
err = - ENODEV ;
goto exit ;
2019-09-12 20:55:50 +03:00
case SIO_F81803_ID :
2021-08-09 19:20:37 +03:00
type = f81803 ;
2019-09-12 20:55:50 +03:00
break ;
2016-04-25 13:28:51 +03:00
case SIO_F81865_ID :
2021-08-09 19:20:37 +03:00
type = f81865 ;
2016-06-08 09:57:50 +03:00
break ;
case SIO_F81866_ID :
2021-08-09 19:20:37 +03:00
type = f81866 ;
2016-04-25 13:28:51 +03:00
break ;
2021-11-17 05:40:52 +03:00
case SIO_F81966_ID :
type = f81966 ;
break ;
2010-08-01 17:30:55 +04:00
default :
2012-02-16 03:06:19 +04:00
pr_info ( " Unrecognized Fintek device: %04x \n " ,
( unsigned int ) devid ) ;
2010-08-01 17:30:55 +04:00
err = - ENODEV ;
goto exit ;
}
2012-02-16 03:06:19 +04:00
pr_info ( " Found %s watchdog chip, revision %d \n " ,
2021-08-09 19:20:37 +03:00
fintek_wdt_names [ type ] ,
2010-08-01 17:30:55 +04:00
( int ) superio_inb ( sioaddr , SIO_REG_DEVREV ) ) ;
2021-08-09 19:20:37 +03:00
2010-08-01 17:30:55 +04:00
exit :
superio_exit ( sioaddr ) ;
2021-08-09 19:20:37 +03:00
return err ? err : type ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:36 +03:00
static struct platform_driver fintek_wdt_driver = {
. probe = fintek_wdt_probe ,
. driver = {
. name = DRVNAME ,
} ,
} ;
static struct platform_device * fintek_wdt_pdev ;
2021-08-09 19:20:34 +03:00
static int __init fintek_wdt_init ( void )
2010-08-01 17:30:55 +04:00
{
static const unsigned short addrs [ ] = { 0x2e , 0x4e } ;
2021-08-09 19:20:37 +03:00
struct fintek_wdt_pdata pdata ;
2021-08-09 19:20:36 +03:00
struct resource wdt_res = { } ;
2021-08-09 19:20:37 +03:00
int ret ;
2010-08-01 17:30:55 +04:00
int i ;
2020-06-11 22:17:46 +03:00
if ( f71862fg_pin ! = 63 & & f71862fg_pin ! = 56 ) {
pr_err ( " Invalid argument f71862fg_pin=%d \n " , f71862fg_pin ) ;
return - EINVAL ;
}
2010-08-01 17:30:55 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( addrs ) ; i + + ) {
2021-08-09 19:20:37 +03:00
ret = fintek_wdt_find ( addrs [ i ] ) ;
if ( ret > = 0 )
2010-08-01 17:30:55 +04:00
break ;
}
if ( i = = ARRAY_SIZE ( addrs ) )
2021-08-09 19:20:37 +03:00
return ret ;
pdata . type = ret ;
2010-08-01 17:30:55 +04:00
2022-05-26 11:03:03 +03:00
ret = platform_driver_register ( & fintek_wdt_driver ) ;
if ( ret )
return ret ;
2021-08-09 19:20:36 +03:00
wdt_res . name = " superio port " ;
wdt_res . flags = IORESOURCE_IO ;
wdt_res . start = addrs [ i ] ;
wdt_res . end = addrs [ i ] + 1 ;
2021-08-09 19:20:37 +03:00
fintek_wdt_pdev = platform_device_register_resndata ( NULL , DRVNAME , - 1 ,
& wdt_res , 1 ,
& pdata , sizeof ( pdata ) ) ;
2021-08-09 19:20:36 +03:00
if ( IS_ERR ( fintek_wdt_pdev ) ) {
platform_driver_unregister ( & fintek_wdt_driver ) ;
return PTR_ERR ( fintek_wdt_pdev ) ;
}
return 0 ;
2010-08-01 17:30:55 +04:00
}
2021-08-09 19:20:34 +03:00
static void __exit fintek_wdt_exit ( void )
2010-08-01 17:30:55 +04:00
{
2021-08-09 19:20:36 +03:00
platform_device_unregister ( fintek_wdt_pdev ) ;
platform_driver_unregister ( & fintek_wdt_driver ) ;
2010-08-01 17:30:55 +04:00
}
MODULE_DESCRIPTION ( " F71808E Watchdog Driver " ) ;
MODULE_AUTHOR ( " Giel van Schijndel <me@mortis.eu> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2021-08-09 19:20:34 +03:00
module_init ( fintek_wdt_init ) ;
module_exit ( fintek_wdt_exit ) ;