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"
2012-06-09 16:10:28 +04:00
# define DRV_VERSION "1.10"
2006-05-21 16:37:44 +04:00
/* Includes */
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/... */
2012-04-20 23:14:11 +04:00
# include <linux/mfd/core.h>
# include <linux/mfd/lpc_ich.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 ;
struct resource * gcs_res ;
2008-05-19 17:06:25 +04:00
/* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
unsigned long __iomem * gcs ;
/* 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 ;
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
*/
static inline unsigned int seconds_to_ticks ( int seconds )
{
/* the internal timer is stored as ticks which decrement
* every 0.6 seconds */
return ( seconds * 10 ) / 6 ;
}
static void iTCO_wdt_set_NO_REBOOT_bit ( void )
{
u32 val32 ;
/* Set the NO_REBOOT bit: this disables reboots */
if ( iTCO_wdt_private . iTCO_version = = 2 ) {
val32 = readl ( iTCO_wdt_private . gcs ) ;
val32 | = 0x00000020 ;
writel ( val32 , iTCO_wdt_private . gcs ) ;
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
pci_read_config_dword ( iTCO_wdt_private . pdev , 0xd4 , & val32 ) ;
val32 | = 0x00000002 ;
pci_write_config_dword ( iTCO_wdt_private . pdev , 0xd4 , val32 ) ;
}
}
static int iTCO_wdt_unset_NO_REBOOT_bit ( void )
{
int ret = 0 ;
u32 val32 ;
/* Unset the NO_REBOOT bit: this enables reboots */
if ( iTCO_wdt_private . iTCO_version = = 2 ) {
val32 = readl ( iTCO_wdt_private . gcs ) ;
val32 & = 0xffffffdf ;
writel ( val32 , iTCO_wdt_private . gcs ) ;
val32 = readl ( iTCO_wdt_private . gcs ) ;
if ( val32 & 0x00000020 )
ret = - EIO ;
} else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
pci_read_config_dword ( iTCO_wdt_private . pdev , 0xd4 , & val32 ) ;
val32 & = 0xfffffffd ;
pci_write_config_dword ( iTCO_wdt_private . pdev , 0xd4 , val32 ) ;
pci_read_config_dword ( iTCO_wdt_private . pdev , 0xd4 , & val32 ) ;
if ( val32 & 0x00000002 )
ret = - EIO ;
}
return ret ; /* returns: 0 = OK, -EIO = Error */
}
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 */
if ( iTCO_wdt_private . iTCO_version = = 2 )
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 */
2008-05-19 17:06:25 +04:00
if ( iTCO_wdt_private . iTCO_version = = 2 )
2006-05-21 16:37:44 +04:00
outw ( 0x01 , TCO_RLD ) ;
2010-04-19 16:38:25 +04:00
else if ( iTCO_wdt_private . iTCO_version = = 1 ) {
/* 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 ;
if ( ( ( iTCO_wdt_private . iTCO_version = = 2 ) & & ( tmrval > 0x3ff ) ) | |
( ( 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 */
if ( iTCO_wdt_private . iTCO_version = = 2 ) {
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 */
if ( iTCO_wdt_private . iTCO_version = = 2 ) {
spin_lock ( & iTCO_wdt_private . io_lock ) ;
val16 = inw ( TCO_RLD ) ;
val16 & = 0x3ff ;
spin_unlock ( & iTCO_wdt_private . io_lock ) ;
2012-06-09 16:10:28 +04:00
time_left = ( val16 * 6 ) / 10 ;
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 ) ;
2012-06-09 16:10:28 +04:00
time_left = ( val8 * 6 ) / 10 ;
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 ,
. stop = iTCO_wdt_stop ,
. ping = iTCO_wdt_ping ,
. 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 ,
. 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 ) ) ;
if ( iTCO_wdt_private . iTCO_version = = 2 ) {
iounmap ( iTCO_wdt_private . gcs ) ;
release_mem_region ( iTCO_wdt_private . gcs_res - > start ,
resource_size ( iTCO_wdt_private . gcs_res ) ) ;
}
iTCO_wdt_private . tco_res = NULL ;
iTCO_wdt_private . smi_res = NULL ;
iTCO_wdt_private . gcs_res = NULL ;
iTCO_wdt_private . gcs = NULL ;
}
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 ;
2013-07-30 14:58:51 +04:00
struct lpc_ich_info * ich_info = dev_get_platdata ( & dev - > dev ) ;
2012-04-20 23:14:11 +04:00
if ( ! ich_info )
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 ;
iTCO_wdt_private . iTCO_version = ich_info - > iTCO_version ;
iTCO_wdt_private . dev = dev ;
iTCO_wdt_private . pdev = to_pci_dev ( dev - > dev . parent ) ;
2006-05-21 16:37:44 +04:00
/*
2012-04-20 23:14:11 +04:00
* Get the Memory - Mapped GCS register , we need it for the
* NO_REBOOT flag ( TCO v2 ) .
2006-05-21 16:37:44 +04:00
*/
if ( iTCO_wdt_private . iTCO_version = = 2 ) {
2012-04-20 23:14:11 +04:00
iTCO_wdt_private . gcs_res = platform_get_resource ( dev ,
IORESOURCE_MEM ,
ICH_RES_MEM_GCS ) ;
if ( ! iTCO_wdt_private . gcs_res )
goto out ;
if ( ! request_mem_region ( iTCO_wdt_private . gcs_res - > start ,
resource_size ( iTCO_wdt_private . gcs_res ) , dev - > name ) ) {
ret = - EBUSY ;
2009-06-05 15:13:08 +04:00
goto out ;
}
2012-04-20 23:14:11 +04:00
iTCO_wdt_private . gcs = ioremap ( iTCO_wdt_private . gcs_res - > start ,
resource_size ( iTCO_wdt_private . gcs_res ) ) ;
if ( ! iTCO_wdt_private . gcs ) {
ret = - EIO ;
goto unreg_gcs ;
}
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 */
2012-04-20 23:14:11 +04:00
goto unmap_gcs ;
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 ;
goto unmap_gcs ;
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 " ,
2012-05-15 00:15:20 +04:00
ich_info - > name , ich_info - > iTCO_version , ( u64 ) TCOBASE ) ;
2006-05-21 16:37:44 +04:00
/* Clear out the (probably old) status */
2010-04-19 16:38:25 +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 */
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 ) ;
iTCO_wdt_watchdog_dev . parent = dev - > dev . parent ;
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 ) ) ;
unmap_gcs :
2006-05-21 16:37:44 +04:00
if ( iTCO_wdt_private . iTCO_version = = 2 )
iounmap ( iTCO_wdt_private . gcs ) ;
2012-04-20 23:14:11 +04:00
unreg_gcs :
2006-05-21 16:37:44 +04:00
if ( iTCO_wdt_private . iTCO_version = = 2 )
2012-04-20 23:14:11 +04:00
release_mem_region ( iTCO_wdt_private . gcs_res - > start ,
resource_size ( iTCO_wdt_private . gcs_res ) ) ;
out :
iTCO_wdt_private . tco_res = NULL ;
iTCO_wdt_private . smi_res = NULL ;
iTCO_wdt_private . gcs_res = NULL ;
iTCO_wdt_private . gcs = 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
}
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 = {
. owner = THIS_MODULE ,
. name = DRV_NAME ,
} ,
} ;
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 ) ;