2006-05-21 16:37:44 +04:00
/*
2009-11-15 16:44:54 +03:00
* intel TCO Watchdog Driver
2006-05-21 16:37:44 +04:00
*
2011-10-20 01:59:26 +04:00
* ( c ) Copyright 2006 - 2011 Wim Van Sebroeck < wim @ iguana . be > .
2006-05-21 16:37:44 +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 Wim Van Sebroeck nor Iguana vzw . admit liability nor
* provide warranty for any of this software . This material is
* provided " AS-IS " and at no charge .
*
* The TCO watchdog is implemented in the following I / O controller hubs :
* ( See the intel documentation on http : //developer.intel.com.)
2009-11-15 16:44:54 +03:00
* document number 290655 - 003 , 290677 - 014 : 82801 AA ( ICH ) , 82801 AB ( ICHO )
* document number 290687 - 002 , 298242 - 027 : 82801 BA ( ICH2 )
* document number 290733 - 003 , 290739 - 013 : 82801 CA ( ICH3 - S )
* document number 290716 - 001 , 290718 - 007 : 82801 CAM ( ICH3 - M )
* document number 290744 - 001 , 290745 - 025 : 82801 DB ( ICH4 )
* document number 252337 - 001 , 252663 - 00 8 : 82801 DBM ( ICH4 - M )
* document number 273599 - 001 , 273645 - 002 : 82801 E ( C - ICH )
* document number 252516 - 001 , 252517 - 02 8 : 82801 EB ( ICH5 ) , 82801 ER ( ICH5R )
* document number 300641 - 004 , 300884 - 013 : 6300 ESB
* document number 301473 - 002 , 301474 - 026 : 82801F ( ICH6 )
* document number 313082 - 001 , 313075 - 006 : 631 xESB , 632 xESB
* document number 307013 - 003 , 307014 - 024 : 82801 G ( ICH7 )
2010-12-31 17:10:45 +03:00
* document number 322896 - 001 , 322897 - 001 : NM10
2009-11-15 16:44:54 +03:00
* document number 313056 - 003 , 313057 - 017 : 82801 H ( ICH8 )
* document number 316972 - 004 , 316973 - 012 : 82801 I ( ICH9 )
* document number 319973 - 002 , 319974 - 002 : 82801 J ( ICH10 )
2010-01-14 23:58:05 +03:00
* document number 322169 - 001 , 322170 - 003 : 5 Series , 3400 Series ( PCH )
2009-12-07 22:42:26 +03:00
* document number 320066 - 003 , 320257 - 00 8 : EP80597 ( IICH )
2011-01-08 04:11:08 +03:00
* document number 324645 - 001 , 324646 - 001 : Cougar Point ( CPT )
2010-11-17 22:15:08 +03:00
* document number TBD : Patsburg ( PBG )
2011-01-08 04:11:08 +03:00
* document number TBD : DH89xxCC
2011-04-20 21:56:20 +04:00
* document number TBD : Panther Point
2012-01-24 04:40:55 +04:00
* document number TBD : Lynx Point
2012-08-09 20:46:13 +04:00
* document number TBD : Lynx Point - LP
2006-05-21 16:37:44 +04:00
*/
/*
* Includes , defines , variables , module parameters , . . .
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2006-05-21 16:37:44 +04:00
/* Module and version information */
2008-08-07 00:19:41 +04:00
# define DRV_NAME "iTCO_wdt"
2014-03-11 01:34:55 +04:00
# define DRV_VERSION "1.11"
2006-05-21 16:37:44 +04:00
/* Includes */
2015-04-03 16:25:04 +03:00
# include <linux/acpi.h> /* For ACPI support */
2006-06-30 10:44:53 +04:00
# include <linux/module.h> /* For module specific items */
# include <linux/moduleparam.h> /* For new moduleparam's */
# include <linux/types.h> /* For standard types (like size_t) */
# include <linux/errno.h> /* For the -ENODEV/... values */
# include <linux/kernel.h> /* For printk/panic/... */
# include <linux/watchdog.h> /* For the watchdog specific items */
# include <linux/init.h> /* For __init/__exit/... */
# include <linux/fs.h> /* For file operations */
# include <linux/platform_device.h> /* For platform_driver framework */
# include <linux/pci.h> /* For pci functions */
# include <linux/ioport.h> /* For io-port access */
# include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
2008-05-19 17:06:25 +04:00
# include <linux/uaccess.h> /* For copy_to_user/put_user/... */
# include <linux/io.h> /* For inb/outb/... */
2015-08-06 15:46:24 +03:00
# include <linux/platform_data/itco_wdt.h>
2006-06-30 10:44:53 +04:00
2008-05-19 17:06:25 +04:00
# include "iTCO_vendor.h"
2006-05-21 16:37:44 +04:00
/* Address definitions for the TCO */
2008-05-19 17:06:25 +04:00
/* TCO base address */
2017-01-01 22:11:39 +03:00
# define TCOBASE(p) ((p)->tco_res->start)
2008-05-19 17:06:25 +04:00
/* SMI Control and Enable Register */
2017-01-01 22:11:39 +03:00
# define SMI_EN(p) ((p)->smi_res->start)
# define TCO_RLD(p) (TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */
# define TCOv1_TMR(p) (TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/
# define TCO_DAT_IN(p) (TCOBASE(p) + 0x02) /* TCO Data In Register */
# define TCO_DAT_OUT(p) (TCOBASE(p) + 0x03) /* TCO Data Out Register */
# define TCO1_STS(p) (TCOBASE(p) + 0x04) /* TCO1 Status Register */
# define TCO2_STS(p) (TCOBASE(p) + 0x06) /* TCO2 Status Register */
# define TCO1_CNT(p) (TCOBASE(p) + 0x08) /* TCO1 Control Register */
# define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
# define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
2006-05-21 16:37:44 +04:00
/* internal variables */
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private {
struct watchdog_device wddev ;
2008-05-19 17:06:25 +04:00
/* TCO version/generation */
unsigned int iTCO_version ;
2012-04-20 23:14:11 +04:00
struct resource * tco_res ;
struct resource * smi_res ;
2014-03-11 01:34:55 +04:00
/*
* NO_REBOOT flag is Memory - Mapped GCS register bit 5 ( TCO version 2 ) ,
* or memory - mapped PMC register bit 4 ( TCO version 3 ) .
*/
struct resource * gcs_pmc_res ;
unsigned long __iomem * gcs_pmc ;
2008-05-19 17:06:25 +04:00
/* the lock for io operations */
spinlock_t io_lock ;
/* the PCI-device */
2017-01-02 20:27:36 +03:00
struct pci_dev * pci_dev ;
2015-04-03 16:25:04 +03:00
/* whether or not the watchdog has been suspended */
bool suspended ;
2017-04-10 01:00:19 +03:00
/* no reboot API private data */
void * no_reboot_priv ;
2017-04-10 01:00:18 +03:00
/* no reboot update function pointer */
int ( * update_no_reboot_bit ) ( void * p , bool set ) ;
2017-01-01 22:11:39 +03:00
} ;
2006-05-21 16:37:44 +04:00
/* module parameters */
2012-06-09 16:10:28 +04:00
# define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */
static int heartbeat = WATCHDOG_TIMEOUT ; /* in seconds */
2006-05-21 16:37:44 +04:00
module_param ( heartbeat , int , 0 ) ;
2010-04-19 16:38:25 +04:00
MODULE_PARM_DESC ( heartbeat , " Watchdog timeout in seconds. "
" 5..76 (TCO v1) or 3..614 (TCO v2), default= "
2012-06-09 16:10:28 +04:00
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " ) " ) ;
2006-05-21 16:37:44 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:06:25 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2006-11-12 20:05:09 +03:00
2011-12-26 18:23:51 +04:00
static int turn_SMI_watchdog_clear_off = 1 ;
2011-10-20 01:59:26 +04:00
module_param ( turn_SMI_watchdog_clear_off , int , 0 ) ;
MODULE_PARM_DESC ( turn_SMI_watchdog_clear_off ,
2011-12-26 18:23:51 +04:00
" Turn off SMI clearing watchdog (depends on TCO-version)(default=1) " ) ;
2011-10-20 01:59:26 +04:00
2006-05-21 16:37:44 +04:00
/*
* Some TCO specific functions
*/
2014-03-11 01:34:55 +04:00
/*
* The iTCO v1 and v2 ' s internal timer is stored as ticks which decrement
* every 0.6 seconds . v3 ' s internal timer is stored as seconds ( some
* datasheets incorrectly state 0.6 seconds ) .
*/
2017-01-01 22:11:39 +03:00
static inline unsigned int seconds_to_ticks ( struct iTCO_wdt_private * p ,
int secs )
2006-05-21 16:37:44 +04:00
{
2017-01-01 22:11:39 +03:00
return p - > iTCO_version = = 3 ? secs : ( secs * 10 ) / 6 ;
2014-03-11 01:34:55 +04:00
}
2017-01-01 22:11:39 +03:00
static inline unsigned int ticks_to_seconds ( struct iTCO_wdt_private * p ,
int ticks )
2014-03-11 01:34:55 +04:00
{
2017-01-01 22:11:39 +03:00
return p - > iTCO_version = = 3 ? ticks : ( ticks * 6 ) / 10 ;
2006-05-21 16:37:44 +04:00
}
2017-01-01 22:11:39 +03:00
static inline u32 no_reboot_bit ( struct iTCO_wdt_private * p )
2015-08-06 15:46:26 +03:00
{
u32 enable_bit ;
2017-01-01 22:11:39 +03:00
switch ( p - > iTCO_version ) {
2016-06-17 03:33:31 +03:00
case 5 :
2015-08-06 15:46:26 +03:00
case 3 :
enable_bit = 0x00000010 ;
break ;
case 2 :
enable_bit = 0x00000020 ;
break ;
case 4 :
case 1 :
default :
enable_bit = 0x00000002 ;
break ;
}
return enable_bit ;
}
2017-04-10 01:00:18 +03:00
static int update_no_reboot_bit_def ( void * priv , bool set )
2006-05-21 16:37:44 +04:00
{
2017-04-10 01:00:18 +03:00
return 0 ;
2006-05-21 16:37:44 +04:00
}
2017-04-10 01:00:18 +03:00
static int update_no_reboot_bit_pci ( void * priv , bool set )
2006-05-21 16:37:44 +04:00
{
2017-04-10 01:00:18 +03:00
struct iTCO_wdt_private * p = priv ;
u32 val32 = 0 , newval32 = 0 ;
2006-05-21 16:37:44 +04:00
2017-04-10 01:00:18 +03:00
pci_read_config_dword ( p - > pci_dev , 0xd4 , & val32 ) ;
if ( set )
val32 | = no_reboot_bit ( p ) ;
else
val32 & = ~ no_reboot_bit ( p ) ;
pci_write_config_dword ( p - > pci_dev , 0xd4 , val32 ) ;
pci_read_config_dword ( p - > pci_dev , 0xd4 , & newval32 ) ;
2006-05-21 16:37:44 +04:00
2017-04-10 01:00:18 +03:00
/* make sure the update is successful */
if ( val32 ! = newval32 )
return - EIO ;
2006-05-21 16:37:44 +04:00
2017-04-10 01:00:18 +03:00
return 0 ;
}
static int update_no_reboot_bit_mem ( void * priv , bool set )
{
struct iTCO_wdt_private * p = priv ;
u32 val32 = 0 , newval32 = 0 ;
val32 = readl ( p - > gcs_pmc ) ;
if ( set )
val32 | = no_reboot_bit ( p ) ;
else
val32 & = ~ no_reboot_bit ( p ) ;
writel ( val32 , p - > gcs_pmc ) ;
newval32 = readl ( p - > gcs_pmc ) ;
2006-05-21 16:37:44 +04:00
2017-04-10 01:00:18 +03:00
/* make sure the update is successful */
if ( val32 ! = newval32 )
2015-08-06 15:46:26 +03:00
return - EIO ;
return 0 ;
2006-05-21 16:37:44 +04:00
}
2017-04-10 01:00:19 +03:00
static void iTCO_wdt_no_reboot_bit_setup ( struct iTCO_wdt_private * p ,
struct itco_wdt_platform_data * pdata )
2017-04-10 01:00:18 +03:00
{
2017-04-10 01:00:19 +03:00
if ( pdata - > update_no_reboot_bit ) {
p - > update_no_reboot_bit = pdata - > update_no_reboot_bit ;
p - > no_reboot_priv = pdata - > no_reboot_priv ;
return ;
}
2017-04-10 01:00:18 +03:00
if ( p - > iTCO_version > = 2 )
p - > update_no_reboot_bit = update_no_reboot_bit_mem ;
else if ( p - > iTCO_version = = 1 )
p - > update_no_reboot_bit = update_no_reboot_bit_pci ;
else
p - > update_no_reboot_bit = update_no_reboot_bit_def ;
2017-04-10 01:00:19 +03:00
p - > no_reboot_priv = p ;
2017-04-10 01:00:18 +03:00
}
2012-06-09 16:10:28 +04:00
static int iTCO_wdt_start ( struct watchdog_device * wd_dev )
2006-05-21 16:37:44 +04:00
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = watchdog_get_drvdata ( wd_dev ) ;
2006-05-21 16:37:44 +04:00
unsigned int val ;
2017-01-01 22:11:39 +03:00
spin_lock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
iTCO_vendor_pre_start ( p - > smi_res , wd_dev - > timeout ) ;
2006-11-12 20:05:09 +03:00
2006-05-21 16:37:44 +04:00
/* disable chipset's NO_REBOOT bit */
2017-04-10 01:00:19 +03:00
if ( p - > update_no_reboot_bit ( p - > no_reboot_priv , false ) ) {
2017-01-01 22:11:39 +03:00
spin_unlock ( & p - > io_lock ) ;
2012-02-16 03:06:19 +04:00
pr_err ( " failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS \n " ) ;
2006-05-21 16:37:44 +04:00
return - EIO ;
}
2008-11-19 22:39:58 +03:00
/* Force the timer to its reload value by writing to the TCO_RLD
register */
2017-01-01 22:11:39 +03:00
if ( p - > iTCO_version > = 2 )
outw ( 0x01 , TCO_RLD ( p ) ) ;
else if ( p - > iTCO_version = = 1 )
outb ( 0x01 , TCO_RLD ( p ) ) ;
2008-11-19 22:39:58 +03:00
2006-05-21 16:37:44 +04:00
/* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
2017-01-01 22:11:39 +03:00
val = inw ( TCO1_CNT ( p ) ) ;
2006-05-21 16:37:44 +04:00
val & = 0xf7ff ;
2017-01-01 22:11:39 +03:00
outw ( val , TCO1_CNT ( p ) ) ;
val = inw ( TCO1_CNT ( p ) ) ;
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
if ( val & 0x0800 )
return - 1 ;
return 0 ;
}
2012-06-09 16:10:28 +04:00
static int iTCO_wdt_stop ( struct watchdog_device * wd_dev )
2006-05-21 16:37:44 +04:00
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = watchdog_get_drvdata ( wd_dev ) ;
2006-05-21 16:37:44 +04:00
unsigned int val ;
2017-01-01 22:11:39 +03:00
spin_lock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
iTCO_vendor_pre_stop ( p - > smi_res ) ;
2006-11-12 20:05:09 +03:00
2006-05-21 16:37:44 +04:00
/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
2017-01-01 22:11:39 +03:00
val = inw ( TCO1_CNT ( p ) ) ;
2006-05-21 16:37:44 +04:00
val | = 0x0800 ;
2017-01-01 22:11:39 +03:00
outw ( val , TCO1_CNT ( p ) ) ;
val = inw ( TCO1_CNT ( p ) ) ;
2006-05-21 16:37:44 +04:00
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
2017-04-10 01:00:19 +03:00
p - > update_no_reboot_bit ( p - > no_reboot_priv , true ) ;
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
if ( ( val & 0x0800 ) = = 0 )
return - 1 ;
return 0 ;
}
2012-06-09 16:10:28 +04:00
static int iTCO_wdt_ping ( struct watchdog_device * wd_dev )
2006-05-21 16:37:44 +04:00
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = watchdog_get_drvdata ( wd_dev ) ;
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
spin_lock ( & p - > io_lock ) ;
iTCO_vendor_pre_keepalive ( p - > smi_res , wd_dev - > timeout ) ;
2006-11-12 20:05:09 +03:00
2006-05-21 16:37:44 +04:00
/* Reload the timer by writing to the TCO Timer Counter register */
2017-09-09 18:41:24 +03:00
if ( p - > iTCO_version > = 2 ) {
2017-01-01 22:11:39 +03:00
outw ( 0x01 , TCO_RLD ( p ) ) ;
2017-09-09 18:41:24 +03:00
} else if ( p - > iTCO_version = = 1 ) {
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw ( 0x0008 , TCO1_STS ( p ) ) ; /* write 1 to clear bit */
2017-01-01 22:11:39 +03:00
outb ( 0x01 , TCO_RLD ( p ) ) ;
2017-09-09 18:41:24 +03:00
}
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
return 0 ;
}
2012-06-09 16:10:28 +04:00
static int iTCO_wdt_set_timeout ( struct watchdog_device * wd_dev , unsigned int t )
2006-05-21 16:37:44 +04:00
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = watchdog_get_drvdata ( wd_dev ) ;
2006-05-21 16:37:44 +04:00
unsigned int val16 ;
unsigned char val8 ;
unsigned int tmrval ;
2017-09-09 18:41:24 +03:00
tmrval = seconds_to_ticks ( p , t ) ;
/* For TCO v1 the timer counts down twice before rebooting */
if ( p - > iTCO_version = = 1 )
tmrval / = 2 ;
2010-04-19 16:38:25 +04:00
2006-05-21 16:37:44 +04:00
/* from the specs: */
/* "Values of 0h-3h are ignored and should not be attempted" */
if ( tmrval < 0x04 )
return - EINVAL ;
2017-01-01 22:11:39 +03:00
if ( ( p - > iTCO_version > = 2 & & tmrval > 0x3ff ) | |
( p - > iTCO_version = = 1 & & tmrval > 0x03f ) )
2006-05-21 16:37:44 +04:00
return - EINVAL ;
2006-11-12 20:05:09 +03:00
iTCO_vendor_pre_set_heartbeat ( tmrval ) ;
2006-05-21 16:37:44 +04:00
/* Write new heartbeat to watchdog */
2017-01-01 22:11:39 +03:00
if ( p - > iTCO_version > = 2 ) {
spin_lock ( & p - > io_lock ) ;
val16 = inw ( TCOv2_TMR ( p ) ) ;
2006-05-21 16:37:44 +04:00
val16 & = 0xfc00 ;
val16 | = tmrval ;
2017-01-01 22:11:39 +03:00
outw ( val16 , TCOv2_TMR ( p ) ) ;
val16 = inw ( TCOv2_TMR ( p ) ) ;
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
if ( ( val16 & 0x3ff ) ! = tmrval )
return - EINVAL ;
2017-01-01 22:11:39 +03:00
} else if ( p - > iTCO_version = = 1 ) {
spin_lock ( & p - > io_lock ) ;
val8 = inb ( TCOv1_TMR ( p ) ) ;
2006-05-21 16:37:44 +04:00
val8 & = 0xc0 ;
val8 | = ( tmrval & 0xff ) ;
2017-01-01 22:11:39 +03:00
outb ( val8 , TCOv1_TMR ( p ) ) ;
val8 = inb ( TCOv1_TMR ( p ) ) ;
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
if ( ( val8 & 0x3f ) ! = tmrval )
return - EINVAL ;
}
2012-06-09 16:10:28 +04:00
wd_dev - > timeout = t ;
2006-05-21 16:37:44 +04:00
return 0 ;
}
2012-06-09 16:10:28 +04:00
static unsigned int iTCO_wdt_get_timeleft ( struct watchdog_device * wd_dev )
2006-05-21 16:37:44 +04:00
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = watchdog_get_drvdata ( wd_dev ) ;
2006-05-21 16:37:44 +04:00
unsigned int val16 ;
unsigned char val8 ;
2012-06-09 16:10:28 +04:00
unsigned int time_left = 0 ;
2006-05-21 16:37:44 +04:00
/* read the TCO Timer */
2017-01-01 22:11:39 +03:00
if ( p - > iTCO_version > = 2 ) {
spin_lock ( & p - > io_lock ) ;
val16 = inw ( TCO_RLD ( p ) ) ;
2006-05-21 16:37:44 +04:00
val16 & = 0x3ff ;
2017-01-01 22:11:39 +03:00
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
time_left = ticks_to_seconds ( p , val16 ) ;
} else if ( p - > iTCO_version = = 1 ) {
spin_lock ( & p - > io_lock ) ;
val8 = inb ( TCO_RLD ( p ) ) ;
2006-05-21 16:37:44 +04:00
val8 & = 0x3f ;
2017-01-01 22:11:39 +03:00
if ( ! ( inw ( TCO1_STS ( p ) ) & 0x0008 ) )
val8 + = ( inb ( TCOv1_TMR ( p ) ) & 0x3f ) ;
spin_unlock ( & p - > io_lock ) ;
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
time_left = ticks_to_seconds ( p , val8 ) ;
2006-05-21 16:37:44 +04:00
}
2012-06-09 16:10:28 +04:00
return time_left ;
2006-05-21 16:37:44 +04:00
}
/*
* Kernel Interfaces
*/
2012-06-09 16:10:28 +04:00
static const struct watchdog_info ident = {
. options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE ,
. firmware_version = 0 ,
. identity = DRV_NAME ,
} ;
static const struct watchdog_ops iTCO_wdt_ops = {
2008-05-19 17:06:25 +04:00
. owner = THIS_MODULE ,
2012-06-09 16:10:28 +04:00
. start = iTCO_wdt_start ,
2014-02-27 09:41:42 +04:00
. stop = iTCO_wdt_stop ,
. ping = iTCO_wdt_ping ,
2012-06-09 16:10:28 +04:00
. set_timeout = iTCO_wdt_set_timeout ,
. get_timeleft = iTCO_wdt_get_timeleft ,
2006-05-21 16:37:44 +04:00
} ;
/*
* Init & exit routines
*/
2017-01-02 20:27:36 +03:00
static int iTCO_wdt_probe ( struct platform_device * pdev )
2006-05-21 16:37:44 +04:00
{
2017-01-02 20:27:36 +03:00
struct device * dev = & pdev - > dev ;
struct itco_wdt_platform_data * pdata = dev_get_platdata ( dev ) ;
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p ;
unsigned long val32 ;
int ret ;
2012-04-20 23:14:11 +04:00
2015-08-06 15:46:24 +03:00
if ( ! pdata )
2017-01-01 22:11:39 +03:00
return - ENODEV ;
2012-04-20 23:14:11 +04:00
2017-01-02 20:27:36 +03:00
p = devm_kzalloc ( dev , sizeof ( * p ) , GFP_KERNEL ) ;
2017-01-01 22:11:39 +03:00
if ( ! p )
return - ENOMEM ;
2012-04-20 23:14:11 +04:00
2017-01-01 22:11:39 +03:00
spin_lock_init ( & p - > io_lock ) ;
2012-04-20 23:14:11 +04:00
2017-01-02 20:27:36 +03:00
p - > tco_res = platform_get_resource ( pdev , IORESOURCE_IO , ICH_RES_IO_TCO ) ;
2017-01-01 22:11:39 +03:00
if ( ! p - > tco_res )
return - ENODEV ;
2012-04-20 23:14:11 +04:00
2017-01-02 20:27:36 +03:00
p - > smi_res = platform_get_resource ( pdev , IORESOURCE_IO , ICH_RES_IO_SMI ) ;
2017-01-01 22:11:39 +03:00
if ( ! p - > smi_res )
return - ENODEV ;
p - > iTCO_version = pdata - > version ;
2017-01-02 20:27:36 +03:00
p - > pci_dev = to_pci_dev ( dev - > parent ) ;
2006-05-21 16:37:44 +04:00
2017-04-10 01:00:19 +03:00
iTCO_wdt_no_reboot_bit_setup ( p , pdata ) ;
2017-04-10 01:00:18 +03:00
2006-05-21 16:37:44 +04:00
/*
2014-03-11 01:34:55 +04:00
* Get the Memory - Mapped GCS or PMC register , we need it for the
* NO_REBOOT flag ( TCO v2 and v3 ) .
2006-05-21 16:37:44 +04:00
*/
2017-04-10 01:00:19 +03:00
if ( p - > iTCO_version > = 2 & & ! pdata - > update_no_reboot_bit ) {
2017-01-02 20:27:36 +03:00
p - > gcs_pmc_res = platform_get_resource ( pdev ,
2017-01-01 22:11:39 +03:00
IORESOURCE_MEM ,
ICH_RES_MEM_GCS_PMC ) ;
2017-01-02 20:27:36 +03:00
p - > gcs_pmc = devm_ioremap_resource ( dev , p - > gcs_pmc_res ) ;
2017-01-01 21:39:09 +03:00
if ( IS_ERR ( p - > gcs_pmc ) )
return PTR_ERR ( p - > gcs_pmc ) ;
2006-05-21 16:37:44 +04:00
}
/* Check chipset's NO_REBOOT bit */
2017-04-10 01:00:19 +03:00
if ( p - > update_no_reboot_bit ( p - > no_reboot_priv , false ) & &
2017-01-01 22:11:39 +03:00
iTCO_vendor_check_noreboot_on ( ) ) {
2012-02-16 03:06:19 +04:00
pr_info ( " unable to reset NO_REBOOT flag, device disabled by hardware/BIOS \n " ) ;
2017-01-01 21:39:09 +03:00
return - ENODEV ; /* Cannot reset NO_REBOOT bit */
2006-05-21 16:37:44 +04:00
}
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
2017-04-10 01:00:19 +03:00
p - > update_no_reboot_bit ( p - > no_reboot_priv , true ) ;
2006-05-21 16:37:44 +04:00
2008-11-19 22:39:58 +03:00
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
2017-01-02 20:27:36 +03:00
if ( ! devm_request_region ( dev , p - > smi_res - > start ,
2017-01-01 21:39:09 +03:00
resource_size ( p - > smi_res ) ,
2017-01-02 20:27:36 +03:00
pdev - > name ) ) {
2012-04-20 23:14:11 +04:00
pr_err ( " I/O address 0x%04llx already in use, device disabled \n " ,
2017-01-01 22:11:39 +03:00
( u64 ) SMI_EN ( p ) ) ;
2017-01-01 21:39:09 +03:00
return - EBUSY ;
2006-05-21 16:37:44 +04:00
}
2017-01-01 22:11:39 +03:00
if ( turn_SMI_watchdog_clear_off > = p - > iTCO_version ) {
2012-04-20 23:14:11 +04:00
/*
* Bit 13 : TCO_EN - > 0
* Disables TCO logic generating an SMI #
*/
2017-01-01 22:11:39 +03:00
val32 = inl ( SMI_EN ( p ) ) ;
2011-10-20 01:59:26 +04:00
val32 & = 0xffffdfff ; /* Turn off SMI clearing watchdog */
2017-01-01 22:11:39 +03:00
outl ( val32 , SMI_EN ( p ) ) ;
2011-10-20 01:59:26 +04:00
}
2006-05-21 16:37:44 +04:00
2017-01-02 20:27:36 +03:00
if ( ! devm_request_region ( dev , p - > tco_res - > start ,
2017-01-01 21:39:09 +03:00
resource_size ( p - > tco_res ) ,
2017-01-02 20:27:36 +03:00
pdev - > name ) ) {
2012-04-20 23:14:11 +04:00
pr_err ( " I/O address 0x%04llx already in use, device disabled \n " ,
2017-01-01 22:11:39 +03:00
( u64 ) TCOBASE ( p ) ) ;
2017-01-01 21:39:09 +03:00
return - EBUSY ;
2006-05-21 16:37:44 +04:00
}
2012-04-20 23:14:11 +04:00
pr_info ( " Found a %s TCO device (Version=%d, TCOBASE=0x%04llx) \n " ,
2017-01-01 22:11:39 +03:00
pdata - > name , pdata - > version , ( u64 ) TCOBASE ( p ) ) ;
2006-05-21 16:37:44 +04:00
/* Clear out the (probably old) status */
2017-01-01 22:11:39 +03:00
switch ( p - > iTCO_version ) {
2016-06-17 03:33:31 +03:00
case 5 :
2015-08-06 15:46:26 +03:00
case 4 :
2017-01-01 22:11:39 +03:00
outw ( 0x0008 , TCO1_STS ( p ) ) ; /* Clear the Time Out Status bit */
outw ( 0x0002 , TCO2_STS ( p ) ) ; /* Clear SECOND_TO_STS bit */
2015-08-06 15:46:26 +03:00
break ;
case 3 :
2017-01-01 22:11:39 +03:00
outl ( 0x20008 , TCO1_STS ( p ) ) ;
2015-08-06 15:46:26 +03:00
break ;
case 2 :
case 1 :
default :
2017-01-01 22:11:39 +03:00
outw ( 0x0008 , TCO1_STS ( p ) ) ; /* Clear the Time Out Status bit */
outw ( 0x0002 , TCO2_STS ( p ) ) ; /* Clear SECOND_TO_STS bit */
outw ( 0x0004 , TCO2_STS ( p ) ) ; /* Clear BOOT_STS bit */
2015-08-06 15:46:26 +03:00
break ;
2014-03-11 01:34:55 +04:00
}
2006-05-21 16:37:44 +04:00
2017-01-01 22:11:39 +03:00
p - > wddev . info = & ident ,
p - > wddev . ops = & iTCO_wdt_ops ,
p - > wddev . bootstatus = 0 ;
p - > wddev . timeout = WATCHDOG_TIMEOUT ;
watchdog_set_nowayout ( & p - > wddev , nowayout ) ;
2017-01-02 20:27:36 +03:00
p - > wddev . parent = dev ;
2017-01-01 22:11:39 +03:00
watchdog_set_drvdata ( & p - > wddev , p ) ;
2017-01-02 20:27:36 +03:00
platform_set_drvdata ( pdev , p ) ;
2012-06-09 16:10:28 +04:00
2006-05-21 16:37:44 +04:00
/* Make sure the watchdog is not running */
2017-01-01 22:11:39 +03:00
iTCO_wdt_stop ( & p - > wddev ) ;
2006-05-21 16:37:44 +04:00
2008-05-19 17:06:25 +04:00
/* Check that the heartbeat value is within it's range;
if not reset to the default */
2017-01-01 22:11:39 +03:00
if ( iTCO_wdt_set_timeout ( & p - > wddev , heartbeat ) ) {
iTCO_wdt_set_timeout ( & p - > wddev , WATCHDOG_TIMEOUT ) ;
2012-06-09 16:10:28 +04:00
pr_info ( " timeout value out of range, using %d \n " ,
WATCHDOG_TIMEOUT ) ;
2006-05-21 16:37:44 +04:00
}
2017-01-11 02:21:49 +03:00
watchdog_stop_on_reboot ( & p - > wddev ) ;
2017-01-02 20:27:36 +03:00
ret = devm_watchdog_register_device ( dev , & p - > wddev ) ;
2006-05-21 16:37:44 +04:00
if ( ret ! = 0 ) {
2012-06-09 16:10:28 +04:00
pr_err ( " cannot register watchdog device (err=%d) \n " , ret ) ;
2017-01-01 21:39:09 +03:00
return ret ;
2006-05-21 16:37:44 +04:00
}
2012-02-16 03:06:19 +04:00
pr_info ( " initialized. heartbeat=%d sec (nowayout=%d) \n " ,
heartbeat , nowayout ) ;
2006-05-21 16:37:44 +04:00
return 0 ;
}
2017-01-02 20:27:36 +03:00
static int iTCO_wdt_remove ( struct platform_device * pdev )
2006-05-21 16:37:44 +04:00
{
2017-01-02 20:27:36 +03:00
struct iTCO_wdt_private * p = platform_get_drvdata ( pdev ) ;
2017-01-01 22:11:39 +03:00
2017-01-01 21:39:09 +03:00
/* Stop the timer before we leave */
if ( ! nowayout )
iTCO_wdt_stop ( & p - > wddev ) ;
2006-05-21 16:37:44 +04:00
2006-06-30 10:44:53 +04:00
return 0 ;
}
2015-04-03 16:25:04 +03:00
# ifdef CONFIG_PM_SLEEP
/*
* Suspend - to - idle requires this , because it stops the ticks and timekeeping , so
* the watchdog cannot be pinged while in that state . In ACPI sleep states the
* watchdog is stopped by the platform firmware .
*/
# ifdef CONFIG_ACPI
static inline bool need_suspend ( void )
{
return acpi_target_system_state ( ) = = ACPI_STATE_S0 ;
}
# else
static inline bool need_suspend ( void ) { return true ; }
# endif
static int iTCO_wdt_suspend_noirq ( struct device * dev )
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = dev_get_drvdata ( dev ) ;
2015-04-03 16:25:04 +03:00
int ret = 0 ;
2017-01-01 22:11:39 +03:00
p - > suspended = false ;
if ( watchdog_active ( & p - > wddev ) & & need_suspend ( ) ) {
ret = iTCO_wdt_stop ( & p - > wddev ) ;
2015-04-03 16:25:04 +03:00
if ( ! ret )
2017-01-01 22:11:39 +03:00
p - > suspended = true ;
2015-04-03 16:25:04 +03:00
}
return ret ;
}
static int iTCO_wdt_resume_noirq ( struct device * dev )
{
2017-01-01 22:11:39 +03:00
struct iTCO_wdt_private * p = dev_get_drvdata ( dev ) ;
if ( p - > suspended )
iTCO_wdt_start ( & p - > wddev ) ;
2015-04-03 16:25:04 +03:00
return 0 ;
}
2016-08-28 23:26:26 +03:00
static const struct dev_pm_ops iTCO_wdt_pm = {
2015-04-03 16:25:04 +03:00
. suspend_noirq = iTCO_wdt_suspend_noirq ,
. resume_noirq = iTCO_wdt_resume_noirq ,
} ;
# define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
# else
# define ITCO_WDT_PM_OPS NULL
# endif /* CONFIG_PM_SLEEP */
2006-06-30 10:44:53 +04:00
static struct platform_driver iTCO_wdt_driver = {
. probe = iTCO_wdt_probe ,
2012-11-19 22:21:12 +04:00
. remove = iTCO_wdt_remove ,
2006-06-30 10:44:53 +04:00
. driver = {
. name = DRV_NAME ,
2015-04-03 16:25:04 +03:00
. pm = ITCO_WDT_PM_OPS ,
2006-06-30 10:44:53 +04:00
} ,
} ;
static int __init iTCO_wdt_init_module ( void )
{
2012-02-16 03:06:19 +04:00
pr_info ( " Intel TCO WatchDog Timer Driver v%s \n " , DRV_VERSION ) ;
2006-06-30 10:44:53 +04:00
2017-01-03 13:43:32 +03:00
return platform_driver_register ( & iTCO_wdt_driver ) ;
2006-06-30 10:44:53 +04:00
}
static void __exit iTCO_wdt_cleanup_module ( void )
{
platform_driver_unregister ( & iTCO_wdt_driver ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog Module Unloaded \n " ) ;
2006-05-21 16:37:44 +04:00
}
module_init ( iTCO_wdt_init_module ) ;
module_exit ( iTCO_wdt_cleanup_module ) ;
MODULE_AUTHOR ( " Wim Van Sebroeck <wim@iguana.be> " ) ;
MODULE_DESCRIPTION ( " Intel TCO WatchDog Timer Driver " ) ;
2006-06-30 10:44:53 +04:00
MODULE_VERSION ( DRV_VERSION ) ;
2006-05-21 16:37:44 +04:00
MODULE_LICENSE ( " GPL " ) ;
2012-06-22 19:41:00 +04:00
MODULE_ALIAS ( " platform: " DRV_NAME ) ;