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 */
2012-04-20 23:14:11 +04:00
# define TCOBASE (iTCO_wdt_private.tco_res->start)
2008-05-19 17:06:25 +04:00
/* SMI Control and Enable Register */
2012-04-20 23:14:11 +04:00
# define SMI_EN (iTCO_wdt_private.smi_res->start)
2006-05-21 16:37:44 +04:00
2009-04-15 00:20:07 +04:00
# define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */
# define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */
# define TCO_DAT_IN (TCOBASE + 0x02) /* TCO Data In Register */
# define TCO_DAT_OUT (TCOBASE + 0x03) /* TCO Data Out Register */
# define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */
# define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */
# define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */
# define TCO2_CNT (TCOBASE + 0x0a) /* TCO2 Control Register */
# define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */
2006-05-21 16:37:44 +04:00
/* internal variables */
2008-05-19 17:06:25 +04:00
static struct { /* this is private data for the iTCO_wdt device */
/* 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 ;
2012-04-20 23:14:11 +04:00
struct platform_device * dev ;
2008-05-19 17:06:25 +04:00
/* the PCI-device */
struct pci_dev * pdev ;
2015-04-03 16:25:04 +03:00
/* whether or not the watchdog has been suspended */
bool suspended ;
2006-05-21 16:37:44 +04:00
} iTCO_wdt_private ;
/* 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 ) .
*/
static inline unsigned int seconds_to_ticks ( int secs )
2006-05-21 16:37:44 +04:00
{
2014-03-11 01:34:55 +04:00
return iTCO_wdt_private . iTCO_version = = 3 ? secs : ( secs * 10 ) / 6 ;
}
static inline unsigned int ticks_to_seconds ( int ticks )
{
return iTCO_wdt_private . iTCO_version = = 3 ? ticks : ( ticks * 6 ) / 10 ;
2006-05-21 16:37:44 +04:00
}
2015-08-06 15:46:26 +03:00
static inline u32 no_reboot_bit ( void )
{
u32 enable_bit ;
switch ( iTCO_wdt_private . 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 ;
}
2006-05-21 16:37:44 +04:00
static void iTCO_wdt_set_NO_REBOOT_bit ( void )
{
u32 val32 ;
/* Set the NO_REBOOT bit: this disables reboots */
2015-08-06 15:46:26 +03:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
2014-03-11 01:34:55 +04:00
val32 = readl ( iTCO_wdt_private . gcs_pmc ) ;
2015-08-06 15:46:26 +03:00
val32 | = no_reboot_bit ( ) ;
2014-03-11 01:34:55 +04:00
writel ( val32 , iTCO_wdt_private . gcs_pmc ) ;
2006-05-21 16:37:44 +04:00
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
pci_read_config_dword ( iTCO_wdt_private . pdev , 0xd4 , & val32 ) ;
2015-08-06 15:46:26 +03:00
val32 | = no_reboot_bit ( ) ;
2006-05-21 16:37:44 +04:00
pci_write_config_dword ( iTCO_wdt_private . pdev , 0xd4 , val32 ) ;
}
}
static int iTCO_wdt_unset_NO_REBOOT_bit ( void )
{
2015-08-06 15:46:26 +03:00
u32 enable_bit = no_reboot_bit ( ) ;
u32 val32 = 0 ;
2006-05-21 16:37:44 +04:00
/* Unset the NO_REBOOT bit: this enables reboots */
2015-08-06 15:46:26 +03:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
2014-03-11 01:34:55 +04:00
val32 = readl ( iTCO_wdt_private . gcs_pmc ) ;
2015-08-06 15:46:26 +03:00
val32 & = ~ enable_bit ;
2014-03-11 01:34:55 +04:00
writel ( val32 , iTCO_wdt_private . gcs_pmc ) ;
2006-05-21 16:37:44 +04:00
2014-03-11 01:34:55 +04:00
val32 = readl ( iTCO_wdt_private . gcs_pmc ) ;
2006-05-21 16:37:44 +04:00
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
pci_read_config_dword ( iTCO_wdt_private . pdev , 0xd4 , & val32 ) ;
2015-08-06 15:46:26 +03:00
val32 & = ~ enable_bit ;
2006-05-21 16:37:44 +04:00
pci_write_config_dword ( iTCO_wdt_private . pdev , 0xd4 , val32 ) ;
pci_read_config_dword ( iTCO_wdt_private . pdev , 0xd4 , & val32 ) ;
}
2015-08-06 15:46:26 +03:00
if ( val32 & enable_bit )
return - EIO ;
return 0 ;
2006-05-21 16:37:44 +04: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
{
unsigned int val ;
spin_lock ( & iTCO_wdt_private . io_lock ) ;
2012-06-09 16:10:28 +04:00
iTCO_vendor_pre_start ( iTCO_wdt_private . 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 */
if ( iTCO_wdt_unset_NO_REBOOT_bit ( ) ) {
2007-10-23 05:08:27 +04:00
spin_unlock ( & iTCO_wdt_private . 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 */
2014-03-11 01:34:55 +04:00
if ( iTCO_wdt_private . iTCO_version > = 2 )
2008-11-19 22:39:58 +03:00
outw ( 0x01 , TCO_RLD ) ;
else if ( iTCO_wdt_private . iTCO_version = = 1 )
outb ( 0x01 , TCO_RLD ) ;
2006-05-21 16:37:44 +04:00
/* Bit 11: TCO Timer Halt -> 0 = The TCO timer is enabled to count */
val = inw ( TCO1_CNT ) ;
val & = 0xf7ff ;
outw ( val , TCO1_CNT ) ;
val = inw ( TCO1_CNT ) ;
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
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
{
unsigned int val ;
spin_lock ( & iTCO_wdt_private . io_lock ) ;
2012-04-20 23:14:11 +04:00
iTCO_vendor_pre_stop ( iTCO_wdt_private . 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 */
val = inw ( TCO1_CNT ) ;
val | = 0x0800 ;
outw ( val , TCO1_CNT ) ;
val = inw ( TCO1_CNT ) ;
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit ( ) ;
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
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
{
spin_lock ( & iTCO_wdt_private . io_lock ) ;
2012-06-09 16:10:28 +04:00
iTCO_vendor_pre_keepalive ( iTCO_wdt_private . 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 */
2014-03-11 01:34:55 +04:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
2006-05-21 16:37:44 +04:00
outw ( 0x01 , TCO_RLD ) ;
2014-03-11 01:34:55 +04:00
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
2010-04-19 16:38:25 +04:00
/* Reset the timeout status bit so that the timer
* needs to count down twice again before rebooting */
outw ( 0x0008 , TCO1_STS ) ; /* write 1 to clear bit */
2006-05-21 16:37:44 +04:00
outb ( 0x01 , TCO_RLD ) ;
2010-04-19 16:38:25 +04:00
}
2006-05-21 16:37:44 +04:00
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
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
{
unsigned int val16 ;
unsigned char val8 ;
unsigned int tmrval ;
tmrval = seconds_to_ticks ( t ) ;
2010-04-19 16:38:25 +04:00
/* For TCO v1 the timer counts down twice before rebooting */
if ( iTCO_wdt_private . iTCO_version = = 1 )
tmrval / = 2 ;
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 ;
2014-03-11 01:34:55 +04:00
if ( ( ( iTCO_wdt_private . iTCO_version > = 2 ) & & ( tmrval > 0x3ff ) ) | |
2006-05-21 16:37:44 +04:00
( ( iTCO_wdt_private . iTCO_version = = 1 ) & & ( tmrval > 0x03f ) ) )
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 */
2014-03-11 01:34:55 +04:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
2006-05-21 16:37:44 +04:00
spin_lock ( & iTCO_wdt_private . io_lock ) ;
val16 = inw ( TCOv2_TMR ) ;
val16 & = 0xfc00 ;
val16 | = tmrval ;
outw ( val16 , TCOv2_TMR ) ;
val16 = inw ( TCOv2_TMR ) ;
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
if ( ( val16 & 0x3ff ) ! = tmrval )
return - EINVAL ;
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
spin_lock ( & iTCO_wdt_private . io_lock ) ;
val8 = inb ( TCOv1_TMR ) ;
val8 & = 0xc0 ;
val8 | = ( tmrval & 0xff ) ;
outb ( val8 , TCOv1_TMR ) ;
val8 = inb ( TCOv1_TMR ) ;
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
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
{
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 */
2014-03-11 01:34:55 +04:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
2006-05-21 16:37:44 +04:00
spin_lock ( & iTCO_wdt_private . io_lock ) ;
val16 = inw ( TCO_RLD ) ;
val16 & = 0x3ff ;
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
2014-03-11 01:34:55 +04:00
time_left = ticks_to_seconds ( val16 ) ;
2006-05-21 16:37:44 +04:00
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
spin_lock ( & iTCO_wdt_private . io_lock ) ;
val8 = inb ( TCO_RLD ) ;
val8 & = 0x3f ;
2010-04-19 16:38:25 +04:00
if ( ! ( inw ( TCO1_STS ) & 0x0008 ) )
val8 + = ( inb ( TCOv1_TMR ) & 0x3f ) ;
2006-05-21 16:37:44 +04:00
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
2014-03-11 01:34:55 +04:00
time_left = ticks_to_seconds ( 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
} ;
2012-06-09 16:10:28 +04:00
static struct watchdog_device iTCO_wdt_watchdog_dev = {
. info = & ident ,
2014-02-27 09:41:42 +04:00
. ops = & iTCO_wdt_ops ,
2006-05-21 16:37:44 +04:00
} ;
/*
* Init & exit routines
*/
2012-11-19 22:26:24 +04:00
static void iTCO_wdt_cleanup ( void )
2012-04-20 23:14:11 +04:00
{
/* Stop the timer before we leave */
if ( ! nowayout )
2012-06-09 16:10:28 +04:00
iTCO_wdt_stop ( & iTCO_wdt_watchdog_dev ) ;
2012-04-20 23:14:11 +04:00
/* Deregister */
2012-06-09 16:10:28 +04:00
watchdog_unregister_device ( & iTCO_wdt_watchdog_dev ) ;
2012-04-20 23:14:11 +04:00
/* release resources */
release_region ( iTCO_wdt_private . tco_res - > start ,
resource_size ( iTCO_wdt_private . tco_res ) ) ;
release_region ( iTCO_wdt_private . smi_res - > start ,
resource_size ( iTCO_wdt_private . smi_res ) ) ;
2014-03-11 01:34:55 +04:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
iounmap ( iTCO_wdt_private . gcs_pmc ) ;
release_mem_region ( iTCO_wdt_private . gcs_pmc_res - > start ,
resource_size ( iTCO_wdt_private . gcs_pmc_res ) ) ;
2012-04-20 23:14:11 +04:00
}
iTCO_wdt_private . tco_res = NULL ;
iTCO_wdt_private . smi_res = NULL ;
2014-03-11 01:34:55 +04:00
iTCO_wdt_private . gcs_pmc_res = NULL ;
iTCO_wdt_private . gcs_pmc = NULL ;
2012-04-20 23:14:11 +04:00
}
2012-11-19 22:21:41 +04:00
static int iTCO_wdt_probe ( struct platform_device * dev )
2006-05-21 16:37:44 +04:00
{
2012-04-20 23:14:11 +04:00
int ret = - ENODEV ;
2009-01-28 23:51:04 +03:00
unsigned long val32 ;
2015-08-06 15:46:24 +03:00
struct itco_wdt_platform_data * pdata = dev_get_platdata ( & dev - > dev ) ;
2012-04-20 23:14:11 +04:00
2015-08-06 15:46:24 +03:00
if ( ! pdata )
2012-04-20 23:14:11 +04:00
goto out ;
spin_lock_init ( & iTCO_wdt_private . io_lock ) ;
iTCO_wdt_private . tco_res =
platform_get_resource ( dev , IORESOURCE_IO , ICH_RES_IO_TCO ) ;
if ( ! iTCO_wdt_private . tco_res )
goto out ;
iTCO_wdt_private . smi_res =
platform_get_resource ( dev , IORESOURCE_IO , ICH_RES_IO_SMI ) ;
if ( ! iTCO_wdt_private . smi_res )
goto out ;
2015-08-06 15:46:24 +03:00
iTCO_wdt_private . iTCO_version = pdata - > version ;
2012-04-20 23:14:11 +04:00
iTCO_wdt_private . dev = dev ;
iTCO_wdt_private . pdev = to_pci_dev ( dev - > dev . parent ) ;
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
*/
2014-03-11 01:34:55 +04:00
if ( iTCO_wdt_private . iTCO_version > = 2 ) {
iTCO_wdt_private . gcs_pmc_res = platform_get_resource ( dev ,
2012-04-20 23:14:11 +04:00
IORESOURCE_MEM ,
2014-03-11 01:34:55 +04:00
ICH_RES_MEM_GCS_PMC ) ;
2012-04-20 23:14:11 +04:00
2014-03-11 01:34:55 +04:00
if ( ! iTCO_wdt_private . gcs_pmc_res )
2012-04-20 23:14:11 +04:00
goto out ;
2014-03-11 01:34:55 +04:00
if ( ! request_mem_region ( iTCO_wdt_private . gcs_pmc_res - > start ,
resource_size ( iTCO_wdt_private . gcs_pmc_res ) , dev - > name ) ) {
2012-04-20 23:14:11 +04:00
ret = - EBUSY ;
2009-06-05 15:13:08 +04:00
goto out ;
}
2014-03-11 01:34:55 +04:00
iTCO_wdt_private . gcs_pmc = ioremap ( iTCO_wdt_private . gcs_pmc_res - > start ,
resource_size ( iTCO_wdt_private . gcs_pmc_res ) ) ;
if ( ! iTCO_wdt_private . gcs_pmc ) {
2012-04-20 23:14:11 +04:00
ret = - EIO ;
2014-03-11 01:34:55 +04:00
goto unreg_gcs_pmc ;
2012-04-20 23:14:11 +04:00
}
2006-05-21 16:37:44 +04:00
}
/* Check chipset's NO_REBOOT bit */
2006-11-12 20:05:09 +03:00
if ( iTCO_wdt_unset_NO_REBOOT_bit ( ) & & 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 " ) ;
2006-05-21 16:37:44 +04:00
ret = - ENODEV ; /* Cannot reset NO_REBOOT bit */
2014-03-11 01:34:55 +04:00
goto unmap_gcs_pmc ;
2006-05-21 16:37:44 +04:00
}
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit ( ) ;
2008-11-19 22:39:58 +03:00
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
2012-04-20 23:14:11 +04:00
if ( ! request_region ( iTCO_wdt_private . smi_res - > start ,
resource_size ( iTCO_wdt_private . smi_res ) , dev - > name ) ) {
pr_err ( " I/O address 0x%04llx already in use, device disabled \n " ,
2012-05-15 00:15:20 +04:00
( u64 ) SMI_EN ) ;
2012-04-20 23:14:11 +04:00
ret = - EBUSY ;
2014-03-11 01:34:55 +04:00
goto unmap_gcs_pmc ;
2006-05-21 16:37:44 +04:00
}
2011-12-26 18:23:51 +04:00
if ( turn_SMI_watchdog_clear_off > = iTCO_wdt_private . iTCO_version ) {
2012-04-20 23:14:11 +04:00
/*
* Bit 13 : TCO_EN - > 0
* Disables TCO logic generating an SMI #
*/
2011-10-20 01:59:26 +04:00
val32 = inl ( SMI_EN ) ;
val32 & = 0xffffdfff ; /* Turn off SMI clearing watchdog */
outl ( val32 , SMI_EN ) ;
}
2006-05-21 16:37:44 +04:00
2012-04-20 23:14:11 +04:00
if ( ! request_region ( iTCO_wdt_private . tco_res - > start ,
resource_size ( iTCO_wdt_private . tco_res ) , dev - > name ) ) {
pr_err ( " I/O address 0x%04llx already in use, device disabled \n " ,
2012-05-15 00:15:20 +04:00
( u64 ) TCOBASE ) ;
2012-04-20 23:14:11 +04:00
ret = - EBUSY ;
goto unreg_smi ;
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 " ,
2015-08-06 15:46:24 +03:00
pdata - > name , pdata - > version , ( u64 ) TCOBASE ) ;
2006-05-21 16:37:44 +04:00
/* Clear out the (probably old) status */
2015-08-06 15:46:26 +03:00
switch ( iTCO_wdt_private . iTCO_version ) {
2016-06-17 03:33:31 +03:00
case 5 :
2015-08-06 15:46:26 +03:00
case 4 :
outw ( 0x0008 , TCO1_STS ) ; /* Clear the Time Out Status bit */
outw ( 0x0002 , TCO2_STS ) ; /* Clear SECOND_TO_STS bit */
break ;
case 3 :
2014-03-11 01:34:55 +04:00
outl ( 0x20008 , TCO1_STS ) ;
2015-08-06 15:46:26 +03:00
break ;
case 2 :
case 1 :
default :
2014-03-11 01:34:55 +04:00
outw ( 0x0008 , TCO1_STS ) ; /* Clear the Time Out Status bit */
outw ( 0x0002 , TCO2_STS ) ; /* Clear SECOND_TO_STS bit */
outw ( 0x0004 , TCO2_STS ) ; /* 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
2012-06-09 16:10:28 +04:00
iTCO_wdt_watchdog_dev . bootstatus = 0 ;
iTCO_wdt_watchdog_dev . timeout = WATCHDOG_TIMEOUT ;
watchdog_set_nowayout ( & iTCO_wdt_watchdog_dev , nowayout ) ;
2014-03-11 00:28:17 +04:00
iTCO_wdt_watchdog_dev . parent = & dev - > dev ;
2012-06-09 16:10:28 +04:00
2006-05-21 16:37:44 +04:00
/* Make sure the watchdog is not running */
2012-06-09 16:10:28 +04:00
iTCO_wdt_stop ( & iTCO_wdt_watchdog_dev ) ;
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 */
2012-06-09 16:10:28 +04:00
if ( iTCO_wdt_set_timeout ( & iTCO_wdt_watchdog_dev , heartbeat ) ) {
iTCO_wdt_set_timeout ( & iTCO_wdt_watchdog_dev , WATCHDOG_TIMEOUT ) ;
pr_info ( " timeout value out of range, using %d \n " ,
WATCHDOG_TIMEOUT ) ;
2006-05-21 16:37:44 +04:00
}
2012-06-09 16:10:28 +04:00
ret = watchdog_register_device ( & iTCO_wdt_watchdog_dev ) ;
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 ) ;
2012-04-20 23:14:11 +04:00
goto unreg_tco ;
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 ;
2012-04-20 23:14:11 +04:00
unreg_tco :
release_region ( iTCO_wdt_private . tco_res - > start ,
resource_size ( iTCO_wdt_private . tco_res ) ) ;
unreg_smi :
release_region ( iTCO_wdt_private . smi_res - > start ,
resource_size ( iTCO_wdt_private . smi_res ) ) ;
2014-03-11 01:34:55 +04:00
unmap_gcs_pmc :
if ( iTCO_wdt_private . iTCO_version > = 2 )
iounmap ( iTCO_wdt_private . gcs_pmc ) ;
unreg_gcs_pmc :
if ( iTCO_wdt_private . iTCO_version > = 2 )
release_mem_region ( iTCO_wdt_private . gcs_pmc_res - > start ,
resource_size ( iTCO_wdt_private . gcs_pmc_res ) ) ;
2012-04-20 23:14:11 +04:00
out :
iTCO_wdt_private . tco_res = NULL ;
iTCO_wdt_private . smi_res = NULL ;
2014-03-11 01:34:55 +04:00
iTCO_wdt_private . gcs_pmc_res = NULL ;
iTCO_wdt_private . gcs_pmc = NULL ;
2006-05-21 16:37:44 +04:00
2010-02-09 02:42:02 +03:00
return ret ;
2006-05-21 16:37:44 +04:00
}
2012-11-19 22:26:24 +04:00
static int iTCO_wdt_remove ( struct platform_device * dev )
2006-05-21 16:37:44 +04:00
{
2012-04-20 23:14:11 +04:00
if ( iTCO_wdt_private . tco_res | | iTCO_wdt_private . smi_res )
2006-05-21 16:37:44 +04:00
iTCO_wdt_cleanup ( ) ;
2006-06-30 10:44:53 +04:00
return 0 ;
}
static void iTCO_wdt_shutdown ( struct platform_device * dev )
{
2012-06-09 16:10:28 +04:00
iTCO_wdt_stop ( NULL ) ;
2006-06-30 10:44:53 +04:00
}
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 )
{
int ret = 0 ;
iTCO_wdt_private . suspended = false ;
if ( watchdog_active ( & iTCO_wdt_watchdog_dev ) & & need_suspend ( ) ) {
ret = iTCO_wdt_stop ( & iTCO_wdt_watchdog_dev ) ;
if ( ! ret )
iTCO_wdt_private . suspended = true ;
}
return ret ;
}
static int iTCO_wdt_resume_noirq ( struct device * dev )
{
if ( iTCO_wdt_private . suspended )
iTCO_wdt_start ( & iTCO_wdt_watchdog_dev ) ;
return 0 ;
}
static struct dev_pm_ops iTCO_wdt_pm = {
. 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
. shutdown = iTCO_wdt_shutdown ,
. 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 )
{
int err ;
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
err = platform_driver_register ( & iTCO_wdt_driver ) ;
if ( err )
return err ;
return 0 ;
}
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 ) ;