2005-04-17 02:20:36 +04:00
/*
2007-05-23 21:28:01 +04:00
* Copyright IBM Corporation 2001 , 2005 , 2006
* Copyright Dave Engebretsen & Todd Inglett 2001
* Copyright Linas Vepstas 2005 , 2006
2012-02-28 00:03:51 +04:00
* Copyright 2001 - 2012 IBM Corporation .
2005-11-04 03:47:50 +03:00
*
2005-04-17 02:20:36 +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 .
2005-11-04 03:47:50 +03:00
*
2005-04-17 02:20:36 +04:00
* 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 .
2005-11-04 03:47:50 +03:00
*
2005-04-17 02:20:36 +04:00
* 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
2007-05-23 21:28:01 +04:00
*
* Please address comments and feedback to Linas Vepstas < linas @ austin . ibm . com >
2005-04-17 02:20:36 +04:00
*/
2005-11-04 03:50:10 +03:00
# include <linux/delay.h>
2012-02-28 00:03:51 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/list.h>
# include <linux/pci.h>
# include <linux/proc_fs.h>
# include <linux/rbtree.h>
# include <linux/seq_file.h>
# include <linux/spinlock.h>
2011-05-27 18:46:24 +04:00
# include <linux/export.h>
2007-12-21 07:52:07 +03:00
# include <linux/of.h>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2005-04-17 02:20:36 +04:00
# include <asm/eeh.h>
2005-11-04 03:50:04 +03:00
# include <asm/eeh_event.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/machdep.h>
2005-11-04 03:50:04 +03:00
# include <asm/ppc-pci.h>
2005-04-17 02:20:36 +04:00
# include <asm/rtas.h>
/** Overview:
* EEH , or " Extended Error Handling " is a PCI bridge technology for
* dealing with PCI bus errors that can ' t be dealt with within the
* usual PCI framework , except by check - stopping the CPU . Systems
* that are designed for high - availability / reliability cannot afford
* to crash due to a " mere " PCI error , thus the need for EEH .
* An EEH - capable bridge operates by converting a detected error
* into a " slot freeze " , taking the PCI adapter off - line , making
* the slot behave , from the OS ' es point of view , as if the slot
* were " empty " : all reads return 0xff ' s and all writes are silently
* ignored . EEH slot isolation events can be triggered by parity
* errors on the address or data busses ( e . g . during posted writes ) ,
2005-11-04 03:47:50 +03:00
* which in turn might be caused by low voltage on the bus , dust ,
* vibration , humidity , radioactivity or plain - old failed hardware .
2005-04-17 02:20:36 +04:00
*
* Note , however , that one of the leading causes of EEH slot
* freeze events are buggy device drivers , buggy device microcode ,
* or buggy device hardware . This is because any attempt by the
* device to bus - master data to a memory address that is not
* assigned to the device will trigger a slot freeze . ( The idea
* is to prevent devices - gone - wild from corrupting system memory ) .
* Buggy hardware / drivers will have a miserable time co - existing
* with EEH .
*
* Ideally , a PCI device driver , when suspecting that an isolation
2011-03-31 05:57:33 +04:00
* event has occurred ( e . g . by reading 0xff ' s ) , will then ask EEH
2005-04-17 02:20:36 +04:00
* whether this is the case , and then take appropriate steps to
* reset the PCI slot , the PCI device , and then resume operations .
* However , until that day , the checking is done here , with the
* eeh_check_failure ( ) routine embedded in the MMIO macros . If
* the slot is found to be isolated , an " EEH Event " is synthesized
* and sent out for processing .
*/
2005-11-04 03:49:31 +03:00
/* If a device driver keeps reading an MMIO register in an interrupt
2008-07-21 20:40:17 +04:00
* handler after a slot isolation event , it might be broken .
* This sets the threshold for how many read attempts we allow
* before printing an error message .
2005-04-17 02:20:36 +04:00
*/
2007-03-19 22:53:22 +03:00
# define EEH_MAX_FAILS 2100000
2005-04-17 02:20:36 +04:00
2007-05-09 20:38:11 +04:00
/* Time to wait for a PCI slot to report status, in milliseconds */
2007-03-19 22:58:07 +03:00
# define PCI_BUS_RESET_WAIT_MSEC (60*1000)
2012-02-28 00:03:53 +04:00
/* Platform dependent EEH operations */
struct eeh_ops * eeh_ops = NULL ;
2005-11-17 03:44:03 +03:00
int eeh_subsystem_enabled ;
EXPORT_SYMBOL ( eeh_subsystem_enabled ) ;
2005-04-17 02:20:36 +04:00
2012-09-08 02:44:21 +04:00
/*
* EEH probe mode support . The intention is to support multiple
* platforms for EEH . Some platforms like pSeries do PCI emunation
* based on device tree . However , other platforms like powernv probe
* PCI devices from hardware . The flag is used to distinguish that .
* In addition , struct eeh_ops : : probe would be invoked for particular
* OF node or PCI device so that the corresponding PE would be created
* there .
*/
int eeh_probe_mode ;
2005-11-04 03:49:23 +03:00
/* Lock to avoid races due to multiple reports of an error */
2013-06-20 09:21:03 +04:00
DEFINE_RAW_SPINLOCK ( confirm_error_lock ) ;
2005-11-04 03:49:23 +03:00
2007-05-09 20:38:11 +04:00
/* Buffer for reporting pci register dumps. Its here in BSS, and
* not dynamically alloced , so that it ends up in RMO where RTAS
* can access it .
*/
2007-05-09 03:35:32 +04:00
# define EEH_PCI_REGS_LOG_LEN 4096
static unsigned char pci_regs_buf [ EEH_PCI_REGS_LOG_LEN ] ;
2012-02-29 19:47:45 +04:00
/*
* The struct is used to maintain the EEH global statistic
* information . Besides , the EEH global statistics will be
* exported to user space through procfs
*/
struct eeh_stats {
u64 no_device ; /* PCI device not found */
u64 no_dn ; /* OF node not found */
u64 no_cfg_addr ; /* Config address not found */
u64 ignored_check ; /* EEH check skipped */
u64 total_mmio_ffs ; /* Total EEH checks */
u64 false_positives ; /* Unnecessary EEH checks */
u64 slot_resets ; /* PE reset */
} ;
static struct eeh_stats eeh_stats ;
2005-04-17 02:20:36 +04:00
2005-11-04 03:55:19 +03:00
# define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
2007-05-09 03:35:32 +04:00
/**
2012-02-28 00:03:52 +04:00
* eeh_gather_pci_data - Copy assorted PCI config space registers to buff
2012-02-28 00:04:07 +04:00
* @ edev : device to report data for
2007-05-09 03:35:32 +04:00
* @ buf : point to buffer in which to log
* @ len : amount of room in buffer
*
* This routine captures assorted PCI configuration space data ,
* and puts them into a buffer for RTAS error logging .
*/
2012-02-28 00:04:07 +04:00
static size_t eeh_gather_pci_data ( struct eeh_dev * edev , char * buf , size_t len )
2007-05-09 03:35:32 +04:00
{
2012-02-28 00:04:07 +04:00
struct device_node * dn = eeh_dev_to_of_node ( edev ) ;
struct pci_dev * dev = eeh_dev_to_pci_dev ( edev ) ;
2007-05-09 03:35:32 +04:00
u32 cfg ;
2007-05-09 03:36:21 +04:00
int cap , i ;
2007-05-09 03:35:32 +04:00
int n = 0 ;
2012-02-28 00:04:07 +04:00
n + = scnprintf ( buf + n , len - n , " %s \n " , dn - > full_name ) ;
printk ( KERN_WARNING " EEH: of node=%s \n " , dn - > full_name ) ;
2007-05-09 03:36:21 +04:00
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , PCI_VENDOR_ID , 4 , & cfg ) ;
2007-05-09 03:36:21 +04:00
n + = scnprintf ( buf + n , len - n , " dev/vend:%08x \n " , cfg ) ;
printk ( KERN_WARNING " EEH: PCI device/vendor: %08x \n " , cfg ) ;
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , PCI_COMMAND , 4 , & cfg ) ;
2007-05-09 03:35:32 +04:00
n + = scnprintf ( buf + n , len - n , " cmd/stat:%x \n " , cfg ) ;
2007-05-09 03:36:21 +04:00
printk ( KERN_WARNING " EEH: PCI cmd/status register: %08x \n " , cfg ) ;
2007-11-02 23:29:01 +03:00
if ( ! dev ) {
printk ( KERN_WARNING " EEH: no PCI device for this of node \n " ) ;
return n ;
}
2007-07-27 02:35:40 +04:00
/* Gather bridge-specific registers */
if ( dev - > class > > 16 = = PCI_BASE_CLASS_BRIDGE ) {
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , PCI_SEC_STATUS , 2 , & cfg ) ;
2007-07-27 02:35:40 +04:00
n + = scnprintf ( buf + n , len - n , " sec stat:%x \n " , cfg ) ;
printk ( KERN_WARNING " EEH: Bridge secondary status: %04x \n " , cfg ) ;
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , PCI_BRIDGE_CONTROL , 2 , & cfg ) ;
2007-07-27 02:35:40 +04:00
n + = scnprintf ( buf + n , len - n , " brdg ctl:%x \n " , cfg ) ;
printk ( KERN_WARNING " EEH: Bridge control: %04x \n " , cfg ) ;
}
2007-05-09 03:36:21 +04:00
/* Dump out the PCI-X command and status regs */
2007-11-02 23:29:01 +03:00
cap = pci_find_capability ( dev , PCI_CAP_ID_PCIX ) ;
2007-05-09 03:36:21 +04:00
if ( cap ) {
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , cap , 4 , & cfg ) ;
2007-05-09 03:36:21 +04:00
n + = scnprintf ( buf + n , len - n , " pcix-cmd:%x \n " , cfg ) ;
printk ( KERN_WARNING " EEH: PCI-X cmd: %08x \n " , cfg ) ;
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , cap + 4 , 4 , & cfg ) ;
2007-05-09 03:36:21 +04:00
n + = scnprintf ( buf + n , len - n , " pcix-stat:%x \n " , cfg ) ;
printk ( KERN_WARNING " EEH: PCI-X status: %08x \n " , cfg ) ;
}
/* If PCI-E capable, dump PCI-E cap 10, and the AER */
2007-11-02 23:29:01 +03:00
cap = pci_find_capability ( dev , PCI_CAP_ID_EXP ) ;
2007-05-09 03:36:21 +04:00
if ( cap ) {
n + = scnprintf ( buf + n , len - n , " pci-e cap10: \n " ) ;
printk ( KERN_WARNING
" EEH: PCI-E capabilities and status follow: \n " ) ;
for ( i = 0 ; i < = 8 ; i + + ) {
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , cap + 4 * i , 4 , & cfg ) ;
2007-05-09 03:36:21 +04:00
n + = scnprintf ( buf + n , len - n , " %02x:%x \n " , 4 * i , cfg ) ;
printk ( KERN_WARNING " EEH: PCI-E %02x: %08x \n " , i , cfg ) ;
}
2007-11-02 23:29:01 +03:00
cap = pci_find_ext_capability ( dev , PCI_EXT_CAP_ID_ERR ) ;
2007-05-09 03:36:21 +04:00
if ( cap ) {
n + = scnprintf ( buf + n , len - n , " pci-e AER: \n " ) ;
printk ( KERN_WARNING
" EEH: PCI-E AER capability register set follows: \n " ) ;
for ( i = 0 ; i < 14 ; i + + ) {
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , cap + 4 * i , 4 , & cfg ) ;
2007-05-09 03:36:21 +04:00
n + = scnprintf ( buf + n , len - n , " %02x:%x \n " , 4 * i , cfg ) ;
printk ( KERN_WARNING " EEH: PCI-E AER %02x: %08x \n " , i , cfg ) ;
}
}
}
2007-07-27 02:35:40 +04:00
2007-05-09 03:35:32 +04:00
return n ;
}
2012-02-28 00:03:51 +04:00
/**
* eeh_slot_error_detail - Generate combined log including driver log and error log
2012-09-08 02:44:16 +04:00
* @ pe : EEH PE
2012-02-28 00:03:51 +04:00
* @ severity : temporary or permanent error log
*
* This routine should be called to generate the combined log , which
* is comprised of driver log and error log . The driver log is figured
* out from the config space of the corresponding PCI device , while
* the error log is fetched through platform dependent function call .
*/
2012-09-08 02:44:16 +04:00
void eeh_slot_error_detail ( struct eeh_pe * pe , int severity )
2007-05-09 03:35:32 +04:00
{
size_t loglen = 0 ;
2012-09-08 02:44:16 +04:00
struct eeh_dev * edev ;
2013-06-27 09:46:42 +04:00
bool valid_cfg_log = true ;
2007-05-09 03:35:32 +04:00
2013-06-27 09:46:42 +04:00
/*
* When the PHB is fenced or dead , it ' s pointless to collect
* the data from PCI config space because it should return
* 0xFF ' s . For ER , we still retrieve the data from the PCI
* config space .
*/
if ( eeh_probe_mode_dev ( ) & &
( pe - > type & EEH_PE_PHB ) & &
( pe - > state & ( EEH_PE_ISOLATED | EEH_PE_PHB_DEAD ) ) )
valid_cfg_log = false ;
if ( valid_cfg_log ) {
eeh_pci_enable ( pe , EEH_OPT_THAW_MMIO ) ;
eeh_ops - > configure_bridge ( pe ) ;
eeh_pe_restore_bars ( pe ) ;
pci_regs_buf [ 0 ] = 0 ;
eeh_pe_for_each_dev ( pe , edev ) {
loglen + = eeh_gather_pci_data ( edev , pci_regs_buf + loglen ,
EEH_PCI_REGS_LOG_LEN - loglen ) ;
}
}
2012-09-08 02:44:16 +04:00
eeh_ops - > get_log ( pe , severity , pci_regs_buf , loglen ) ;
2007-05-09 03:35:32 +04:00
}
2005-04-17 02:20:36 +04:00
/**
2012-02-28 00:03:51 +04:00
* eeh_token_to_phys - Convert EEH address token to phys address
* @ token : I / O token , should be address in the form 0xA . . . .
*
* This routine should be called to convert virtual I / O address
* to physical one .
2005-04-17 02:20:36 +04:00
*/
static inline unsigned long eeh_token_to_phys ( unsigned long token )
{
pte_t * ptep ;
unsigned long pa ;
2013-06-20 13:00:18 +04:00
int hugepage_shift ;
2005-04-17 02:20:36 +04:00
2013-06-20 13:00:18 +04:00
/*
* We won ' t find hugepages here , iomem
*/
ptep = find_linux_pte_or_hugepte ( init_mm . pgd , token , & hugepage_shift ) ;
2005-04-17 02:20:36 +04:00
if ( ! ptep )
return token ;
2013-06-20 13:00:18 +04:00
WARN_ON ( hugepage_shift ) ;
2005-04-17 02:20:36 +04:00
pa = pte_pfn ( * ptep ) < < PAGE_SHIFT ;
return pa | ( token & ( PAGE_SIZE - 1 ) ) ;
}
2013-06-20 09:21:16 +04:00
/*
* On PowerNV platform , we might already have fenced PHB there .
* For that case , it ' s meaningless to recover frozen PE . Intead ,
* We have to handle fenced PHB firstly .
*/
static int eeh_phb_check_failure ( struct eeh_pe * pe )
{
struct eeh_pe * phb_pe ;
unsigned long flags ;
int ret ;
if ( ! eeh_probe_mode_dev ( ) )
return - EPERM ;
/* Find the PHB PE */
phb_pe = eeh_phb_pe_get ( pe - > phb ) ;
if ( ! phb_pe ) {
pr_warning ( " %s Can't find PE for PHB#%d \n " ,
__func__ , pe - > phb - > global_number ) ;
return - EEXIST ;
}
/* If the PHB has been in problematic state */
eeh_serialize_lock ( & flags ) ;
if ( phb_pe - > state & ( EEH_PE_ISOLATED | EEH_PE_PHB_DEAD ) ) {
ret = 0 ;
goto out ;
}
/* Check PHB state */
ret = eeh_ops - > get_state ( phb_pe , NULL ) ;
if ( ( ret < 0 ) | |
( ret = = EEH_STATE_NOT_SUPPORT ) | |
( ret & ( EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE ) ) = =
( EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE ) ) {
ret = 0 ;
goto out ;
}
/* Isolate the PHB and send event */
eeh_pe_state_mark ( phb_pe , EEH_PE_ISOLATED ) ;
eeh_serialize_unlock ( flags ) ;
eeh_send_failure_event ( phb_pe ) ;
2013-06-27 09:46:46 +04:00
pr_err ( " EEH: PHB#%x failure detected \n " ,
phb_pe - > phb - > global_number ) ;
dump_stack ( ) ;
2013-06-20 09:21:16 +04:00
return 1 ;
out :
eeh_serialize_unlock ( flags ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
/**
2012-09-08 02:44:22 +04:00
* eeh_dev_check_failure - Check if all 1 ' s data is due to EEH slot freeze
* @ edev : eeh device
2005-04-17 02:20:36 +04:00
*
* Check for an EEH failure for the given device node . Call this
* routine if the result of a read was all 0xff ' s and you want to
* find out if this is due to an EEH slot freeze . This routine
* will query firmware for the EEH status .
*
* Returns 0 if there has not been an EEH error ; otherwise returns
2005-11-04 03:47:50 +03:00
* a non - zero value and queues up a slot isolation event notification .
2005-04-17 02:20:36 +04:00
*
* It is safe to call this routine in an interrupt context .
*/
2012-09-08 02:44:22 +04:00
int eeh_dev_check_failure ( struct eeh_dev * edev )
2005-04-17 02:20:36 +04:00
{
int ret ;
unsigned long flags ;
2012-09-08 02:44:22 +04:00
struct device_node * dn ;
struct pci_dev * dev ;
2012-09-08 02:44:13 +04:00
struct eeh_pe * pe ;
2005-11-04 03:49:23 +03:00
int rc = 0 ;
2008-07-21 20:40:17 +04:00
const char * location ;
2005-04-17 02:20:36 +04:00
2012-02-29 19:47:45 +04:00
eeh_stats . total_mmio_ffs + + ;
2005-04-17 02:20:36 +04:00
if ( ! eeh_subsystem_enabled )
return 0 ;
2012-09-08 02:44:22 +04:00
if ( ! edev ) {
2012-02-29 19:47:45 +04:00
eeh_stats . no_dn + + ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-11-04 03:48:52 +03:00
}
2012-09-08 02:44:22 +04:00
dn = eeh_dev_to_of_node ( edev ) ;
dev = eeh_dev_to_pci_dev ( edev ) ;
2012-09-08 02:44:13 +04:00
pe = edev - > pe ;
2005-04-17 02:20:36 +04:00
/* Access to IO BARs might get this far and still not want checking. */
2012-09-08 02:44:13 +04:00
if ( ! pe ) {
2012-02-29 19:47:45 +04:00
eeh_stats . ignored_check + + ;
2012-09-08 02:44:13 +04:00
pr_debug ( " EEH: Ignored check for %s %s \n " ,
eeh_pci_name ( dev ) , dn - > full_name ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-09-08 02:44:13 +04:00
if ( ! pe - > addr & & ! pe - > config_addr ) {
2012-02-29 19:47:45 +04:00
eeh_stats . no_cfg_addr + + ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2013-06-20 09:21:16 +04:00
/*
* On PowerNV platform , we might already have fenced PHB
* there and we need take care of that firstly .
*/
ret = eeh_phb_check_failure ( pe ) ;
if ( ret > 0 )
return ret ;
2005-11-04 03:49:23 +03:00
/* If we already have a pending isolation event for this
* slot , we know it ' s bad already , we don ' t need to check .
* Do this checking under a lock ; as multiple PCI devices
* in one slot might report errors simultaneously , and we
* only want one error recovery routine running .
2005-04-17 02:20:36 +04:00
*/
2013-06-20 09:21:03 +04:00
eeh_serialize_lock ( & flags ) ;
2005-11-04 03:49:23 +03:00
rc = 1 ;
2012-09-08 02:44:13 +04:00
if ( pe - > state & EEH_PE_ISOLATED ) {
pe - > check_count + + ;
if ( pe - > check_count % EEH_MAX_FAILS = = 0 ) {
2008-07-21 20:40:17 +04:00
location = of_get_property ( dn , " ibm,loc-code " , NULL ) ;
2012-02-28 00:03:51 +04:00
printk ( KERN_ERR " EEH: %d reads ignored for recovering device at "
2008-07-21 20:40:17 +04:00
" location=%s driver=%s pci addr=%s \n " ,
2012-09-08 02:44:13 +04:00
pe - > check_count , location ,
2012-01-11 13:09:58 +04:00
eeh_driver_name ( dev ) , eeh_pci_name ( dev ) ) ;
2012-02-28 00:03:51 +04:00
printk ( KERN_ERR " EEH: Might be infinite loop in %s driver \n " ,
2012-01-11 13:09:58 +04:00
eeh_driver_name ( dev ) ) ;
2005-11-04 03:49:31 +03:00
dump_stack ( ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-04 03:49:23 +03:00
goto dn_unlock ;
2005-04-17 02:20:36 +04:00
}
/*
* Now test for an EEH failure . This is VERY expensive .
* Note that the eeh_config_addr may be a parent device
* in the case of a device behind a bridge , or it may be
* function zero of a multi - function device .
* In any case they must share a common PHB .
*/
2012-09-08 02:44:13 +04:00
ret = eeh_ops - > get_state ( pe , NULL ) ;
2005-11-04 03:49:15 +03:00
2007-03-19 22:51:00 +03:00
/* Note that config-io to empty slots may fail;
2012-02-28 00:03:51 +04:00
* they are empty when they don ' t have children .
2012-02-28 00:03:57 +04:00
* We will punt with the following conditions : Failure to get
* PE ' s state , EEH not support and Permanently unavailable
* state , PE is in good state .
2012-02-28 00:03:51 +04:00
*/
2012-02-28 00:03:57 +04:00
if ( ( ret < 0 ) | |
( ret = = EEH_STATE_NOT_SUPPORT ) | |
( ret & ( EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE ) ) = =
( EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE ) ) {
2012-02-29 19:47:45 +04:00
eeh_stats . false_positives + + ;
2012-09-08 02:44:13 +04:00
pe - > false_positives + + ;
2005-11-04 03:49:23 +03:00
rc = 0 ;
goto dn_unlock ;
2005-11-04 03:49:15 +03:00
}
2012-02-29 19:47:45 +04:00
eeh_stats . slot_resets + + ;
2013-06-20 09:20:51 +04:00
2005-11-04 03:49:23 +03:00
/* Avoid repeated reports of this failure, including problems
* with other functions on this device , and functions under
2012-02-28 00:03:51 +04:00
* bridges .
*/
2012-09-08 02:44:13 +04:00
eeh_pe_state_mark ( pe , EEH_PE_ISOLATED ) ;
2013-06-20 09:21:03 +04:00
eeh_serialize_unlock ( flags ) ;
2005-04-17 02:20:36 +04:00
2012-09-08 02:44:13 +04:00
eeh_send_failure_event ( pe ) ;
2005-11-04 03:52:49 +03:00
2005-04-17 02:20:36 +04:00
/* Most EEH events are due to device driver bugs. Having
* a stack trace will help the device - driver authors figure
2012-02-28 00:03:51 +04:00
* out what happened . So print that out .
*/
2013-06-27 09:46:46 +04:00
pr_err ( " EEH: Frozen PE#%x detected on PHB#%x \n " ,
pe - > addr , pe - > phb - > global_number ) ;
dump_stack ( ) ;
2005-11-04 03:49:23 +03:00
return 1 ;
dn_unlock :
2013-06-20 09:21:03 +04:00
eeh_serialize_unlock ( flags ) ;
2005-11-04 03:49:23 +03:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2012-09-08 02:44:22 +04:00
EXPORT_SYMBOL_GPL ( eeh_dev_check_failure ) ;
2005-04-17 02:20:36 +04:00
/**
2012-02-28 00:03:51 +04:00
* eeh_check_failure - Check if all 1 ' s data is due to EEH slot freeze
* @ token : I / O token , should be address in the form 0xA . . . .
* @ val : value , should be all 1 ' s ( XXX why do we need this arg ? ? )
2005-04-17 02:20:36 +04:00
*
* Check for an EEH failure at the given token address . Call this
* routine if the result of a read was all 0xff ' s and you want to
* find out if this is due to an EEH slot freeze event . This routine
* will query firmware for the EEH status .
*
* Note this routine is safe to call in an interrupt context .
*/
unsigned long eeh_check_failure ( const volatile void __iomem * token , unsigned long val )
{
unsigned long addr ;
2012-09-08 02:44:22 +04:00
struct eeh_dev * edev ;
2005-04-17 02:20:36 +04:00
/* Finding the phys addr + pci device; this is pretty quick. */
addr = eeh_token_to_phys ( ( unsigned long __force ) token ) ;
2012-09-08 02:44:23 +04:00
edev = eeh_addr_cache_get_dev ( addr ) ;
2012-09-08 02:44:22 +04:00
if ( ! edev ) {
2012-02-29 19:47:45 +04:00
eeh_stats . no_device + + ;
2005-04-17 02:20:36 +04:00
return val ;
2005-11-04 03:48:52 +03:00
}
2005-04-17 02:20:36 +04:00
2012-09-08 02:44:22 +04:00
eeh_dev_check_failure ( edev ) ;
2005-04-17 02:20:36 +04:00
2012-09-08 02:44:22 +04:00
pci_dev_put ( eeh_dev_to_pci_dev ( edev ) ) ;
2005-04-17 02:20:36 +04:00
return val ;
}
EXPORT_SYMBOL ( eeh_check_failure ) ;
2005-11-04 03:50:10 +03:00
2006-09-16 03:57:42 +04:00
/**
2012-02-28 00:03:52 +04:00
* eeh_pci_enable - Enable MMIO or DMA transfers for this slot
2012-09-08 02:44:16 +04:00
* @ pe : EEH PE
2012-02-28 00:03:51 +04:00
*
* This routine should be called to reenable frozen MMIO or DMA
* so that it would work correctly again . It ' s useful while doing
* recovery or log collection on the indicated device .
2006-09-16 03:57:42 +04:00
*/
2012-09-08 02:44:16 +04:00
int eeh_pci_enable ( struct eeh_pe * pe , int function )
2006-09-16 03:57:42 +04:00
{
int rc ;
2012-09-08 02:44:16 +04:00
rc = eeh_ops - > set_option ( pe , function ) ;
2006-09-16 03:57:42 +04:00
if ( rc )
2012-09-08 02:44:16 +04:00
pr_warning ( " %s: Unexpected state change %d on PHB#%d-PE#%x, err=%d \n " ,
__func__ , function , pe - > phb - > global_number , pe - > addr , rc ) ;
2006-09-16 03:57:42 +04:00
2012-09-08 02:44:16 +04:00
rc = eeh_ops - > wait_state ( pe , PCI_BUS_RESET_WAIT_MSEC ) ;
2012-02-28 00:03:57 +04:00
if ( rc > 0 & & ( rc & EEH_STATE_MMIO_ENABLED ) & &
( function = = EEH_OPT_THAW_MMIO ) )
2007-03-19 22:59:59 +03:00
return 0 ;
2006-09-16 03:57:42 +04:00
return rc ;
}
2007-05-08 02:04:05 +04:00
/**
* pcibios_set_pcie_slot_reset - Set PCI - E reset state
2012-02-28 00:03:51 +04:00
* @ dev : pci device struct
* @ state : reset state to enter
2007-05-08 02:04:05 +04:00
*
* Return value :
* 0 if success
2012-02-28 00:03:51 +04:00
*/
2007-05-08 02:04:05 +04:00
int pcibios_set_pcie_reset_state ( struct pci_dev * dev , enum pcie_reset_state state )
{
2012-09-08 02:44:17 +04:00
struct eeh_dev * edev = pci_dev_to_eeh_dev ( dev ) ;
struct eeh_pe * pe = edev - > pe ;
if ( ! pe ) {
pr_err ( " %s: No PE found on PCI device %s \n " ,
__func__ , pci_name ( dev ) ) ;
return - EINVAL ;
}
2007-05-08 02:04:05 +04:00
switch ( state ) {
case pcie_deassert_reset :
2012-09-08 02:44:17 +04:00
eeh_ops - > reset ( pe , EEH_RESET_DEACTIVATE ) ;
2007-05-08 02:04:05 +04:00
break ;
case pcie_hot_reset :
2012-09-08 02:44:17 +04:00
eeh_ops - > reset ( pe , EEH_RESET_HOT ) ;
2007-05-08 02:04:05 +04:00
break ;
case pcie_warm_reset :
2012-09-08 02:44:17 +04:00
eeh_ops - > reset ( pe , EEH_RESET_FUNDAMENTAL ) ;
2007-05-08 02:04:05 +04:00
break ;
default :
return - EINVAL ;
} ;
return 0 ;
}
2006-09-16 03:56:35 +04:00
/**
2012-09-08 02:44:17 +04:00
* eeh_set_pe_freset - Check the required reset for the indicated device
* @ data : EEH device
* @ flag : return value
2012-02-28 00:03:51 +04:00
*
* Each device might have its preferred reset type : fundamental or
* hot reset . The routine is used to collected the information for
* the indicated device and its children so that the bunch of the
* devices could be reset properly .
*/
2012-09-08 02:44:17 +04:00
static void * eeh_set_dev_freset ( void * data , void * flag )
2012-02-28 00:03:51 +04:00
{
struct pci_dev * dev ;
2012-09-08 02:44:17 +04:00
unsigned int * freset = ( unsigned int * ) flag ;
struct eeh_dev * edev = ( struct eeh_dev * ) data ;
2005-11-04 03:50:10 +03:00
2012-09-08 02:44:17 +04:00
dev = eeh_dev_to_pci_dev ( edev ) ;
2012-02-28 00:03:51 +04:00
if ( dev )
* freset | = dev - > needs_freset ;
2012-09-08 02:44:17 +04:00
return NULL ;
2012-02-28 00:03:51 +04:00
}
/**
2012-02-28 00:03:52 +04:00
* eeh_reset_pe_once - Assert the pci # RST line for 1 / 4 second
2012-09-08 02:44:17 +04:00
* @ pe : EEH PE
2012-02-28 00:03:51 +04:00
*
* Assert the PCI # RST line for 1 / 4 second .
*/
2012-09-08 02:44:17 +04:00
static void eeh_reset_pe_once ( struct eeh_pe * pe )
2005-11-04 03:50:10 +03:00
{
2011-04-22 13:59:47 +04:00
unsigned int freset = 0 ;
2009-07-31 02:42:39 +04:00
2011-04-22 13:59:47 +04:00
/* Determine type of EEH reset required for
* Partitionable Endpoint , a hot - reset ( 1 )
* or a fundamental reset ( 3 ) .
* A fundamental reset required by any device under
* Partitionable Endpoint trumps hot - reset .
2013-06-20 09:20:51 +04:00
*/
2012-09-08 02:44:17 +04:00
eeh_pe_dev_traverse ( pe , eeh_set_dev_freset , & freset ) ;
2011-04-22 13:59:47 +04:00
if ( freset )
2012-09-08 02:44:17 +04:00
eeh_ops - > reset ( pe , EEH_RESET_FUNDAMENTAL ) ;
2009-07-31 02:42:39 +04:00
else
2012-09-08 02:44:17 +04:00
eeh_ops - > reset ( pe , EEH_RESET_HOT ) ;
2005-11-04 03:50:10 +03:00
/* The PCI bus requires that the reset be held high for at least
2012-02-28 00:03:51 +04:00
* a 100 milliseconds . We wait a bit longer ' just in case ' .
*/
2005-11-04 03:50:10 +03:00
# define PCI_BUS_RST_HOLD_TIME_MSEC 250
2012-02-28 00:03:51 +04:00
msleep ( PCI_BUS_RST_HOLD_TIME_MSEC ) ;
2013-06-20 09:20:51 +04:00
/* We might get hit with another EEH freeze as soon as the
2005-11-04 03:50:48 +03:00
* pci slot reset line is dropped . Make sure we don ' t miss
2012-02-28 00:03:51 +04:00
* these , and clear the flag now .
*/
2012-09-08 02:44:20 +04:00
eeh_pe_state_clear ( pe , EEH_PE_ISOLATED ) ;
2005-11-04 03:50:48 +03:00
2012-09-08 02:44:17 +04:00
eeh_ops - > reset ( pe , EEH_RESET_DEACTIVATE ) ;
2005-11-04 03:50:10 +03:00
/* After a PCI slot has been reset, the PCI Express spec requires
* a 1.5 second idle time for the bus to stabilize , before starting
2012-02-28 00:03:51 +04:00
* up traffic .
*/
2005-11-04 03:50:10 +03:00
# define PCI_BUS_SETTLE_TIME_MSEC 1800
2012-02-28 00:03:51 +04:00
msleep ( PCI_BUS_SETTLE_TIME_MSEC ) ;
2006-09-22 03:25:56 +04:00
}
2012-02-28 00:03:51 +04:00
/**
2012-02-28 00:03:52 +04:00
* eeh_reset_pe - Reset the indicated PE
2012-09-08 02:44:17 +04:00
* @ pe : EEH PE
2012-02-28 00:03:51 +04:00
*
* This routine should be called to reset indicated device , including
* PE . A PE might include multiple PCI devices and sometimes PCI bridges
* might be involved as well .
*/
2012-09-08 02:44:17 +04:00
int eeh_reset_pe ( struct eeh_pe * pe )
2006-09-22 03:25:56 +04:00
{
2013-06-20 09:20:58 +04:00
int flags = ( EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE ) ;
2006-09-22 03:25:56 +04:00
int i , rc ;
2007-03-19 22:58:07 +03:00
/* Take three shots at resetting the bus */
for ( i = 0 ; i < 3 ; i + + ) {
2012-09-08 02:44:17 +04:00
eeh_reset_pe_once ( pe ) ;
2005-11-04 03:50:10 +03:00
2012-09-08 02:44:17 +04:00
rc = eeh_ops - > wait_state ( pe , PCI_BUS_RESET_WAIT_MSEC ) ;
2013-06-20 09:20:58 +04:00
if ( ( rc & flags ) = = flags )
2005-11-04 03:54:54 +03:00
return 0 ;
2006-09-22 03:25:56 +04:00
if ( rc < 0 ) {
2012-09-08 02:44:17 +04:00
pr_err ( " %s: Unrecoverable slot failure on PHB#%d-PE#%x " ,
__func__ , pe - > phb - > global_number , pe - > addr ) ;
2005-11-04 03:54:54 +03:00
return - 1 ;
2006-09-22 03:25:56 +04:00
}
2012-09-08 02:44:17 +04:00
pr_err ( " EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d \n " ,
i + 1 , pe - > phb - > global_number , pe - > addr , rc ) ;
2005-11-04 03:50:10 +03:00
}
2005-11-04 03:54:54 +03:00
2007-03-19 22:58:07 +03:00
return - 1 ;
2005-11-04 03:50:10 +03:00
}
2005-11-04 03:50:17 +03:00
/**
2012-02-28 00:03:51 +04:00
* eeh_save_bars - Save device bars
2012-02-28 00:04:07 +04:00
* @ edev : PCI device associated EEH device
2005-11-04 03:50:17 +03:00
*
* Save the values of the device bars . Unlike the restore
* routine , this routine is * not * recursive . This is because
2011-02-24 23:10:18 +03:00
* PCI devices are added individually ; but , for the restore ,
2005-11-04 03:50:17 +03:00
* an entire slot is reset at a time .
*/
2012-09-08 02:44:21 +04:00
void eeh_save_bars ( struct eeh_dev * edev )
2005-11-04 03:50:17 +03:00
{
int i ;
2012-02-28 00:04:07 +04:00
struct device_node * dn ;
2005-11-04 03:50:17 +03:00
2012-02-28 00:04:07 +04:00
if ( ! edev )
2005-11-04 03:50:17 +03:00
return ;
2012-02-28 00:04:07 +04:00
dn = eeh_dev_to_of_node ( edev ) ;
2013-06-20 09:20:51 +04:00
2005-11-04 03:50:17 +03:00
for ( i = 0 ; i < 16 ; i + + )
2012-02-28 00:04:11 +04:00
eeh_ops - > read_config ( dn , i * 4 , 4 , & edev - > config_space [ i ] ) ;
2005-11-04 03:50:17 +03:00
}
2012-02-28 00:03:53 +04:00
/**
* eeh_ops_register - Register platform dependent EEH operations
* @ ops : platform dependent EEH operations
*
* Register the platform dependent EEH operation callback
* functions . The platform should call this function before
* any other EEH operations .
*/
int __init eeh_ops_register ( struct eeh_ops * ops )
{
if ( ! ops - > name ) {
pr_warning ( " %s: Invalid EEH ops name for %p \n " ,
__func__ , ops ) ;
return - EINVAL ;
}
if ( eeh_ops & & eeh_ops ! = ops ) {
pr_warning ( " %s: EEH ops of platform %s already existing (%s) \n " ,
__func__ , eeh_ops - > name , ops - > name ) ;
return - EEXIST ;
}
eeh_ops = ops ;
return 0 ;
}
/**
* eeh_ops_unregister - Unreigster platform dependent EEH operations
* @ name : name of EEH platform operations
*
* Unregister the platform dependent EEH operation callback
* functions .
*/
int __exit eeh_ops_unregister ( const char * name )
{
if ( ! name | | ! strlen ( name ) ) {
pr_warning ( " %s: Invalid EEH ops name \n " ,
__func__ ) ;
return - EINVAL ;
}
if ( eeh_ops & & ! strcmp ( eeh_ops - > name , name ) ) {
eeh_ops = NULL ;
return 0 ;
}
return - EEXIST ;
}
2012-02-28 00:03:51 +04:00
/**
* eeh_init - EEH initialization
*
2005-04-17 02:20:36 +04:00
* Initialize EEH by trying to enable it for all of the adapters in the system .
* As a side effect we can determine here if eeh is supported at all .
* Note that we leave EEH on so failed config cycles won ' t cause a machine
* check . If a user turns off EEH for a particular adapter they are really
* telling Linux to ignore errors . Some hardware ( e . g . POWER5 ) won ' t
* grant access to a slot if EEH isn ' t enabled , and so we always enable
* EEH for all slots / all devices .
*
* The eeh - force - off option disables EEH checking globally , for all slots .
* Even if force - off is set , the EEH hardware is still enabled , so that
* newer systems can boot .
*/
2013-06-20 09:20:56 +04:00
int __init eeh_init ( void )
2005-04-17 02:20:36 +04:00
{
2012-03-21 01:30:29 +04:00
struct pci_controller * hose , * tmp ;
struct device_node * phb ;
2013-06-20 09:20:56 +04:00
static int cnt = 0 ;
int ret = 0 ;
/*
* We have to delay the initialization on PowerNV after
* the PCI hierarchy tree has been built because the PEs
* are figured out based on PCI devices instead of device
* tree nodes
*/
if ( machine_is ( powernv ) & & cnt + + < = 0 )
return ret ;
2012-02-28 00:03:54 +04:00
/* call platform initialization function */
if ( ! eeh_ops ) {
pr_warning ( " %s: Platform EEH operation not found \n " ,
__func__ ) ;
2012-09-08 02:44:02 +04:00
return - EEXIST ;
2012-02-28 00:03:54 +04:00
} else if ( ( ret = eeh_ops - > init ( ) ) ) {
pr_warning ( " %s: Failed to call platform init function (%d) \n " ,
__func__ , ret ) ;
2012-09-08 02:44:02 +04:00
return ret ;
2012-02-28 00:03:54 +04:00
}
2005-04-17 02:20:36 +04:00
2013-06-20 09:21:00 +04:00
/* Initialize EEH event */
ret = eeh_event_init ( ) ;
if ( ret )
return ret ;
2012-03-21 01:30:29 +04:00
/* Enable EEH for all adapters */
2012-09-08 02:44:21 +04:00
if ( eeh_probe_mode_devtree ( ) ) {
list_for_each_entry_safe ( hose , tmp ,
& hose_list , list_node ) {
phb = hose - > dn ;
traverse_pci_devices ( phb , eeh_ops - > of_probe , NULL ) ;
}
2013-06-20 09:20:56 +04:00
} else if ( eeh_probe_mode_dev ( ) ) {
list_for_each_entry_safe ( hose , tmp ,
& hose_list , list_node )
pci_walk_bus ( hose - > bus , eeh_ops - > dev_probe , NULL ) ;
} else {
pr_warning ( " %s: Invalid probe mode %d \n " ,
__func__ , eeh_probe_mode ) ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2013-06-20 09:20:57 +04:00
/*
* Call platform post - initialization . Actually , It ' s good chance
* to inform platform that EEH is ready to supply service if the
* I / O cache stuff has been built up .
*/
if ( eeh_ops - > post_init ) {
ret = eeh_ops - > post_init ( ) ;
if ( ret )
return ret ;
}
2005-04-17 02:20:36 +04:00
if ( eeh_subsystem_enabled )
2012-09-08 02:44:21 +04:00
pr_info ( " EEH: PCI Enhanced I/O Error Handling Enabled \n " ) ;
2005-04-17 02:20:36 +04:00
else
2012-09-08 02:44:21 +04:00
pr_warning ( " EEH: No capable adapters found \n " ) ;
2012-09-08 02:44:02 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2012-09-08 02:44:02 +04:00
core_initcall_sync ( eeh_init ) ;
2005-04-17 02:20:36 +04:00
/**
2012-02-28 00:03:51 +04:00
* eeh_add_device_early - Enable EEH for the indicated device_node
2005-04-17 02:20:36 +04:00
* @ dn : device node for which to set up EEH
*
* This routine must be used to perform EEH initialization for PCI
* devices that were added after system boot ( e . g . hotplug , dlpar ) .
* This routine must be called before any i / o is performed to the
* adapter ( inluding any config - space i / o ) .
* Whether this actually enables EEH or not for this device depends
* on the CEC architecture , type of the device , on earlier boot
* command - line arguments & etc .
*/
2006-03-31 22:04:52 +04:00
static void eeh_add_device_early ( struct device_node * dn )
2005-04-17 02:20:36 +04:00
{
struct pci_controller * phb ;
2013-06-20 09:20:59 +04:00
/*
* If we ' re doing EEH probe based on PCI device , we
* would delay the probe until late stage because
* the PCI device isn ' t available this moment .
*/
if ( ! eeh_probe_mode_devtree ( ) )
return ;
2012-09-17 08:34:28 +04:00
if ( ! of_node_to_eeh_dev ( dn ) )
2005-04-17 02:20:36 +04:00
return ;
2012-02-28 00:04:07 +04:00
phb = of_node_to_eeh_dev ( dn ) - > phb ;
2005-11-04 03:54:23 +03:00
/* USB Bus children of PCI devices will not have BUID's */
if ( NULL = = phb | | 0 = = phb - > buid )
2005-04-17 02:20:36 +04:00
return ;
2012-09-08 02:44:21 +04:00
eeh_ops - > of_probe ( dn , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2012-02-28 00:03:51 +04:00
/**
* eeh_add_device_tree_early - Enable EEH for the indicated device
* @ dn : device node
*
* This routine must be used to perform EEH initialization for the
* indicated PCI device that was added after system boot ( e . g .
* hotplug , dlpar ) .
*/
2005-11-04 03:51:31 +03:00
void eeh_add_device_tree_early ( struct device_node * dn )
{
struct device_node * sib ;
2007-12-21 07:52:07 +03:00
for_each_child_of_node ( dn , sib )
2005-11-04 03:51:31 +03:00
eeh_add_device_tree_early ( sib ) ;
eeh_add_device_early ( dn ) ;
}
EXPORT_SYMBOL_GPL ( eeh_add_device_tree_early ) ;
2005-04-17 02:20:36 +04:00
/**
2012-02-28 00:03:51 +04:00
* eeh_add_device_late - Perform EEH initialization for the indicated pci device
2005-04-17 02:20:36 +04:00
* @ dev : pci device for which to set up EEH
*
* This routine must be used to complete EEH initialization for PCI
* devices that were added after system boot ( e . g . hotplug , dlpar ) .
*/
2006-03-31 22:04:52 +04:00
static void eeh_add_device_late ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-04 03:48:45 +03:00
struct device_node * dn ;
2012-02-28 00:04:07 +04:00
struct eeh_dev * edev ;
2005-11-04 03:48:45 +03:00
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! eeh_subsystem_enabled )
return ;
2008-10-27 22:48:41 +03:00
pr_debug ( " EEH: Adding device %s \n " , pci_name ( dev ) ) ;
2005-04-17 02:20:36 +04:00
2005-11-04 03:48:45 +03:00
dn = pci_device_to_OF_node ( dev ) ;
2012-04-16 23:55:39 +04:00
edev = of_node_to_eeh_dev ( dn ) ;
2012-02-28 00:04:07 +04:00
if ( edev - > pdev = = dev ) {
2008-10-27 22:48:41 +03:00
pr_debug ( " EEH: Already referenced ! \n " ) ;
return ;
}
2012-02-28 00:04:07 +04:00
WARN_ON ( edev - > pdev ) ;
2008-10-27 22:48:41 +03:00
2012-02-28 00:03:51 +04:00
pci_dev_get ( dev ) ;
2012-02-28 00:04:07 +04:00
edev - > pdev = dev ;
dev - > dev . archdata . edev = edev ;
2005-11-04 03:48:45 +03:00
2013-06-20 09:20:59 +04:00
/*
* We have to do the EEH probe here because the PCI device
* hasn ' t been created yet in the early stage .
*/
if ( eeh_probe_mode_dev ( ) )
eeh_ops - > dev_probe ( dev , NULL ) ;
2012-09-08 02:44:23 +04:00
eeh_addr_cache_insert_dev ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-31 22:04:52 +04:00
2012-02-28 00:03:51 +04:00
/**
* eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus
* @ bus : PCI bus
*
* This routine must be used to perform EEH initialization for PCI
* devices which are attached to the indicated PCI bus . The PCI bus
* is added after system boot through hotplug or dlpar .
*/
2006-03-31 22:04:52 +04:00
void eeh_add_device_tree_late ( struct pci_bus * bus )
{
struct pci_dev * dev ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
2013-06-20 09:20:51 +04:00
eeh_add_device_late ( dev ) ;
if ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) {
struct pci_bus * subbus = dev - > subordinate ;
if ( subbus )
eeh_add_device_tree_late ( subbus ) ;
}
2006-03-31 22:04:52 +04:00
}
}
EXPORT_SYMBOL_GPL ( eeh_add_device_tree_late ) ;
2005-04-17 02:20:36 +04:00
2012-12-28 13:13:19 +04:00
/**
* eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
* @ bus : PCI bus
*
* This routine must be used to add EEH sysfs files for PCI
* devices which are attached to the indicated PCI bus . The PCI bus
* is added after system boot through hotplug or dlpar .
*/
void eeh_add_sysfs_files ( struct pci_bus * bus )
{
struct pci_dev * dev ;
list_for_each_entry ( dev , & bus - > devices , bus_list ) {
eeh_sysfs_add_device ( dev ) ;
if ( dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) {
struct pci_bus * subbus = dev - > subordinate ;
if ( subbus )
eeh_add_sysfs_files ( subbus ) ;
}
}
}
EXPORT_SYMBOL_GPL ( eeh_add_sysfs_files ) ;
2005-04-17 02:20:36 +04:00
/**
2012-02-28 00:03:51 +04:00
* eeh_remove_device - Undo EEH setup for the indicated pci device
2005-04-17 02:20:36 +04:00
* @ dev : pci device to be removed
2012-09-11 23:16:17 +04:00
* @ purge_pe : remove the PE or not
2005-04-17 02:20:36 +04:00
*
2006-03-31 22:04:52 +04:00
* This routine should be called when a device is removed from
* a running system ( e . g . by hotplug or dlpar ) . It unregisters
* the PCI device from the EEH subsystem . I / O errors affecting
* this device will no longer be detected after this call ; thus ,
* i / o errors affecting this slot may leave this device unusable .
2005-04-17 02:20:36 +04:00
*/
2012-09-11 23:16:17 +04:00
static void eeh_remove_device ( struct pci_dev * dev , int purge_pe )
2005-04-17 02:20:36 +04:00
{
2012-02-28 00:04:07 +04:00
struct eeh_dev * edev ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! eeh_subsystem_enabled )
return ;
2012-02-28 00:04:07 +04:00
edev = pci_dev_to_eeh_dev ( dev ) ;
2005-04-17 02:20:36 +04:00
/* Unregister the device with the EEH/PCI address search system */
2008-10-27 22:48:41 +03:00
pr_debug ( " EEH: Removing device %s \n " , pci_name ( dev ) ) ;
2005-11-04 03:48:45 +03:00
2012-02-28 00:04:07 +04:00
if ( ! edev | | ! edev - > pdev ) {
2008-10-27 22:48:41 +03:00
pr_debug ( " EEH: Not referenced ! \n " ) ;
return ;
2006-04-07 00:41:41 +04:00
}
2012-02-28 00:04:07 +04:00
edev - > pdev = NULL ;
dev - > dev . archdata . edev = NULL ;
2012-02-28 00:03:51 +04:00
pci_dev_put ( dev ) ;
2008-10-27 22:48:41 +03:00
2012-09-11 23:16:17 +04:00
eeh_rmv_from_parent_pe ( edev , purge_pe ) ;
2012-09-08 02:44:23 +04:00
eeh_addr_cache_rmv_dev ( dev ) ;
2008-10-27 22:48:41 +03:00
eeh_sysfs_remove_device ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2012-02-28 00:03:51 +04:00
/**
* eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
* @ dev : PCI device
2012-09-11 23:16:17 +04:00
* @ purge_pe : remove the corresponding PE or not
2012-02-28 00:03:51 +04:00
*
* This routine must be called when a device is removed from the
* running system through hotplug or dlpar . The corresponding
* PCI address cache will be removed .
*/
2012-09-11 23:16:17 +04:00
void eeh_remove_bus_device ( struct pci_dev * dev , int purge_pe )
2005-11-04 03:51:31 +03:00
{
2006-03-31 22:04:52 +04:00
struct pci_bus * bus = dev - > subordinate ;
struct pci_dev * child , * tmp ;
2012-09-11 23:16:17 +04:00
eeh_remove_device ( dev , purge_pe ) ;
2006-03-31 22:04:52 +04:00
if ( bus & & dev - > hdr_type = = PCI_HEADER_TYPE_BRIDGE ) {
list_for_each_entry_safe ( child , tmp , & bus - > devices , bus_list )
2012-09-11 23:16:17 +04:00
eeh_remove_bus_device ( child , purge_pe ) ;
2005-11-04 03:51:31 +03:00
}
}
EXPORT_SYMBOL_GPL ( eeh_remove_bus_device ) ;
2005-04-17 02:20:36 +04:00
static int proc_eeh_show ( struct seq_file * m , void * v )
{
if ( 0 = = eeh_subsystem_enabled ) {
seq_printf ( m , " EEH Subsystem is globally disabled \n " ) ;
2012-02-29 19:47:45 +04:00
seq_printf ( m , " eeh_total_mmio_ffs=%llu \n " , eeh_stats . total_mmio_ffs ) ;
2005-04-17 02:20:36 +04:00
} else {
seq_printf ( m , " EEH Subsystem is enabled \n " ) ;
2005-11-04 03:48:52 +03:00
seq_printf ( m ,
2012-02-29 19:47:45 +04:00
" no device=%llu \n "
" no device node=%llu \n "
" no config address=%llu \n "
" check not wanted=%llu \n "
" eeh_total_mmio_ffs=%llu \n "
" eeh_false_positives=%llu \n "
" eeh_slot_resets=%llu \n " ,
eeh_stats . no_device ,
eeh_stats . no_dn ,
eeh_stats . no_cfg_addr ,
eeh_stats . ignored_check ,
eeh_stats . total_mmio_ffs ,
eeh_stats . false_positives ,
eeh_stats . slot_resets ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static int proc_eeh_open ( struct inode * inode , struct file * file )
{
return single_open ( file , proc_eeh_show , NULL ) ;
}
2007-02-12 11:55:31 +03:00
static const struct file_operations proc_eeh_operations = {
2005-04-17 02:20:36 +04:00
. open = proc_eeh_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int __init eeh_init_proc ( void )
{
2008-04-29 12:02:26 +04:00
if ( machine_is ( pseries ) )
2011-08-26 14:36:31 +04:00
proc_create ( " powerpc/eeh " , 0 , NULL , & proc_eeh_operations ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
__initcall ( eeh_init_proc ) ;