2012-02-28 00:03:53 +04:00
/*
* The file intends to implement the platform dependent EEH operations on pseries .
* Actually , the pseries platform is built based on RTAS heavily . That means the
* pseries platform dependent EEH operations will be built on RTAS calls . The functions
2016-06-01 09:34:37 +03:00
* are derived from arch / powerpc / platforms / pseries / eeh . c and necessary cleanup has
2012-02-28 00:03:53 +04:00
* been done .
*
* Copyright Benjamin Herrenschmidt & Gavin Shan , IBM Corporation 2011.
* Copyright IBM Corporation 2001 , 2005 , 2006
* Copyright Dave Engebretsen & Todd Inglett 2001
* Copyright Linas Vepstas 2005 , 2006
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/atomic.h>
# include <linux/delay.h>
# include <linux/export.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/of.h>
# include <linux/pci.h>
# include <linux/proc_fs.h>
# include <linux/rbtree.h>
# include <linux/sched.h>
# include <linux/seq_file.h>
# include <linux/spinlock.h>
# include <asm/eeh.h>
# include <asm/eeh_event.h>
# include <asm/io.h>
# include <asm/machdep.h>
# include <asm/ppc-pci.h>
# include <asm/rtas.h>
2012-02-28 00:03:54 +04:00
/* RTAS tokens */
static int ibm_set_eeh_option ;
static int ibm_set_slot_reset ;
static int ibm_read_slot_reset_state ;
static int ibm_read_slot_reset_state2 ;
static int ibm_slot_error_detail ;
static int ibm_get_config_addr_info ;
static int ibm_get_config_addr_info2 ;
static int ibm_configure_pe ;
2012-02-28 00:04:00 +04:00
/*
* Buffer for reporting slot - error - detail rtas calls . Its here
* in BSS , and not dynamically alloced , so that it ends up in
* RMO where RTAS can access it .
*/
static unsigned char slot_errbuf [ RTAS_ERROR_LOG_MAX ] ;
static DEFINE_SPINLOCK ( slot_errbuf_lock ) ;
static int eeh_error_buf_size ;
2012-02-28 00:03:53 +04:00
/**
* pseries_eeh_init - EEH platform dependent initialization
*
* EEH platform dependent initialization on pseries .
*/
static int pseries_eeh_init ( void )
{
2012-02-28 00:03:54 +04:00
/* figure out EEH RTAS function call tokens */
ibm_set_eeh_option = rtas_token ( " ibm,set-eeh-option " ) ;
ibm_set_slot_reset = rtas_token ( " ibm,set-slot-reset " ) ;
ibm_read_slot_reset_state2 = rtas_token ( " ibm,read-slot-reset-state2 " ) ;
ibm_read_slot_reset_state = rtas_token ( " ibm,read-slot-reset-state " ) ;
ibm_slot_error_detail = rtas_token ( " ibm,slot-error-detail " ) ;
ibm_get_config_addr_info2 = rtas_token ( " ibm,get-config-addr-info2 " ) ;
ibm_get_config_addr_info = rtas_token ( " ibm,get-config-addr-info " ) ;
ibm_configure_pe = rtas_token ( " ibm,configure-pe " ) ;
2016-04-07 09:28:27 +03:00
/*
* ibm , configure - pe and ibm , configure - bridge have the same semantics ,
* however ibm , configure - pe can be faster . If we can ' t find
* ibm , configure - pe then fall back to using ibm , configure - bridge .
*/
if ( ibm_configure_pe = = RTAS_UNKNOWN_SERVICE )
ibm_configure_pe = rtas_token ( " ibm,configure-bridge " ) ;
2012-02-28 00:03:54 +04:00
2013-06-05 18:25:50 +04:00
/*
* Necessary sanity check . We needn ' t check " get-config-addr-info "
* and its variant since the old firmware probably support address
* of domain / bus / slot / function for EEH RTAS operations .
*/
2014-09-30 06:39:04 +04:00
if ( ibm_set_eeh_option = = RTAS_UNKNOWN_SERVICE | |
ibm_set_slot_reset = = RTAS_UNKNOWN_SERVICE | |
( ibm_read_slot_reset_state2 = = RTAS_UNKNOWN_SERVICE & &
ibm_read_slot_reset_state = = RTAS_UNKNOWN_SERVICE ) | |
ibm_slot_error_detail = = RTAS_UNKNOWN_SERVICE | |
2016-04-07 09:28:27 +03:00
ibm_configure_pe = = RTAS_UNKNOWN_SERVICE ) {
2014-09-30 06:39:04 +04:00
pr_info ( " EEH functionality not supported \n " ) ;
2012-02-28 00:03:54 +04:00
return - EINVAL ;
}
2012-02-28 00:04:00 +04:00
/* Initialize error log lock and size */
spin_lock_init ( & slot_errbuf_lock ) ;
eeh_error_buf_size = rtas_token ( " rtas-error-log-max " ) ;
if ( eeh_error_buf_size = = RTAS_UNKNOWN_SERVICE ) {
2014-09-30 06:39:04 +04:00
pr_info ( " %s: unknown EEH error log size \n " ,
2012-02-28 00:04:00 +04:00
__func__ ) ;
eeh_error_buf_size = 1024 ;
} else if ( eeh_error_buf_size > RTAS_ERROR_LOG_MAX ) {
2014-09-30 06:39:04 +04:00
pr_info ( " %s: EEH error log size %d exceeds the maximal %d \n " ,
2012-02-28 00:04:00 +04:00
__func__ , eeh_error_buf_size , RTAS_ERROR_LOG_MAX ) ;
eeh_error_buf_size = RTAS_ERROR_LOG_MAX ;
}
2012-09-08 02:44:21 +04:00
/* Set EEH probe mode */
2014-07-17 08:41:39 +04:00
eeh_add_flag ( EEH_PROBE_MODE_DEVTREE | EEH_ENABLE_IO_FOR_LOG ) ;
2012-09-08 02:44:21 +04:00
2012-02-28 00:03:53 +04:00
return 0 ;
}
2015-03-17 08:15:06 +03:00
static int pseries_eeh_cap_start ( struct pci_dn * pdn )
2013-07-24 06:24:59 +04:00
{
u32 status ;
if ( ! pdn )
return 0 ;
rtas_read_config ( pdn , PCI_STATUS , 2 , & status ) ;
if ( ! ( status & PCI_STATUS_CAP_LIST ) )
return 0 ;
return PCI_CAPABILITY_LIST ;
}
2015-03-17 08:15:06 +03:00
static int pseries_eeh_find_cap ( struct pci_dn * pdn , int cap )
2013-07-24 06:24:59 +04:00
{
2015-03-17 08:15:06 +03:00
int pos = pseries_eeh_cap_start ( pdn ) ;
2013-07-24 06:24:59 +04:00
int cnt = 48 ; /* Maximal number of capabilities */
u32 id ;
if ( ! pos )
return 0 ;
while ( cnt - - ) {
rtas_read_config ( pdn , pos , 1 , & pos ) ;
if ( pos < 0x40 )
break ;
pos & = ~ 3 ;
rtas_read_config ( pdn , pos + PCI_CAP_LIST_ID , 1 , & id ) ;
if ( id = = 0xff )
break ;
if ( id = = cap )
return pos ;
pos + = PCI_CAP_LIST_NEXT ;
}
return 0 ;
}
2015-03-17 08:15:06 +03:00
static int pseries_eeh_find_ecap ( struct pci_dn * pdn , int cap )
2014-04-24 12:00:16 +04:00
{
2015-03-17 08:15:06 +03:00
struct eeh_dev * edev = pdn_to_eeh_dev ( pdn ) ;
2014-04-24 12:00:16 +04:00
u32 header ;
int pos = 256 ;
int ttl = ( 4096 - 256 ) / 8 ;
if ( ! edev | | ! edev - > pcie_cap )
return 0 ;
if ( rtas_read_config ( pdn , pos , 4 , & header ) ! = PCIBIOS_SUCCESSFUL )
return 0 ;
else if ( ! header )
return 0 ;
while ( ttl - - > 0 ) {
if ( PCI_EXT_CAP_ID ( header ) = = cap & & pos )
return pos ;
pos = PCI_EXT_CAP_NEXT ( header ) ;
if ( pos < 256 )
break ;
if ( rtas_read_config ( pdn , pos , 4 , & header ) ! = PCIBIOS_SUCCESSFUL )
break ;
}
return 0 ;
}
2012-09-08 02:44:21 +04:00
/**
2015-03-17 08:15:06 +03:00
* pseries_eeh_probe - EEH probe on the given device
* @ pdn : PCI device node
* @ data : Unused
2012-09-08 02:44:21 +04:00
*
* When EEH module is installed during system boot , all PCI devices
* are checked one by one to see if it supports EEH . The function
* is introduced for the purpose .
*/
2015-03-17 08:15:06 +03:00
static void * pseries_eeh_probe ( struct pci_dn * pdn , void * data )
2012-09-08 02:44:21 +04:00
{
struct eeh_dev * edev ;
struct eeh_pe pe ;
2013-07-24 06:24:59 +04:00
u32 pcie_flags ;
2012-09-08 02:44:21 +04:00
int enable = 0 ;
int ret ;
/* Retrieve OF node and eeh device */
2015-03-17 08:15:06 +03:00
edev = pdn_to_eeh_dev ( pdn ) ;
if ( ! edev | | edev - > pe )
2012-09-08 02:44:21 +04:00
return NULL ;
2015-03-17 08:15:06 +03:00
/* Check class/vendor/device IDs */
if ( ! pdn - > vendor_id | | ! pdn - > device_id | | ! pdn - > class_code )
2012-09-08 02:44:21 +04:00
return NULL ;
2015-03-17 08:15:06 +03:00
/* Skip for PCI-ISA bridge */
if ( ( pdn - > class_code > > 8 ) = = PCI_CLASS_BRIDGE_ISA )
return NULL ;
2013-11-20 15:14:58 +04:00
2013-07-24 06:24:59 +04:00
/*
* Update class code and mode of eeh device . We need
* correctly reflects that current device is root port
* or PCIe switch downstream port .
*/
2015-03-17 08:15:06 +03:00
edev - > class_code = pdn - > class_code ;
edev - > pcix_cap = pseries_eeh_find_cap ( pdn , PCI_CAP_ID_PCIX ) ;
edev - > pcie_cap = pseries_eeh_find_cap ( pdn , PCI_CAP_ID_EXP ) ;
edev - > aer_cap = pseries_eeh_find_ecap ( pdn , PCI_EXT_CAP_ID_ERR ) ;
2013-07-24 06:25:01 +04:00
edev - > mode & = 0xFFFFFF00 ;
2013-07-24 06:24:59 +04:00
if ( ( edev - > class_code > > 8 ) = = PCI_CLASS_BRIDGE_PCI ) {
edev - > mode | = EEH_DEV_BRIDGE ;
if ( edev - > pcie_cap ) {
rtas_read_config ( pdn , edev - > pcie_cap + PCI_EXP_FLAGS ,
2 , & pcie_flags ) ;
pcie_flags = ( pcie_flags & PCI_EXP_FLAGS_TYPE ) > > 4 ;
if ( pcie_flags = = PCI_EXP_TYPE_ROOT_PORT )
edev - > mode | = EEH_DEV_ROOT_PORT ;
else if ( pcie_flags = = PCI_EXP_TYPE_DOWNSTREAM )
edev - > mode | = EEH_DEV_DS_PORT ;
}
}
2012-09-08 02:44:21 +04:00
/* Initialize the fake PE */
memset ( & pe , 0 , sizeof ( struct eeh_pe ) ) ;
2017-08-29 10:34:01 +03:00
pe . phb = pdn - > phb ;
2015-03-17 08:15:06 +03:00
pe . config_addr = ( pdn - > busno < < 16 ) | ( pdn - > devfn < < 8 ) ;
2012-09-08 02:44:21 +04:00
/* Enable EEH on the device */
ret = eeh_ops - > set_option ( & pe , EEH_OPT_ENABLE ) ;
if ( ! ret ) {
/* Retrieve PE address */
edev - > pe_config_addr = eeh_ops - > get_pe_addr ( & pe ) ;
pe . addr = edev - > pe_config_addr ;
/* Some older systems (Power4) allow the ibm,set-eeh-option
* call to succeed even on nodes where EEH is not supported .
* Verify support explicitly .
*/
ret = eeh_ops - > get_state ( & pe , NULL ) ;
if ( ret > 0 & & ret ! = EEH_STATE_NOT_SUPPORT )
enable = 1 ;
if ( enable ) {
2014-07-17 08:41:38 +04:00
eeh_add_flag ( EEH_ENABLED ) ;
2012-09-08 02:44:21 +04:00
eeh_add_to_parent_pe ( edev ) ;
2016-11-16 06:02:15 +03:00
pr_debug ( " %s: EEH enabled on %02x:%02x.%01x PHB#%x-PE#%x \n " ,
2015-03-17 08:15:06 +03:00
__func__ , pdn - > busno , PCI_SLOT ( pdn - > devfn ) ,
PCI_FUNC ( pdn - > devfn ) , pe . phb - > global_number ,
pe . addr ) ;
} else if ( pdn - > parent & & pdn_to_eeh_dev ( pdn - > parent ) & &
( pdn_to_eeh_dev ( pdn - > parent ) ) - > pe ) {
2012-09-08 02:44:21 +04:00
/* This device doesn't support EEH, but it may have an
* EEH parent , in which case we mark it as supported .
*/
2015-03-17 08:15:06 +03:00
edev - > pe_config_addr = pdn_to_eeh_dev ( pdn - > parent ) - > pe_config_addr ;
2012-09-08 02:44:21 +04:00
eeh_add_to_parent_pe ( edev ) ;
}
}
/* Save memory bars */
eeh_save_bars ( edev ) ;
return NULL ;
}
2012-02-28 00:03:53 +04:00
/**
* pseries_eeh_set_option - Initialize EEH or MMIO / DMA reenable
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2012-02-28 00:03:53 +04:00
* @ option : operation to be issued
*
* The function is used to control the EEH functionality globally .
* Currently , following options are support according to PAPR :
* Enable EEH , Disable EEH , Enable MMIO and Enable DMA
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_set_option ( struct eeh_pe * pe , int option )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:03:55 +04:00
int ret = 0 ;
int config_addr ;
/*
* When we ' re enabling or disabling EEH functioality on
* the particular PE , the PE config address is possibly
* unavailable . Therefore , we have to figure it out from
* the FDT node .
*/
switch ( option ) {
case EEH_OPT_DISABLE :
case EEH_OPT_ENABLE :
case EEH_OPT_THAW_MMIO :
case EEH_OPT_THAW_DMA :
2012-09-08 02:44:14 +04:00
config_addr = pe - > config_addr ;
if ( pe - > addr )
config_addr = pe - > addr ;
2012-02-28 00:03:55 +04:00
break ;
2014-09-30 06:38:52 +04:00
case EEH_OPT_FREEZE_PE :
/* Not support */
return 0 ;
2012-02-28 00:03:55 +04:00
default :
pr_err ( " %s: Invalid option %d \n " ,
__func__ , option ) ;
return - EINVAL ;
}
ret = rtas_call ( ibm_set_eeh_option , 4 , 1 , NULL ,
2012-09-08 02:44:14 +04:00
config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) , option ) ;
2012-02-28 00:03:55 +04:00
return ret ;
2012-02-28 00:03:53 +04:00
}
/**
* pseries_eeh_get_pe_addr - Retrieve PE address
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2012-02-28 00:03:53 +04:00
*
* Retrieve the assocated PE address . Actually , there ' re 2 RTAS
* function calls dedicated for the purpose . We need implement
* it through the new function and then the old one . Besides ,
* you should make sure the config address is figured out from
* FDT node before calling the function .
*
* It ' s notable that zero ' ed return value means invalid PE config
* address .
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_get_pe_addr ( struct eeh_pe * pe )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:03:56 +04:00
int ret = 0 ;
int rets [ 3 ] ;
if ( ibm_get_config_addr_info2 ! = RTAS_UNKNOWN_SERVICE ) {
/*
* First of all , we need to make sure there has one PE
* associated with the device . Otherwise , PE address is
* meaningless .
*/
ret = rtas_call ( ibm_get_config_addr_info2 , 4 , 2 , rets ,
2012-09-08 02:44:14 +04:00
pe - > config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) , 1 ) ;
2012-02-28 00:03:56 +04:00
if ( ret | | ( rets [ 0 ] = = 0 ) )
return 0 ;
/* Retrieve the associated PE config address */
ret = rtas_call ( ibm_get_config_addr_info2 , 4 , 2 , rets ,
2012-09-08 02:44:14 +04:00
pe - > config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) , 0 ) ;
2012-02-28 00:03:56 +04:00
if ( ret ) {
2016-11-16 06:02:15 +03:00
pr_warn ( " %s: Failed to get address for PHB#%x-PE#%x \n " ,
2012-09-08 02:44:14 +04:00
__func__ , pe - > phb - > global_number , pe - > config_addr ) ;
2012-02-28 00:03:56 +04:00
return 0 ;
}
return rets [ 0 ] ;
}
if ( ibm_get_config_addr_info ! = RTAS_UNKNOWN_SERVICE ) {
ret = rtas_call ( ibm_get_config_addr_info , 4 , 2 , rets ,
2012-09-08 02:44:14 +04:00
pe - > config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) , 0 ) ;
2012-02-28 00:03:56 +04:00
if ( ret ) {
2016-11-16 06:02:15 +03:00
pr_warn ( " %s: Failed to get address for PHB#%x-PE#%x \n " ,
2012-09-08 02:44:14 +04:00
__func__ , pe - > phb - > global_number , pe - > config_addr ) ;
2012-02-28 00:03:56 +04:00
return 0 ;
}
return rets [ 0 ] ;
}
return ret ;
2012-02-28 00:03:53 +04:00
}
/**
* pseries_eeh_get_state - Retrieve PE state
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2012-02-28 00:03:53 +04:00
* @ state : return value
*
* Retrieve the state of the specified PE . On RTAS compliant
* pseries platform , there already has one dedicated RTAS function
* for the purpose . It ' s notable that the associated PE config address
* might be ready when calling the function . Therefore , endeavour to
* use the PE config address if possible . Further more , there ' re 2
* RTAS calls for the purpose , we need to try the new one and back
* to the old one if the new one couldn ' t work properly .
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_get_state ( struct eeh_pe * pe , int * state )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:03:57 +04:00
int config_addr ;
int ret ;
int rets [ 4 ] ;
int result ;
/* Figure out PE config address if possible */
2012-09-08 02:44:14 +04:00
config_addr = pe - > config_addr ;
if ( pe - > addr )
config_addr = pe - > addr ;
2012-02-28 00:03:57 +04:00
if ( ibm_read_slot_reset_state2 ! = RTAS_UNKNOWN_SERVICE ) {
ret = rtas_call ( ibm_read_slot_reset_state2 , 3 , 4 , rets ,
2012-09-08 02:44:14 +04:00
config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) ) ;
2012-02-28 00:03:57 +04:00
} else if ( ibm_read_slot_reset_state ! = RTAS_UNKNOWN_SERVICE ) {
/* Fake PE unavailable info */
rets [ 2 ] = 0 ;
ret = rtas_call ( ibm_read_slot_reset_state , 3 , 3 , rets ,
2012-09-08 02:44:14 +04:00
config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) ) ;
2012-02-28 00:03:57 +04:00
} else {
return EEH_STATE_NOT_SUPPORT ;
}
if ( ret )
return ret ;
/* Parse the result out */
2015-10-08 06:58:56 +03:00
if ( ! rets [ 1 ] )
return EEH_STATE_NOT_SUPPORT ;
switch ( rets [ 0 ] ) {
case 0 :
result = EEH_STATE_MMIO_ACTIVE |
EEH_STATE_DMA_ACTIVE ;
break ;
case 1 :
result = EEH_STATE_RESET_ACTIVE |
EEH_STATE_MMIO_ACTIVE |
EEH_STATE_DMA_ACTIVE ;
break ;
case 2 :
result = 0 ;
break ;
case 4 :
result = EEH_STATE_MMIO_ENABLED ;
break ;
case 5 :
if ( rets [ 2 ] ) {
if ( state ) * state = rets [ 2 ] ;
result = EEH_STATE_UNAVAILABLE ;
} else {
2012-02-28 00:03:57 +04:00
result = EEH_STATE_NOT_SUPPORT ;
}
2015-10-08 06:58:56 +03:00
break ;
default :
2012-02-28 00:03:57 +04:00
result = EEH_STATE_NOT_SUPPORT ;
}
return result ;
2012-02-28 00:03:53 +04:00
}
/**
* pseries_eeh_reset - Reset the specified PE
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2012-02-28 00:03:53 +04:00
* @ option : reset option
*
* Reset the specified PE
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_reset ( struct eeh_pe * pe , int option )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:03:59 +04:00
int config_addr ;
int ret ;
/* Figure out PE address */
2012-09-08 02:44:14 +04:00
config_addr = pe - > config_addr ;
if ( pe - > addr )
config_addr = pe - > addr ;
2012-02-28 00:03:59 +04:00
/* Reset PE through RTAS call */
ret = rtas_call ( ibm_set_slot_reset , 4 , 1 , NULL ,
2012-09-08 02:44:14 +04:00
config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) , option ) ;
2012-02-28 00:03:59 +04:00
/* If fundamental-reset not supported, try hot-reset */
if ( option = = EEH_RESET_FUNDAMENTAL & &
ret = = - 8 ) {
2014-04-24 12:00:23 +04:00
option = EEH_RESET_HOT ;
2012-02-28 00:03:59 +04:00
ret = rtas_call ( ibm_set_slot_reset , 4 , 1 , NULL ,
2012-09-08 02:44:14 +04:00
config_addr , BUID_HI ( pe - > phb - > buid ) ,
2014-04-24 12:00:23 +04:00
BUID_LO ( pe - > phb - > buid ) , option ) ;
2012-02-28 00:03:59 +04:00
}
2014-04-24 12:00:23 +04:00
/* We need reset hold or settlement delay */
if ( option = = EEH_RESET_FUNDAMENTAL | |
option = = EEH_RESET_HOT )
msleep ( EEH_PE_RST_HOLD_TIME ) ;
else
msleep ( EEH_PE_RST_SETTLE_TIME ) ;
2012-02-28 00:03:59 +04:00
return ret ;
2012-02-28 00:03:53 +04:00
}
/**
* pseries_eeh_wait_state - Wait for PE state
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2015-04-27 04:25:10 +03:00
* @ max_wait : maximal period in millisecond
2012-02-28 00:03:53 +04:00
*
* Wait for the state of associated PE . It might take some time
* to retrieve the PE ' s state .
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_wait_state ( struct eeh_pe * pe , int max_wait )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:03:58 +04:00
int ret ;
int mwait ;
/*
* According to PAPR , the state of PE might be temporarily
* unavailable . Under the circumstance , we have to wait
* for indicated time determined by firmware . The maximal
* wait time is 5 minutes , which is acquired from the original
* EEH implementation . Also , the original implementation
* also defined the minimal wait time as 1 second .
*/
# define EEH_STATE_MIN_WAIT_TIME (1000)
# define EEH_STATE_MAX_WAIT_TIME (300 * 1000)
while ( 1 ) {
2012-09-08 02:44:14 +04:00
ret = pseries_eeh_get_state ( pe , & mwait ) ;
2012-02-28 00:03:58 +04:00
/*
* If the PE ' s state is temporarily unavailable ,
* we have to wait for the specified time . Otherwise ,
* the PE ' s state will be returned immediately .
*/
if ( ret ! = EEH_STATE_UNAVAILABLE )
return ret ;
if ( max_wait < = 0 ) {
2014-07-17 08:41:41 +04:00
pr_warn ( " %s: Timeout when getting PE's state (%d) \n " ,
2012-02-28 00:03:58 +04:00
__func__ , max_wait ) ;
return EEH_STATE_NOT_SUPPORT ;
}
if ( mwait < = 0 ) {
2014-07-17 08:41:41 +04:00
pr_warn ( " %s: Firmware returned bad wait value %d \n " ,
2012-02-28 00:03:58 +04:00
__func__ , mwait ) ;
mwait = EEH_STATE_MIN_WAIT_TIME ;
} else if ( mwait > EEH_STATE_MAX_WAIT_TIME ) {
2014-07-17 08:41:41 +04:00
pr_warn ( " %s: Firmware returned too long wait value %d \n " ,
2012-02-28 00:03:58 +04:00
__func__ , mwait ) ;
mwait = EEH_STATE_MAX_WAIT_TIME ;
}
max_wait - = mwait ;
msleep ( mwait ) ;
}
return EEH_STATE_NOT_SUPPORT ;
2012-02-28 00:03:53 +04:00
}
/**
* pseries_eeh_get_log - Retrieve error log
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2012-02-28 00:03:53 +04:00
* @ severity : temporary or permanent error log
* @ drv_log : driver log to be combined with retrieved error log
* @ len : length of driver log
*
* Retrieve the temporary or permanent error from the PE .
* Actually , the error will be retrieved through the dedicated
* RTAS call .
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_get_log ( struct eeh_pe * pe , int severity , char * drv_log , unsigned long len )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:04:00 +04:00
int config_addr ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & slot_errbuf_lock , flags ) ;
memset ( slot_errbuf , 0 , eeh_error_buf_size ) ;
/* Figure out the PE address */
2012-09-08 02:44:14 +04:00
config_addr = pe - > config_addr ;
if ( pe - > addr )
config_addr = pe - > addr ;
2012-02-28 00:04:00 +04:00
ret = rtas_call ( ibm_slot_error_detail , 8 , 1 , NULL , config_addr ,
2012-09-08 02:44:14 +04:00
BUID_HI ( pe - > phb - > buid ) , BUID_LO ( pe - > phb - > buid ) ,
2012-02-28 00:04:00 +04:00
virt_to_phys ( drv_log ) , len ,
virt_to_phys ( slot_errbuf ) , eeh_error_buf_size ,
severity ) ;
if ( ! ret )
log_error ( slot_errbuf , ERR_TYPE_RTAS_LOG , 0 ) ;
spin_unlock_irqrestore ( & slot_errbuf_lock , flags ) ;
return ret ;
2012-02-28 00:03:53 +04:00
}
/**
* pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE
2012-09-08 02:44:14 +04:00
* @ pe : EEH PE
2012-02-28 00:03:53 +04:00
*
* The function will be called to reconfigure the bridges included
* in the specified PE so that the mulfunctional PE would be recovered
* again .
*/
2012-09-08 02:44:14 +04:00
static int pseries_eeh_configure_bridge ( struct eeh_pe * pe )
2012-02-28 00:03:53 +04:00
{
2012-02-28 00:04:01 +04:00
int config_addr ;
int ret ;
2016-04-07 09:28:26 +03:00
/* Waiting 0.2s maximum before skipping configuration */
int max_wait = 200 ;
2012-02-28 00:04:01 +04:00
/* Figure out the PE address */
2012-09-08 02:44:14 +04:00
config_addr = pe - > config_addr ;
if ( pe - > addr )
config_addr = pe - > addr ;
2012-02-28 00:04:01 +04:00
2016-04-07 09:28:26 +03:00
while ( max_wait > 0 ) {
2016-04-07 09:28:27 +03:00
ret = rtas_call ( ibm_configure_pe , 3 , 1 , NULL ,
config_addr , BUID_HI ( pe - > phb - > buid ) ,
BUID_LO ( pe - > phb - > buid ) ) ;
2012-02-28 00:04:01 +04:00
2016-04-07 09:28:26 +03:00
if ( ! ret )
return ret ;
/*
* If RTAS returns a delay value that ' s above 100 ms , cut it
* down to 100 ms in case firmware made a mistake . For more
* on how these delay values work see rtas_busy_delay_time
*/
if ( ret > RTAS_EXTENDED_DELAY_MIN + 2 & &
ret < = RTAS_EXTENDED_DELAY_MAX )
ret = RTAS_EXTENDED_DELAY_MIN + 2 ;
max_wait - = rtas_busy_delay_time ( ret ) ;
if ( max_wait < 0 )
break ;
rtas_busy_delay ( ret ) ;
}
2012-02-28 00:04:01 +04:00
2016-11-16 06:02:15 +03:00
pr_warn ( " %s: Unable to configure bridge PHB#%x-PE#%x (%d) \n " ,
2016-04-07 09:28:26 +03:00
__func__ , pe - > phb - > global_number , pe - > addr , ret ) ;
2012-02-28 00:04:01 +04:00
return ret ;
2012-02-28 00:03:53 +04:00
}
2012-02-28 00:04:11 +04:00
/**
* pseries_eeh_read_config - Read PCI config space
2015-03-17 08:15:07 +03:00
* @ pdn : PCI device node
2012-02-28 00:04:11 +04:00
* @ where : PCI address
* @ size : size to read
* @ val : return value
*
* Read config space from the speicifed device
*/
2015-03-17 08:15:07 +03:00
static int pseries_eeh_read_config ( struct pci_dn * pdn , int where , int size , u32 * val )
2012-02-28 00:04:11 +04:00
{
return rtas_read_config ( pdn , where , size , val ) ;
}
/**
* pseries_eeh_write_config - Write PCI config space
2015-03-17 08:15:07 +03:00
* @ pdn : PCI device node
2012-02-28 00:04:11 +04:00
* @ where : PCI address
* @ size : size to write
* @ val : value to be written
*
* Write config space to the specified device
*/
2015-03-17 08:15:07 +03:00
static int pseries_eeh_write_config ( struct pci_dn * pdn , int where , int size , u32 val )
2012-02-28 00:04:11 +04:00
{
return rtas_write_config ( pdn , where , size , val ) ;
}
2012-02-28 00:03:53 +04:00
static struct eeh_ops pseries_eeh_ops = {
. name = " pseries " ,
. init = pseries_eeh_init ,
2015-03-17 08:15:06 +03:00
. probe = pseries_eeh_probe ,
2012-02-28 00:03:53 +04:00
. set_option = pseries_eeh_set_option ,
. get_pe_addr = pseries_eeh_get_pe_addr ,
. get_state = pseries_eeh_get_state ,
. reset = pseries_eeh_reset ,
. wait_state = pseries_eeh_wait_state ,
. get_log = pseries_eeh_get_log ,
2012-02-28 00:04:11 +04:00
. configure_bridge = pseries_eeh_configure_bridge ,
2014-09-30 06:38:56 +04:00
. err_inject = NULL ,
2012-02-28 00:04:11 +04:00
. read_config = pseries_eeh_read_config ,
2014-01-03 13:47:12 +04:00
. write_config = pseries_eeh_write_config ,
. next_error = NULL ,
. restore_config = NULL
2012-02-28 00:03:53 +04:00
} ;
/**
* eeh_pseries_init - Register platform dependent EEH operations
*
* EEH initialization on pseries platform . This function should be
* called before any EEH related functions .
*/
2012-09-08 02:44:02 +04:00
static int __init eeh_pseries_init ( void )
2012-02-28 00:03:53 +04:00
{
2014-07-16 06:02:43 +04:00
int ret ;
2012-09-08 02:44:04 +04:00
ret = eeh_ops_register ( & pseries_eeh_ops ) ;
if ( ! ret )
pr_info ( " EEH: pSeries platform initialized \n " ) ;
else
pr_info ( " EEH: pSeries platform initialization failure (%d) \n " ,
ret ) ;
return ret ;
2012-02-28 00:03:53 +04:00
}
2014-07-16 06:02:43 +04:00
machine_early_initcall ( pseries , eeh_pseries_init ) ;