Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
aba0eb84
Commit
aba0eb84
authored
Mar 13, 2012
by
Benjamin Herrenschmidt
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'eeh' into next
parents
7230c564
3780444c
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
1479 additions
and
869 deletions
+1479
-869
arch/powerpc/include/asm/device.h
arch/powerpc/include/asm/device.h
+3
-0
arch/powerpc/include/asm/eeh.h
arch/powerpc/include/asm/eeh.h
+106
-28
arch/powerpc/include/asm/eeh_event.h
arch/powerpc/include/asm/eeh_event.h
+9
-24
arch/powerpc/include/asm/ppc-pci.h
arch/powerpc/include/asm/ppc-pci.h
+9
-80
arch/powerpc/kernel/of_platform.c
arch/powerpc/kernel/of_platform.c
+5
-1
arch/powerpc/kernel/rtas_pci.c
arch/powerpc/kernel/rtas_pci.c
+3
-0
arch/powerpc/platforms/pseries/Makefile
arch/powerpc/platforms/pseries/Makefile
+2
-1
arch/powerpc/platforms/pseries/eeh.c
arch/powerpc/platforms/pseries/eeh.c
+462
-582
arch/powerpc/platforms/pseries/eeh_cache.c
arch/powerpc/platforms/pseries/eeh_cache.c
+27
-17
arch/powerpc/platforms/pseries/eeh_dev.c
arch/powerpc/platforms/pseries/eeh_dev.c
+102
-0
arch/powerpc/platforms/pseries/eeh_driver.c
arch/powerpc/platforms/pseries/eeh_driver.c
+120
-93
arch/powerpc/platforms/pseries/eeh_event.c
arch/powerpc/platforms/pseries/eeh_event.c
+28
-27
arch/powerpc/platforms/pseries/eeh_pseries.c
arch/powerpc/platforms/pseries/eeh_pseries.c
+565
-0
arch/powerpc/platforms/pseries/eeh_sysfs.c
arch/powerpc/platforms/pseries/eeh_sysfs.c
+11
-14
arch/powerpc/platforms/pseries/msi.c
arch/powerpc/platforms/pseries/msi.c
+1
-1
arch/powerpc/platforms/pseries/pci_dlpar.c
arch/powerpc/platforms/pseries/pci_dlpar.c
+3
-0
arch/powerpc/platforms/pseries/setup.c
arch/powerpc/platforms/pseries/setup.c
+6
-1
include/linux/of.h
include/linux/of.h
+10
-0
include/linux/pci.h
include/linux/pci.h
+7
-0
No files found.
arch/powerpc/include/asm/device.h
View file @
aba0eb84
...
@@ -31,6 +31,9 @@ struct dev_archdata {
...
@@ -31,6 +31,9 @@ struct dev_archdata {
#ifdef CONFIG_SWIOTLB
#ifdef CONFIG_SWIOTLB
dma_addr_t
max_direct_dma_addr
;
dma_addr_t
max_direct_dma_addr
;
#endif
#endif
#ifdef CONFIG_EEH
struct
eeh_dev
*
edev
;
#endif
};
};
struct
pdev_archdata
{
struct
pdev_archdata
{
...
...
arch/powerpc/include/asm/eeh.h
View file @
aba0eb84
/*
/*
* eeh.h
* Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation.
* Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation.
* Copyright 2001-2012 IBM Corporation.
*
*
* This program is free software; you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by
...
@@ -31,44 +31,105 @@ struct device_node;
...
@@ -31,44 +31,105 @@ struct device_node;
#ifdef CONFIG_EEH
#ifdef CONFIG_EEH
extern
int
eeh_subsystem_enabled
;
/*
* The struct is used to trace EEH state for the associated
* PCI device node or PCI device. In future, it might
* represent PE as well so that the EEH device to form
* another tree except the currently existing tree of PCI
* buses and PCI devices
*/
#define EEH_MODE_SUPPORTED (1<<0)
/* EEH supported on the device */
#define EEH_MODE_NOCHECK (1<<1)
/* EEH check should be skipped */
#define EEH_MODE_ISOLATED (1<<2)
/* The device has been isolated */
#define EEH_MODE_RECOVERING (1<<3)
/* Recovering the device */
#define EEH_MODE_IRQ_DISABLED (1<<4)
/* Interrupt disabled */
struct
eeh_dev
{
int
mode
;
/* EEH mode */
int
class_code
;
/* Class code of the device */
int
config_addr
;
/* Config address */
int
pe_config_addr
;
/* PE config address */
int
check_count
;
/* Times of ignored error */
int
freeze_count
;
/* Times of froze up */
int
false_positives
;
/* Times of reported #ff's */
u32
config_space
[
16
];
/* Saved PCI config space */
struct
pci_controller
*
phb
;
/* Associated PHB */
struct
device_node
*
dn
;
/* Associated device node */
struct
pci_dev
*
pdev
;
/* Associated PCI device */
};
static
inline
struct
device_node
*
eeh_dev_to_of_node
(
struct
eeh_dev
*
edev
)
{
return
edev
->
dn
;
}
static
inline
struct
pci_dev
*
eeh_dev_to_pci_dev
(
struct
eeh_dev
*
edev
)
{
return
edev
->
pdev
;
}
/* Values for eeh_mode bits in device_node */
/*
#define EEH_MODE_SUPPORTED (1<<0)
* The struct is used to trace the registered EEH operation
#define EEH_MODE_NOCHECK (1<<1)
* callback functions. Actually, those operation callback
#define EEH_MODE_ISOLATED (1<<2)
* functions are heavily platform dependent. That means the
#define EEH_MODE_RECOVERING (1<<3)
* platform should register its own EEH operation callback
#define EEH_MODE_IRQ_DISABLED (1<<4)
* functions before any EEH further operations.
*/
#define EEH_OPT_DISABLE 0
/* EEH disable */
#define EEH_OPT_ENABLE 1
/* EEH enable */
#define EEH_OPT_THAW_MMIO 2
/* MMIO enable */
#define EEH_OPT_THAW_DMA 3
/* DMA enable */
#define EEH_STATE_UNAVAILABLE (1 << 0)
/* State unavailable */
#define EEH_STATE_NOT_SUPPORT (1 << 1)
/* EEH not supported */
#define EEH_STATE_RESET_ACTIVE (1 << 2)
/* Active reset */
#define EEH_STATE_MMIO_ACTIVE (1 << 3)
/* Active MMIO */
#define EEH_STATE_DMA_ACTIVE (1 << 4)
/* Active DMA */
#define EEH_STATE_MMIO_ENABLED (1 << 5)
/* MMIO enabled */
#define EEH_STATE_DMA_ENABLED (1 << 6)
/* DMA enabled */
#define EEH_RESET_DEACTIVATE 0
/* Deactivate the PE reset */
#define EEH_RESET_HOT 1
/* Hot reset */
#define EEH_RESET_FUNDAMENTAL 3
/* Fundamental reset */
#define EEH_LOG_TEMP 1
/* EEH temporary error log */
#define EEH_LOG_PERM 2
/* EEH permanent error log */
struct
eeh_ops
{
char
*
name
;
int
(
*
init
)(
void
);
int
(
*
set_option
)(
struct
device_node
*
dn
,
int
option
);
int
(
*
get_pe_addr
)(
struct
device_node
*
dn
);
int
(
*
get_state
)(
struct
device_node
*
dn
,
int
*
state
);
int
(
*
reset
)(
struct
device_node
*
dn
,
int
option
);
int
(
*
wait_state
)(
struct
device_node
*
dn
,
int
max_wait
);
int
(
*
get_log
)(
struct
device_node
*
dn
,
int
severity
,
char
*
drv_log
,
unsigned
long
len
);
int
(
*
configure_bridge
)(
struct
device_node
*
dn
);
int
(
*
read_config
)(
struct
device_node
*
dn
,
int
where
,
int
size
,
u32
*
val
);
int
(
*
write_config
)(
struct
device_node
*
dn
,
int
where
,
int
size
,
u32
val
);
};
extern
struct
eeh_ops
*
eeh_ops
;
extern
int
eeh_subsystem_enabled
;
/* Max number of EEH freezes allowed before we consider the device
/*
* to be permanently disabled. */
* Max number of EEH freezes allowed before we consider the device
* to be permanently disabled.
*/
#define EEH_MAX_ALLOWED_FREEZES 5
#define EEH_MAX_ALLOWED_FREEZES 5
void
*
__devinit
eeh_dev_init
(
struct
device_node
*
dn
,
void
*
data
);
void
__devinit
eeh_dev_phb_init_dynamic
(
struct
pci_controller
*
phb
);
void
__init
eeh_dev_phb_init
(
void
);
void
__init
eeh_init
(
void
);
void
__init
eeh_init
(
void
);
#ifdef CONFIG_PPC_PSERIES
int
__init
eeh_pseries_init
(
void
);
#endif
int
__init
eeh_ops_register
(
struct
eeh_ops
*
ops
);
int
__exit
eeh_ops_unregister
(
const
char
*
name
);
unsigned
long
eeh_check_failure
(
const
volatile
void
__iomem
*
token
,
unsigned
long
eeh_check_failure
(
const
volatile
void
__iomem
*
token
,
unsigned
long
val
);
unsigned
long
val
);
int
eeh_dn_check_failure
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
);
int
eeh_dn_check_failure
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
);
void
__init
pci_addr_cache_build
(
void
);
void
__init
pci_addr_cache_build
(
void
);
/**
* eeh_add_device_early
* eeh_add_device_late
*
* Perform eeh initialization for devices added after boot.
* Call eeh_add_device_early before doing any i/o to the
* device (including config space i/o). Call eeh_add_device_late
* to finish the eeh setup for this device.
*/
void
eeh_add_device_tree_early
(
struct
device_node
*
);
void
eeh_add_device_tree_early
(
struct
device_node
*
);
void
eeh_add_device_tree_late
(
struct
pci_bus
*
);
void
eeh_add_device_tree_late
(
struct
pci_bus
*
);
/**
* eeh_remove_device_recursive - undo EEH for device & children.
* @dev: pci device to be removed
*
* As above, this removes the device; it also removes child
* pci devices as well.
*/
void
eeh_remove_bus_device
(
struct
pci_dev
*
);
void
eeh_remove_bus_device
(
struct
pci_dev
*
);
/**
/**
...
@@ -87,8 +148,25 @@ void eeh_remove_bus_device(struct pci_dev *);
...
@@ -87,8 +148,25 @@ void eeh_remove_bus_device(struct pci_dev *);
#define EEH_IO_ERROR_VALUE(size) (~0U >> ((4 - (size)) * 8))
#define EEH_IO_ERROR_VALUE(size) (~0U >> ((4 - (size)) * 8))
#else
/* !CONFIG_EEH */
#else
/* !CONFIG_EEH */
static
inline
void
*
eeh_dev_init
(
struct
device_node
*
dn
,
void
*
data
)
{
return
NULL
;
}
static
inline
void
eeh_dev_phb_init_dynamic
(
struct
pci_controller
*
phb
)
{
}
static
inline
void
eeh_dev_phb_init
(
void
)
{
}
static
inline
void
eeh_init
(
void
)
{
}
static
inline
void
eeh_init
(
void
)
{
}
#ifdef CONFIG_PPC_PSERIES
static
inline
int
eeh_pseries_init
(
void
)
{
return
0
;
}
#endif
/* CONFIG_PPC_PSERIES */
static
inline
unsigned
long
eeh_check_failure
(
const
volatile
void
__iomem
*
token
,
unsigned
long
val
)
static
inline
unsigned
long
eeh_check_failure
(
const
volatile
void
__iomem
*
token
,
unsigned
long
val
)
{
{
return
val
;
return
val
;
...
...
arch/powerpc/include/asm/eeh_event.h
View file @
aba0eb84
/*
/*
* eeh_event.h
*
* This program is free software; you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation; either version 2 of the License, or
...
@@ -22,32 +20,19 @@
...
@@ -22,32 +20,19 @@
#define ASM_POWERPC_EEH_EVENT_H
#define ASM_POWERPC_EEH_EVENT_H
#ifdef __KERNEL__
#ifdef __KERNEL__
/** EEH event -- structure holding pci controller data that describes
/*
* a change in the isolation status of a PCI slot. A pointer
* structure holding pci controller data that describes a
* to this struct is passed as the data pointer in a notify callback.
* change in the isolation status of a PCI slot. A pointer
* to this struct is passed as the data pointer in a notify
* callback.
*/
*/
struct
eeh_event
{
struct
eeh_event
{
struct
list_head
list
;
struct
list_head
list
;
/* to form event queue */
struct
device_node
*
dn
;
/* struct device node */
struct
eeh_dev
*
edev
;
/* EEH device */
struct
pci_dev
*
dev
;
/* affected device */
};
};
/**
int
eeh_send_failure_event
(
struct
eeh_dev
*
edev
);
* eeh_send_failure_event - generate a PCI error event
struct
eeh_dev
*
handle_eeh_events
(
struct
eeh_event
*
);
* @dev pci device
*
* This routine builds a PCI error event which will be delivered
* to all listeners on the eeh_notifier_chain.
*
* This routine can be called within an interrupt context;
* the actual event will be delivered in a normal context
* (from a workqueue).
*/
int
eeh_send_failure_event
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
);
/* Main recovery function */
struct
pci_dn
*
handle_eeh_events
(
struct
eeh_event
*
);
#endif
/* __KERNEL__ */
#endif
/* __KERNEL__ */
#endif
/* ASM_POWERPC_EEH_EVENT_H */
#endif
/* ASM_POWERPC_EEH_EVENT_H */
arch/powerpc/include/asm/ppc-pci.h
View file @
aba0eb84
...
@@ -47,92 +47,21 @@ extern int rtas_setup_phb(struct pci_controller *phb);
...
@@ -47,92 +47,21 @@ extern int rtas_setup_phb(struct pci_controller *phb);
extern
unsigned
long
pci_probe_only
;
extern
unsigned
long
pci_probe_only
;
/* ---- EEH internal-use-only related routines ---- */
#ifdef CONFIG_EEH
#ifdef CONFIG_EEH
void
pci_addr_cache_build
(
void
);
void
pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
);
void
pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
);
void
pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
);
void
pci_addr_cache_remove_device
(
struct
pci_dev
*
dev
);
void
pci_addr_cache_build
(
void
);
struct
pci_dev
*
pci_addr_cache_get_device
(
unsigned
long
addr
);
struct
pci_dev
*
pci_get_device_by_addr
(
unsigned
long
addr
);
void
eeh_slot_error_detail
(
struct
eeh_dev
*
edev
,
int
severity
);
int
eeh_pci_enable
(
struct
eeh_dev
*
edev
,
int
function
);
/**
int
eeh_reset_pe
(
struct
eeh_dev
*
);
* eeh_slot_error_detail -- record and EEH error condition to the log
void
eeh_restore_bars
(
struct
eeh_dev
*
);
* @pdn: pci device node
* @severity: EEH_LOG_TEMP_FAILURE or EEH_LOG_PERM_FAILURE
*
* Obtains the EEH error details from the RTAS subsystem,
* and then logs these details with the RTAS error log system.
*/
#define EEH_LOG_TEMP_FAILURE 1
#define EEH_LOG_PERM_FAILURE 2
void
eeh_slot_error_detail
(
struct
pci_dn
*
pdn
,
int
severity
);
/**
* rtas_pci_enable - enable IO transfers for this slot
* @pdn: pci device node
* @function: either EEH_THAW_MMIO or EEH_THAW_DMA
*
* Enable I/O transfers to this slot
*/
#define EEH_THAW_MMIO 2
#define EEH_THAW_DMA 3
int
rtas_pci_enable
(
struct
pci_dn
*
pdn
,
int
function
);
/**
* rtas_set_slot_reset -- unfreeze a frozen slot
* @pdn: pci device node
*
* Clear the EEH-frozen condition on a slot. This routine
* does this by asserting the PCI #RST line for 1/8th of
* a second; this routine will sleep while the adapter is
* being reset.
*
* Returns a non-zero value if the reset failed.
*/
int
rtas_set_slot_reset
(
struct
pci_dn
*
);
int
eeh_wait_for_slot_status
(
struct
pci_dn
*
pdn
,
int
max_wait_msecs
);
/**
* eeh_restore_bars - Restore device configuration info.
* @pdn: pci device node
*
* A reset of a PCI device will clear out its config space.
* This routines will restore the config space for this
* device, and is children, to values previously obtained
* from the firmware.
*/
void
eeh_restore_bars
(
struct
pci_dn
*
);
/**
* rtas_configure_bridge -- firmware initialization of pci bridge
* @pdn: pci device node
*
* Ask the firmware to configure all PCI bridges devices
* located behind the indicated node. Required after a
* pci device reset. Does essentially the same hing as
* eeh_restore_bars, but for brdges, and lets firmware
* do the work.
*/
void
rtas_configure_bridge
(
struct
pci_dn
*
);
int
rtas_write_config
(
struct
pci_dn
*
,
int
where
,
int
size
,
u32
val
);
int
rtas_write_config
(
struct
pci_dn
*
,
int
where
,
int
size
,
u32
val
);
int
rtas_read_config
(
struct
pci_dn
*
,
int
where
,
int
size
,
u32
*
val
);
int
rtas_read_config
(
struct
pci_dn
*
,
int
where
,
int
size
,
u32
*
val
);
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
/**
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
* eeh_mark_slot -- set mode flags for pertition endpoint
struct
device_node
*
eeh_find_device_pe
(
struct
device_node
*
dn
);
* @pdn: pci device node
*
* mark and clear slots: find "partition endpoint" PE and set or
* clear the flags for each subnode of the PE.
*/
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
);
/**
* find_device_pe -- Find the associated "Partiationable Endpoint" PE
* @pdn: pci device node
*/
struct
device_node
*
find_device_pe
(
struct
device_node
*
dn
);
void
eeh_sysfs_add_device
(
struct
pci_dev
*
pdev
);
void
eeh_sysfs_add_device
(
struct
pci_dev
*
pdev
);
void
eeh_sysfs_remove_device
(
struct
pci_dev
*
pdev
);
void
eeh_sysfs_remove_device
(
struct
pci_dev
*
pdev
);
...
...
arch/powerpc/kernel/of_platform.c
View file @
aba0eb84
...
@@ -21,12 +21,13 @@
...
@@ -21,12 +21,13 @@
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_platform.h>
#include <linux/atomic.h>
#include <asm/errno.h>
#include <asm/errno.h>
#include <asm/topology.h>
#include <asm/topology.h>
#include <asm/pci-bridge.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
#include <asm/ppc-pci.h>
#include <
linux/atomic
.h>
#include <
asm/eeh
.h>
#ifdef CONFIG_PPC_OF_PLATFORM_PCI
#ifdef CONFIG_PPC_OF_PLATFORM_PCI
...
@@ -66,6 +67,9 @@ static int __devinit of_pci_phb_probe(struct platform_device *dev)
...
@@ -66,6 +67,9 @@ static int __devinit of_pci_phb_probe(struct platform_device *dev)
/* Init pci_dn data structures */
/* Init pci_dn data structures */
pci_devs_phb_init_dynamic
(
phb
);
pci_devs_phb_init_dynamic
(
phb
);
/* Create EEH devices for the PHB */
eeh_dev_phb_init_dynamic
(
phb
);
/* Register devices with EEH */
/* Register devices with EEH */
#ifdef CONFIG_EEH
#ifdef CONFIG_EEH
if
(
dev
->
dev
.
of_node
->
child
)
if
(
dev
->
dev
.
of_node
->
child
)
...
...
arch/powerpc/kernel/rtas_pci.c
View file @
aba0eb84
...
@@ -275,6 +275,9 @@ void __init find_and_init_phbs(void)
...
@@ -275,6 +275,9 @@ void __init find_and_init_phbs(void)
of_node_put
(
root
);
of_node_put
(
root
);
pci_devs_phb_init
();
pci_devs_phb_init
();
/* Create EEH devices for all PHBs */
eeh_dev_phb_init
();
/*
/*
* pci_probe_only and pci_assign_all_buses can be set via properties
* pci_probe_only and pci_assign_all_buses can be set via properties
* in chosen.
* in chosen.
...
...
arch/powerpc/platforms/pseries/Makefile
View file @
aba0eb84
...
@@ -6,7 +6,8 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \
...
@@ -6,7 +6,8 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \
firmware.o power.o dlpar.o mobility.o
firmware.o power.o dlpar.o mobility.o
obj-$(CONFIG_SMP)
+=
smp.o
obj-$(CONFIG_SMP)
+=
smp.o
obj-$(CONFIG_SCANLOG)
+=
scanlog.o
obj-$(CONFIG_SCANLOG)
+=
scanlog.o
obj-$(CONFIG_EEH)
+=
eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o
obj-$(CONFIG_EEH)
+=
eeh.o eeh_dev.o eeh_cache.o eeh_driver.o
\
eeh_event.o eeh_sysfs.o eeh_pseries.o
obj-$(CONFIG_KEXEC)
+=
kexec.o
obj-$(CONFIG_KEXEC)
+=
kexec.o
obj-$(CONFIG_PCI)
+=
pci.o pci_dlpar.o
obj-$(CONFIG_PCI)
+=
pci.o pci_dlpar.o
obj-$(CONFIG_PSERIES_MSI)
+=
msi.o
obj-$(CONFIG_PSERIES_MSI)
+=
msi.o
...
...
arch/powerpc/platforms/pseries/eeh.c
View file @
aba0eb84
/*
/*
* eeh.c
* Copyright IBM Corporation 2001, 2005, 2006
* Copyright IBM Corporation 2001, 2005, 2006
* Copyright Dave Engebretsen & Todd Inglett 2001
* Copyright Dave Engebretsen & Todd Inglett 2001
* Copyright Linas Vepstas 2005, 2006
* Copyright Linas Vepstas 2005, 2006
* Copyright 2001-2012 IBM Corporation.
*
*
* This program is free software; you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by
...
@@ -22,7 +22,7 @@
...
@@ -22,7 +22,7 @@
*/
*/
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/sched.h>
/* for init_mm */
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/pci.h>
...
@@ -86,16 +86,8 @@
...
@@ -86,16 +86,8 @@
/* Time to wait for a PCI slot to report status, in milliseconds */
/* Time to wait for a PCI slot to report status, in milliseconds */
#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
/* RTAS tokens */
/* Platform dependent EEH operations */
static
int
ibm_set_eeh_option
;
struct
eeh_ops
*
eeh_ops
=
NULL
;
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_bridge
;
static
int
ibm_configure_pe
;
int
eeh_subsystem_enabled
;
int
eeh_subsystem_enabled
;
EXPORT_SYMBOL
(
eeh_subsystem_enabled
);
EXPORT_SYMBOL
(
eeh_subsystem_enabled
);
...
@@ -103,14 +95,6 @@ EXPORT_SYMBOL(eeh_subsystem_enabled);
...
@@ -103,14 +95,6 @@ EXPORT_SYMBOL(eeh_subsystem_enabled);
/* Lock to avoid races due to multiple reports of an error */
/* Lock to avoid races due to multiple reports of an error */
static
DEFINE_RAW_SPINLOCK
(
confirm_error_lock
);
static
DEFINE_RAW_SPINLOCK
(
confirm_error_lock
);
/* 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
;
/* Buffer for reporting pci register dumps. Its here in BSS, and
/* Buffer for reporting pci register dumps. Its here in BSS, and
* not dynamically alloced, so that it ends up in RMO where RTAS
* not dynamically alloced, so that it ends up in RMO where RTAS
* can access it.
* can access it.
...
@@ -118,74 +102,50 @@ static int eeh_error_buf_size;
...
@@ -118,74 +102,50 @@ static int eeh_error_buf_size;
#define EEH_PCI_REGS_LOG_LEN 4096
#define EEH_PCI_REGS_LOG_LEN 4096
static
unsigned
char
pci_regs_buf
[
EEH_PCI_REGS_LOG_LEN
];
static
unsigned
char
pci_regs_buf
[
EEH_PCI_REGS_LOG_LEN
];
/* System monitoring statistics */
/*
static
unsigned
long
no_device
;
* The struct is used to maintain the EEH global statistic
static
unsigned
long
no_dn
;
* information. Besides, the EEH global statistics will be
static
unsigned
long
no_cfg_addr
;
* exported to user space through procfs
static
unsigned
long
ignored_check
;
*/
static
unsigned
long
total_mmio_ffs
;
struct
eeh_stats
{
static
unsigned
long
false_positives
;
u64
no_device
;
/* PCI device not found */
static
unsigned
long
slot_resets
;
u64
no_dn
;
/* OF node not found */
u64
no_cfg_addr
;
/* Config address not found */
#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
u64
ignored_check
;
/* EEH check skipped */
u64
total_mmio_ffs
;
/* Total EEH checks */
/* --------------------------------------------------------------- */
u64
false_positives
;
/* Unnecessary EEH checks */
/* Below lies the EEH event infrastructure */
u64
slot_resets
;
/* PE reset */
};
static
void
rtas_slot_error_detail
(
struct
pci_dn
*
pdn
,
int
severity
,
static
struct
eeh_stats
eeh_stats
;
char
*
driver_log
,
size_t
loglen
)
{
int
config_addr
;
unsigned
long
flags
;
int
rc
;
/* Log the error with the rtas logger */
#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
spin_lock_irqsave
(
&
slot_errbuf_lock
,
flags
);
memset
(
slot_errbuf
,
0
,
eeh_error_buf_size
);
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
rc
=
rtas_call
(
ibm_slot_error_detail
,
8
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
),
virt_to_phys
(
driver_log
),
loglen
,
virt_to_phys
(
slot_errbuf
),
eeh_error_buf_size
,
severity
);
if
(
rc
==
0
)
log_error
(
slot_errbuf
,
ERR_TYPE_RTAS_LOG
,
0
);
spin_unlock_irqrestore
(
&
slot_errbuf_lock
,
flags
);
}
/**
/**
*
gather_pci_data - c
opy assorted PCI config space registers to buff
*
eeh_gather_pci_data - C
opy assorted PCI config space registers to buff
* @
pdn
: device to report data for
* @
edev
: device to report data for
* @buf: point to buffer in which to log
* @buf: point to buffer in which to log
* @len: amount of room in buffer
* @len: amount of room in buffer
*
*
* This routine captures assorted PCI configuration space data,
* This routine captures assorted PCI configuration space data,
* and puts them into a buffer for RTAS error logging.
* and puts them into a buffer for RTAS error logging.
*/
*/
static
size_t
gather_pci_data
(
struct
pci_dn
*
pdn
,
char
*
buf
,
size_t
len
)
static
size_t
eeh_gather_pci_data
(
struct
eeh_dev
*
edev
,
char
*
buf
,
size_t
len
)
{
{
struct
pci_dev
*
dev
=
pdn
->
pcidev
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
struct
pci_dev
*
dev
=
eeh_dev_to_pci_dev
(
edev
);
u32
cfg
;
u32
cfg
;
int
cap
,
i
;
int
cap
,
i
;
int
n
=
0
;
int
n
=
0
;
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"%s
\n
"
,
pdn
->
node
->
full_name
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"%s
\n
"
,
dn
->
full_name
);
printk
(
KERN_WARNING
"EEH: of node=%s
\n
"
,
pdn
->
node
->
full_name
);
printk
(
KERN_WARNING
"EEH: of node=%s
\n
"
,
dn
->
full_name
);
rtas_read_config
(
p
dn
,
PCI_VENDOR_ID
,
4
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
PCI_VENDOR_ID
,
4
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"dev/vend:%08x
\n
"
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"dev/vend:%08x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI device/vendor: %08x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI device/vendor: %08x
\n
"
,
cfg
);
rtas_read_config
(
p
dn
,
PCI_COMMAND
,
4
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
PCI_COMMAND
,
4
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"cmd/stat:%x
\n
"
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"cmd/stat:%x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI cmd/status register: %08x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI cmd/status register: %08x
\n
"
,
cfg
);
...
@@ -196,11 +156,11 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
...
@@ -196,11 +156,11 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/* Gather bridge-specific registers */
/* Gather bridge-specific registers */
if
(
dev
->
class
>>
16
==
PCI_BASE_CLASS_BRIDGE
)
{
if
(
dev
->
class
>>
16
==
PCI_BASE_CLASS_BRIDGE
)
{
rtas_read_config
(
p
dn
,
PCI_SEC_STATUS
,
2
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
PCI_SEC_STATUS
,
2
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"sec stat:%x
\n
"
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"sec stat:%x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: Bridge secondary status: %04x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: Bridge secondary status: %04x
\n
"
,
cfg
);
rtas_read_config
(
p
dn
,
PCI_BRIDGE_CONTROL
,
2
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
PCI_BRIDGE_CONTROL
,
2
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"brdg ctl:%x
\n
"
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"brdg ctl:%x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: Bridge control: %04x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: Bridge control: %04x
\n
"
,
cfg
);
}
}
...
@@ -208,11 +168,11 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
...
@@ -208,11 +168,11 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/* Dump out the PCI-X command and status regs */
/* Dump out the PCI-X command and status regs */
cap
=
pci_find_capability
(
dev
,
PCI_CAP_ID_PCIX
);
cap
=
pci_find_capability
(
dev
,
PCI_CAP_ID_PCIX
);
if
(
cap
)
{
if
(
cap
)
{
rtas_read_config
(
p
dn
,
cap
,
4
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
cap
,
4
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"pcix-cmd:%x
\n
"
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"pcix-cmd:%x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-X cmd: %08x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-X cmd: %08x
\n
"
,
cfg
);
rtas_read_config
(
p
dn
,
cap
+
4
,
4
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
cap
+
4
,
4
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"pcix-stat:%x
\n
"
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"pcix-stat:%x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-X status: %08x
\n
"
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-X status: %08x
\n
"
,
cfg
);
}
}
...
@@ -225,7 +185,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
...
@@ -225,7 +185,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
"EEH: PCI-E capabilities and status follow:
\n
"
);
"EEH: PCI-E capabilities and status follow:
\n
"
);
for
(
i
=
0
;
i
<=
8
;
i
++
)
{
for
(
i
=
0
;
i
<=
8
;
i
++
)
{
rtas_read_config
(
p
dn
,
cap
+
4
*
i
,
4
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
cap
+
4
*
i
,
4
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"%02x:%x
\n
"
,
4
*
i
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"%02x:%x
\n
"
,
4
*
i
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-E %02x: %08x
\n
"
,
i
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-E %02x: %08x
\n
"
,
i
,
cfg
);
}
}
...
@@ -237,7 +197,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
...
@@ -237,7 +197,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
"EEH: PCI-E AER capability register set follows:
\n
"
);
"EEH: PCI-E AER capability register set follows:
\n
"
);
for
(
i
=
0
;
i
<
14
;
i
++
)
{
for
(
i
=
0
;
i
<
14
;
i
++
)
{
rtas_read_config
(
p
dn
,
cap
+
4
*
i
,
4
,
&
cfg
);
eeh_ops
->
read_config
(
dn
,
cap
+
4
*
i
,
4
,
&
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"%02x:%x
\n
"
,
4
*
i
,
cfg
);
n
+=
scnprintf
(
buf
+
n
,
len
-
n
,
"%02x:%x
\n
"
,
4
*
i
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-E AER %02x: %08x
\n
"
,
i
,
cfg
);
printk
(
KERN_WARNING
"EEH: PCI-E AER %02x: %08x
\n
"
,
i
,
cfg
);
}
}
...
@@ -246,111 +206,46 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
...
@@ -246,111 +206,46 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
/* Gather status on devices under the bridge */
/* Gather status on devices under the bridge */
if
(
dev
->
class
>>
16
==
PCI_BASE_CLASS_BRIDGE
)
{
if
(
dev
->
class
>>
16
==
PCI_BASE_CLASS_BRIDGE
)
{
struct
device_node
*
dn
;
struct
device_node
*
child
;
for_each_child_of_node
(
pdn
->
node
,
dn
)
{
for_each_child_of_node
(
dn
,
child
)
{
pdn
=
PCI_DN
(
dn
);
if
(
of_node_to_eeh_dev
(
child
))
if
(
pdn
)
n
+=
eeh_gather_pci_data
(
of_node_to_eeh_dev
(
child
),
buf
+
n
,
len
-
n
);
n
+=
gather_pci_data
(
pdn
,
buf
+
n
,
len
-
n
);
}
}
}
}
return
n
;
return
n
;
}
}
void
eeh_slot_error_detail
(
struct
pci_dn
*
pdn
,
int
severity
)
{
size_t
loglen
=
0
;
pci_regs_buf
[
0
]
=
0
;
rtas_pci_enable
(
pdn
,
EEH_THAW_MMIO
);
rtas_configure_bridge
(
pdn
);
eeh_restore_bars
(
pdn
);
loglen
=
gather_pci_data
(
pdn
,
pci_regs_buf
,
EEH_PCI_REGS_LOG_LEN
);
rtas_slot_error_detail
(
pdn
,
severity
,
pci_regs_buf
,
loglen
);
}
/**
/**
* read_slot_reset_state - Read the reset state of a device node's slot
* eeh_slot_error_detail - Generate combined log including driver log and error log
* @dn: device node to read
* @edev: device to report error log for
* @rets: array to return results in
* @severity: temporary or permanent error log
*/
static
int
read_slot_reset_state
(
struct
pci_dn
*
pdn
,
int
rets
[])
{
int
token
,
outputs
;
int
config_addr
;
if
(
ibm_read_slot_reset_state2
!=
RTAS_UNKNOWN_SERVICE
)
{
token
=
ibm_read_slot_reset_state2
;
outputs
=
4
;
}
else
{
token
=
ibm_read_slot_reset_state
;
rets
[
2
]
=
0
;
/* fake PE Unavailable info */
outputs
=
3
;
}
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
return
rtas_call
(
token
,
3
,
outputs
,
rets
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
));
}
/**
* eeh_wait_for_slot_status - returns error status of slot
* @pdn pci device node
* @max_wait_msecs maximum number to millisecs to wait
*
* Return negative value if a permanent error, else return
* Partition Endpoint (PE) status value.
*
*
*
If @max_wait_msecs is positive, then this routine will
*
This routine should be called to generate the combined log, which
*
sleep until a valid status can be obtained, or until
*
is comprised of driver log and error log. The driver log is figured
*
the max allowed wait time is exceeded, in which cas
e
*
out from the config space of the corresponding PCI device, whil
e
*
a -2 is returned
.
*
the error log is fetched through platform dependent function call
.
*/
*/
int
void
eeh_slot_error_detail
(
struct
eeh_dev
*
edev
,
int
severity
)
eeh_wait_for_slot_status
(
struct
pci_dn
*
pdn
,
int
max_wait_msecs
)
{
{
int
rc
;
size_t
loglen
=
0
;
int
rets
[
3
];
pci_regs_buf
[
0
]
=
0
;
int
mwait
;
while
(
1
)
{
rc
=
read_slot_reset_state
(
pdn
,
rets
);
if
(
rc
)
return
rc
;
if
(
rets
[
1
]
==
0
)
return
-
1
;
/* EEH is not supported */
if
(
rets
[
0
]
!=
5
)
return
rets
[
0
];
/* return actual status */
if
(
rets
[
2
]
==
0
)
return
-
1
;
/* permanently unavailable */
if
(
max_wait_msecs
<=
0
)
break
;
eeh_pci_enable
(
edev
,
EEH_OPT_THAW_MMIO
);
eeh_ops
->
configure_bridge
(
eeh_dev_to_of_node
(
edev
));
eeh_restore_bars
(
edev
);
loglen
=
eeh_gather_pci_data
(
edev
,
pci_regs_buf
,
EEH_PCI_REGS_LOG_LEN
);
mwait
=
rets
[
2
];
eeh_ops
->
get_log
(
eeh_dev_to_of_node
(
edev
),
severity
,
pci_regs_buf
,
loglen
);
if
(
mwait
<=
0
)
{
printk
(
KERN_WARNING
"EEH: Firmware returned bad wait value=%d
\n
"
,
mwait
);
mwait
=
1000
;
}
else
if
(
mwait
>
300
*
1000
)
{
printk
(
KERN_WARNING
"EEH: Firmware is taking too long, time=%d
\n
"
,
mwait
);
mwait
=
300
*
1000
;
}
max_wait_msecs
-=
mwait
;
msleep
(
mwait
);
}
printk
(
KERN_WARNING
"EEH: Timed out waiting for slot status
\n
"
);
return
-
2
;
}
}
/**
/**
* eeh_token_to_phys - convert EEH address token to phys address
* eeh_token_to_phys - Convert EEH address token to phys address
* @token i/o token, should be address in the form 0xA....
* @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.
*/
*/
static
inline
unsigned
long
eeh_token_to_phys
(
unsigned
long
token
)
static
inline
unsigned
long
eeh_token_to_phys
(
unsigned
long
token
)
{
{
...
@@ -365,36 +260,43 @@ static inline unsigned long eeh_token_to_phys(unsigned long token)
...
@@ -365,36 +260,43 @@ static inline unsigned long eeh_token_to_phys(unsigned long token)
return
pa
|
(
token
&
(
PAGE_SIZE
-
1
));
return
pa
|
(
token
&
(
PAGE_SIZE
-
1
));
}
}
/**
/**
* Return the "partitionable endpoint" (pe) under which this device lies
* eeh_find_device_pe - Retrieve the PE for the given device
* @dn: device node
*
* Return the PE under which this device lies
*/
*/
struct
device_node
*
find_device_pe
(
struct
device_node
*
dn
)
struct
device_node
*
eeh_
find_device_pe
(
struct
device_node
*
dn
)
{
{
while
(
(
dn
->
parent
)
&&
PCI_DN
(
dn
->
parent
)
&&
while
(
dn
->
parent
&&
of_node_to_eeh_dev
(
dn
->
parent
)
&&
(
PCI_DN
(
dn
->
parent
)
->
eeh_
mode
&
EEH_MODE_SUPPORTED
))
{
(
of_node_to_eeh_dev
(
dn
->
parent
)
->
mode
&
EEH_MODE_SUPPORTED
))
{
dn
=
dn
->
parent
;
dn
=
dn
->
parent
;
}
}
return
dn
;
return
dn
;
}
}
/** Mark all devices that are children of this device as failed.
/**
* Mark the device driver too, so that it can see the failure
* __eeh_mark_slot - Mark all child devices as failed
* immediately; this is critical, since some drivers poll
* @parent: parent device
* status registers in interrupts ... If a driver is polling,
* @mode_flag: failure flag
* and the slot is frozen, then the driver can deadlock in
*
* an interrupt context, which is bad.
* Mark all devices that are children of this device as failed.
* Mark the device driver too, so that it can see the failure
* immediately; this is critical, since some drivers poll
* status registers in interrupts ... If a driver is polling,
* and the slot is frozen, then the driver can deadlock in
* an interrupt context, which is bad.
*/
*/
static
void
__eeh_mark_slot
(
struct
device_node
*
parent
,
int
mode_flag
)
static
void
__eeh_mark_slot
(
struct
device_node
*
parent
,
int
mode_flag
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
for_each_child_of_node
(
parent
,
dn
)
{
for_each_child_of_node
(
parent
,
dn
)
{
if
(
PCI_DN
(
dn
))
{
if
(
of_node_to_eeh_dev
(
dn
))
{
/* Mark the pci device driver too */
/* Mark the pci device driver too */
struct
pci_dev
*
dev
=
PCI_DN
(
dn
)
->
pci
dev
;
struct
pci_dev
*
dev
=
of_node_to_eeh_dev
(
dn
)
->
p
dev
;
PCI_DN
(
dn
)
->
eeh_
mode
|=
mode_flag
;
of_node_to_eeh_dev
(
dn
)
->
mode
|=
mode_flag
;
if
(
dev
&&
dev
->
driver
)
if
(
dev
&&
dev
->
driver
)
dev
->
error_state
=
pci_channel_io_frozen
;
dev
->
error_state
=
pci_channel_io_frozen
;
...
@@ -404,92 +306,81 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag)
...
@@ -404,92 +306,81 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag)
}
}
}
}
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
)
/**
* eeh_mark_slot - Mark the indicated device and its children as failed
* @dn: parent device
* @mode_flag: failure flag
*
* Mark the indicated device and its child devices as failed.
* The device drivers are marked as failed as well.
*/
void
eeh_mark_slot
(
struct
device_node
*
dn
,
int
mode_flag
)
{
{
struct
pci_dev
*
dev
;
struct
pci_dev
*
dev
;
dn
=
find_device_pe
(
dn
);
dn
=
eeh_find_device_pe
(
dn
);
/* Back up one, since config addrs might be shared */
/* Back up one, since config addrs might be shared */
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
PCI_DN
(
dn
->
parent
))
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
of_node_to_eeh_dev
(
dn
->
parent
))
dn
=
dn
->
parent
;
dn
=
dn
->
parent
;
PCI_DN
(
dn
)
->
eeh_
mode
|=
mode_flag
;
of_node_to_eeh_dev
(
dn
)
->
mode
|=
mode_flag
;
/* Mark the pci device too */
/* Mark the pci device too */
dev
=
PCI_DN
(
dn
)
->
pci
dev
;
dev
=
of_node_to_eeh_dev
(
dn
)
->
p
dev
;
if
(
dev
)
if
(
dev
)
dev
->
error_state
=
pci_channel_io_frozen
;
dev
->
error_state
=
pci_channel_io_frozen
;
__eeh_mark_slot
(
dn
,
mode_flag
);
__eeh_mark_slot
(
dn
,
mode_flag
);
}
}
/**
* __eeh_clear_slot - Clear failure flag for the child devices
* @parent: parent device
* @mode_flag: flag to be cleared
*
* Clear failure flag for the child devices.
*/
static
void
__eeh_clear_slot
(
struct
device_node
*
parent
,
int
mode_flag
)
static
void
__eeh_clear_slot
(
struct
device_node
*
parent
,
int
mode_flag
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
for_each_child_of_node
(
parent
,
dn
)
{
for_each_child_of_node
(
parent
,
dn
)
{
if
(
PCI_DN
(
dn
))
{
if
(
of_node_to_eeh_dev
(
dn
))
{
PCI_DN
(
dn
)
->
eeh_
mode
&=
~
mode_flag
;
of_node_to_eeh_dev
(
dn
)
->
mode
&=
~
mode_flag
;
PCI_DN
(
dn
)
->
eeh_
check_count
=
0
;
of_node_to_eeh_dev
(
dn
)
->
check_count
=
0
;
__eeh_clear_slot
(
dn
,
mode_flag
);
__eeh_clear_slot
(
dn
,
mode_flag
);
}
}
}
}
}
}
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
)
/**
* eeh_clear_slot - Clear failure flag for the indicated device and its children
* @dn: parent device
* @mode_flag: flag to be cleared
*
* Clear failure flag for the indicated device and its children.
*/
void
eeh_clear_slot
(
struct
device_node
*
dn
,
int
mode_flag
)
{
{
unsigned
long
flags
;
unsigned
long
flags
;
raw_spin_lock_irqsave
(
&
confirm_error_lock
,
flags
);
raw_spin_lock_irqsave
(
&
confirm_error_lock
,
flags
);
dn
=
find_device_pe
(
dn
);
dn
=
eeh_find_device_pe
(
dn
);
/* Back up one, since config addrs might be shared */
/* Back up one, since config addrs might be shared */
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
PCI_DN
(
dn
->
parent
))
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
of_node_to_eeh_dev
(
dn
->
parent
))
dn
=
dn
->
parent
;
dn
=
dn
->
parent
;
PCI_DN
(
dn
)
->
eeh_
mode
&=
~
mode_flag
;
of_node_to_eeh_dev
(
dn
)
->
mode
&=
~
mode_flag
;
PCI_DN
(
dn
)
->
eeh_
check_count
=
0
;
of_node_to_eeh_dev
(
dn
)
->
check_count
=
0
;
__eeh_clear_slot
(
dn
,
mode_flag
);
__eeh_clear_slot
(
dn
,
mode_flag
);
raw_spin_unlock_irqrestore
(
&
confirm_error_lock
,
flags
);
raw_spin_unlock_irqrestore
(
&
confirm_error_lock
,
flags
);
}
}
void
__eeh_set_pe_freset
(
struct
device_node
*
parent
,
unsigned
int
*
freset
)
{
struct
device_node
*
dn
;
for_each_child_of_node
(
parent
,
dn
)
{
if
(
PCI_DN
(
dn
))
{
struct
pci_dev
*
dev
=
PCI_DN
(
dn
)
->
pcidev
;
if
(
dev
&&
dev
->
driver
)
*
freset
|=
dev
->
needs_freset
;
__eeh_set_pe_freset
(
dn
,
freset
);
}
}
}
void
eeh_set_pe_freset
(
struct
device_node
*
dn
,
unsigned
int
*
freset
)
{
struct
pci_dev
*
dev
;
dn
=
find_device_pe
(
dn
);
/* Back up one, since config addrs might be shared */
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
PCI_DN
(
dn
->
parent
))
dn
=
dn
->
parent
;
dev
=
PCI_DN
(
dn
)
->
pcidev
;
if
(
dev
)
*
freset
|=
dev
->
needs_freset
;
__eeh_set_pe_freset
(
dn
,
freset
);
}
/**
/**
* eeh_dn_check_failure -
c
heck if all 1's data is due to EEH slot freeze
* eeh_dn_check_failure -
C
heck if all 1's data is due to EEH slot freeze
* @dn device node
* @dn
:
device node
* @dev pci device, if known
* @dev
:
pci device, if known
*
*
* Check for an EEH failure for the given device node. Call this
* 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
* routine if the result of a read was all 0xff's and you want to
...
@@ -504,35 +395,34 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
...
@@ -504,35 +395,34 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset)
int
eeh_dn_check_failure
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
)
int
eeh_dn_check_failure
(
struct
device_node
*
dn
,
struct
pci_dev
*
dev
)
{
{
int
ret
;
int
ret
;
int
rets
[
3
];
unsigned
long
flags
;
unsigned
long
flags
;
struct
pci_dn
*
pdn
;
struct
eeh_dev
*
edev
;
int
rc
=
0
;
int
rc
=
0
;
const
char
*
location
;
const
char
*
location
;
total_mmio_ffs
++
;
eeh_stats
.
total_mmio_ffs
++
;
if
(
!
eeh_subsystem_enabled
)
if
(
!
eeh_subsystem_enabled
)
return
0
;
return
0
;
if
(
!
dn
)
{
if
(
!
dn
)
{
no_dn
++
;
eeh_stats
.
no_dn
++
;
return
0
;
return
0
;
}
}
dn
=
find_device_pe
(
dn
);
dn
=
eeh_
find_device_pe
(
dn
);
pdn
=
PCI_DN
(
dn
);
edev
=
of_node_to_eeh_dev
(
dn
);
/* Access to IO BARs might get this far and still not want checking. */
/* Access to IO BARs might get this far and still not want checking. */
if
(
!
(
pdn
->
eeh_
mode
&
EEH_MODE_SUPPORTED
)
||
if
(
!
(
edev
->
mode
&
EEH_MODE_SUPPORTED
)
||
pdn
->
eeh_
mode
&
EEH_MODE_NOCHECK
)
{
edev
->
mode
&
EEH_MODE_NOCHECK
)
{
ignored_check
++
;
eeh_stats
.
ignored_check
++
;
pr_debug
(
"EEH: Ignored check (%x) for %s %s
\n
"
,
pr_debug
(
"EEH: Ignored check (%x) for %s %s
\n
"
,
pdn
->
eeh_
mode
,
eeh_pci_name
(
dev
),
dn
->
full_name
);
edev
->
mode
,
eeh_pci_name
(
dev
),
dn
->
full_name
);
return
0
;
return
0
;
}
}
if
(
!
pdn
->
eeh_config_addr
&&
!
pdn
->
eeh_
pe_config_addr
)
{
if
(
!
edev
->
config_addr
&&
!
edev
->
pe_config_addr
)
{
no_cfg_addr
++
;
eeh_stats
.
no_cfg_addr
++
;
return
0
;
return
0
;
}
}
...
@@ -544,15 +434,15 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
...
@@ -544,15 +434,15 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
*/
*/
raw_spin_lock_irqsave
(
&
confirm_error_lock
,
flags
);
raw_spin_lock_irqsave
(
&
confirm_error_lock
,
flags
);
rc
=
1
;
rc
=
1
;
if
(
pdn
->
eeh_
mode
&
EEH_MODE_ISOLATED
)
{
if
(
edev
->
mode
&
EEH_MODE_ISOLATED
)
{
pdn
->
eeh_check_count
++
;
edev
->
check_count
++
;
if
(
pdn
->
eeh_
check_count
%
EEH_MAX_FAILS
==
0
)
{
if
(
edev
->
check_count
%
EEH_MAX_FAILS
==
0
)
{
location
=
of_get_property
(
dn
,
"ibm,loc-code"
,
NULL
);
location
=
of_get_property
(
dn
,
"ibm,loc-code"
,
NULL
);
printk
(
KERN_ERR
"EEH: %d reads ignored for recovering device at "
printk
(
KERN_ERR
"EEH: %d reads ignored for recovering device at "
"location=%s driver=%s pci addr=%s
\n
"
,
"location=%s driver=%s pci addr=%s
\n
"
,
pdn
->
eeh_
check_count
,
location
,
edev
->
check_count
,
location
,
eeh_driver_name
(
dev
),
eeh_pci_name
(
dev
));
eeh_driver_name
(
dev
),
eeh_pci_name
(
dev
));
printk
(
KERN_ERR
"EEH: Might be infinite loop in %s driver
\n
"
,
printk
(
KERN_ERR
"EEH: Might be infinite loop in %s driver
\n
"
,
eeh_driver_name
(
dev
));
eeh_driver_name
(
dev
));
dump_stack
();
dump_stack
();
}
}
...
@@ -566,58 +456,39 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
...
@@ -566,58 +456,39 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
* function zero of a multi-function device.
* function zero of a multi-function device.
* In any case they must share a common PHB.
* In any case they must share a common PHB.
*/
*/
ret
=
read_slot_reset_state
(
pdn
,
rets
);
ret
=
eeh_ops
->
get_state
(
dn
,
NULL
);
/* If the call to firmware failed, punt */
if
(
ret
!=
0
)
{
printk
(
KERN_WARNING
"EEH: read_slot_reset_state() failed; rc=%d dn=%s
\n
"
,
ret
,
dn
->
full_name
);
false_positives
++
;
pdn
->
eeh_false_positives
++
;
rc
=
0
;
goto
dn_unlock
;
}
/* Note that config-io to empty slots may fail;
/* Note that config-io to empty slots may fail;
* they are empty when they don't have children. */
* they are empty when they don't have children.
if
((
rets
[
0
]
==
5
)
&&
(
rets
[
2
]
==
0
)
&&
(
dn
->
child
==
NULL
))
{
* We will punt with the following conditions: Failure to get
false_positives
++
;
* PE's state, EEH not support and Permanently unavailable
pdn
->
eeh_false_positives
++
;
* state, PE is in good state.
rc
=
0
;
*/
goto
dn_unlock
;
if
((
ret
<
0
)
||
}
(
ret
==
EEH_STATE_NOT_SUPPORT
)
||
(
ret
&
(
EEH_STATE_MMIO_ACTIVE
|
EEH_STATE_DMA_ACTIVE
))
==
/* If EEH is not supported on this device, punt. */
(
EEH_STATE_MMIO_ACTIVE
|
EEH_STATE_DMA_ACTIVE
))
{
if
(
rets
[
1
]
!=
1
)
{
eeh_stats
.
false_positives
++
;
printk
(
KERN_WARNING
"EEH: event on unsupported device, rc=%d dn=%s
\n
"
,
edev
->
false_positives
++
;
ret
,
dn
->
full_name
);
false_positives
++
;
pdn
->
eeh_false_positives
++
;
rc
=
0
;
goto
dn_unlock
;
}
/* If not the kind of error we know about, punt. */
if
(
rets
[
0
]
!=
1
&&
rets
[
0
]
!=
2
&&
rets
[
0
]
!=
4
&&
rets
[
0
]
!=
5
)
{
false_positives
++
;
pdn
->
eeh_false_positives
++
;
rc
=
0
;
rc
=
0
;
goto
dn_unlock
;
goto
dn_unlock
;
}
}
slot_resets
++
;
eeh_stats
.
slot_resets
++
;
/* Avoid repeated reports of this failure, including problems
/* Avoid repeated reports of this failure, including problems
* with other functions on this device, and functions under
* with other functions on this device, and functions under
* bridges. */
* bridges.
eeh_mark_slot
(
dn
,
EEH_MODE_ISOLATED
);
*/
eeh_mark_slot
(
dn
,
EEH_MODE_ISOLATED
);
raw_spin_unlock_irqrestore
(
&
confirm_error_lock
,
flags
);
raw_spin_unlock_irqrestore
(
&
confirm_error_lock
,
flags
);
eeh_send_failure_event
(
dn
,
dev
);
eeh_send_failure_event
(
e
dev
);
/* Most EEH events are due to device driver bugs. Having
/* Most EEH events are due to device driver bugs. Having
* a stack trace will help the device-driver authors figure
* a stack trace will help the device-driver authors figure
* out what happened. So print that out. */
* out what happened. So print that out.
*/
dump_stack
();
dump_stack
();
return
1
;
return
1
;
...
@@ -629,9 +500,9 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
...
@@ -629,9 +500,9 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
EXPORT_SYMBOL_GPL
(
eeh_dn_check_failure
);
EXPORT_SYMBOL_GPL
(
eeh_dn_check_failure
);
/**
/**
* eeh_check_failure -
c
heck if all 1's data is due to EEH slot freeze
* eeh_check_failure -
C
heck if all 1's data is due to EEH slot freeze
* @token
i/o
token, should be address in the form 0xA....
* @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??)
* @val
:
value, should be all 1's (XXX why do we need this arg??)
*
*
* Check for an EEH failure at the given token address. Call this
* 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
* routine if the result of a read was all 0xff's and you want to
...
@@ -648,14 +519,14 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
...
@@ -648,14 +519,14 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
/* Finding the phys addr + pci device; this is pretty quick. */
/* Finding the phys addr + pci device; this is pretty quick. */
addr
=
eeh_token_to_phys
((
unsigned
long
__force
)
token
);
addr
=
eeh_token_to_phys
((
unsigned
long
__force
)
token
);
dev
=
pci_
get_device_by_addr
(
addr
);
dev
=
pci_
addr_cache_get_device
(
addr
);
if
(
!
dev
)
{
if
(
!
dev
)
{
no_device
++
;
eeh_stats
.
no_device
++
;
return
val
;
return
val
;
}
}
dn
=
pci_device_to_OF_node
(
dev
);
dn
=
pci_device_to_OF_node
(
dev
);
eeh_dn_check_failure
(
dn
,
dev
);
eeh_dn_check_failure
(
dn
,
dev
);
pci_dev_put
(
dev
);
pci_dev_put
(
dev
);
return
val
;
return
val
;
...
@@ -663,115 +534,54 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
...
@@ -663,115 +534,54 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
EXPORT_SYMBOL
(
eeh_check_failure
);
EXPORT_SYMBOL
(
eeh_check_failure
);
/* ------------------------------------------------------------- */
/* The code below deals with error recovery */
/**
/**
* rtas_pci_enable - enable MMIO or DMA transfers for this slot
* eeh_pci_enable - Enable MMIO or DMA transfers for this slot
* @pdn pci device node
* @edev: pci device node
*
* 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.
*/
*/
int
eeh_pci_enable
(
struct
eeh_dev
*
edev
,
int
function
)
int
rtas_pci_enable
(
struct
pci_dn
*
pdn
,
int
function
)
{
{
int
config_addr
;
int
rc
;
int
rc
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
/* Use PE configuration address, if present */
rc
=
eeh_ops
->
set_option
(
dn
,
function
);
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
rc
=
rtas_call
(
ibm_set_eeh_option
,
4
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
),
function
);
if
(
rc
)
if
(
rc
)
printk
(
KERN_WARNING
"EEH: Unexpected state change %d, err=%d dn=%s
\n
"
,
printk
(
KERN_WARNING
"EEH: Unexpected state change %d, err=%d dn=%s
\n
"
,
function
,
rc
,
pdn
->
node
->
full_name
);
function
,
rc
,
dn
->
full_name
);
rc
=
eeh_wait_for_slot_status
(
pdn
,
PCI_BUS_RESET_WAIT_MSEC
);
rc
=
eeh_ops
->
wait_state
(
dn
,
PCI_BUS_RESET_WAIT_MSEC
);
if
((
rc
==
4
)
&&
(
function
==
EEH_THAW_MMIO
))
if
(
rc
>
0
&&
(
rc
&
EEH_STATE_MMIO_ENABLED
)
&&
(
function
==
EEH_OPT_THAW_MMIO
))
return
0
;
return
0
;
return
rc
;
return
rc
;
}
}
/**
* rtas_pci_slot_reset - raises/lowers the pci #RST line
* @pdn pci device node
* @state: 1/0 to raise/lower the #RST
*
* Clear the EEH-frozen condition on a slot. This routine
* asserts the PCI #RST line if the 'state' argument is '1',
* and drops the #RST line if 'state is '0'. This routine is
* safe to call in an interrupt context.
*
*/
static
void
rtas_pci_slot_reset
(
struct
pci_dn
*
pdn
,
int
state
)
{
int
config_addr
;
int
rc
;
BUG_ON
(
pdn
==
NULL
);
if
(
!
pdn
->
phb
)
{
printk
(
KERN_WARNING
"EEH: in slot reset, device node %s has no phb
\n
"
,
pdn
->
node
->
full_name
);
return
;
}
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
rc
=
rtas_call
(
ibm_set_slot_reset
,
4
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
),
state
);
/* Fundamental-reset not supported on this PE, try hot-reset */
if
(
rc
==
-
8
&&
state
==
3
)
{
rc
=
rtas_call
(
ibm_set_slot_reset
,
4
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
),
1
);
if
(
rc
)
printk
(
KERN_WARNING
"EEH: Unable to reset the failed slot,"
" #RST=%d dn=%s
\n
"
,
rc
,
pdn
->
node
->
full_name
);
}
}
/**
/**
* pcibios_set_pcie_slot_reset - Set PCI-E reset state
* pcibios_set_pcie_slot_reset - Set PCI-E reset state
* @dev:
pci device struct
* @dev:
pci device struct
* @state:
reset state to enter
* @state:
reset state to enter
*
*
* Return value:
* Return value:
* 0 if success
* 0 if success
*
*
/
*/
int
pcibios_set_pcie_reset_state
(
struct
pci_dev
*
dev
,
enum
pcie_reset_state
state
)
int
pcibios_set_pcie_reset_state
(
struct
pci_dev
*
dev
,
enum
pcie_reset_state
state
)
{
{
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
struct
pci_dn
*
pdn
=
PCI_DN
(
dn
);
switch
(
state
)
{
switch
(
state
)
{
case
pcie_deassert_reset
:
case
pcie_deassert_reset
:
rtas_pci_slot_reset
(
pdn
,
0
);
eeh_ops
->
reset
(
dn
,
EEH_RESET_DEACTIVATE
);
break
;
break
;
case
pcie_hot_reset
:
case
pcie_hot_reset
:
rtas_pci_slot_reset
(
pdn
,
1
);
eeh_ops
->
reset
(
dn
,
EEH_RESET_HOT
);
break
;
break
;
case
pcie_warm_reset
:
case
pcie_warm_reset
:
rtas_pci_slot_reset
(
pdn
,
3
);
eeh_ops
->
reset
(
dn
,
EEH_RESET_FUNDAMENTAL
);
break
;
break
;
default:
default:
return
-
EINVAL
;
return
-
EINVAL
;
...
@@ -781,13 +591,66 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
...
@@ -781,13 +591,66 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat
}
}
/**
/**
* rtas_set_slot_reset -- assert the pci #RST line for 1/4 second
* __eeh_set_pe_freset - Check the required reset for child devices
* @pdn: pci device node to be reset.
* @parent: parent device
* @freset: return value
*
* Each device might have its preferred reset type: fundamental or
* hot reset. The routine is used to collect the information from
* the child devices so that they could be reset accordingly.
*/
void
__eeh_set_pe_freset
(
struct
device_node
*
parent
,
unsigned
int
*
freset
)
{
struct
device_node
*
dn
;
for_each_child_of_node
(
parent
,
dn
)
{
if
(
of_node_to_eeh_dev
(
dn
))
{
struct
pci_dev
*
dev
=
of_node_to_eeh_dev
(
dn
)
->
pdev
;
if
(
dev
&&
dev
->
driver
)
*
freset
|=
dev
->
needs_freset
;
__eeh_set_pe_freset
(
dn
,
freset
);
}
}
}
/**
* eeh_set_pe_freset - Check the required reset for the indicated device and its children
* @dn: parent device
* @freset: return value
*
* 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.
*/
*/
void
eeh_set_pe_freset
(
struct
device_node
*
dn
,
unsigned
int
*
freset
)
{
struct
pci_dev
*
dev
;
dn
=
eeh_find_device_pe
(
dn
);
/* Back up one, since config addrs might be shared */
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
of_node_to_eeh_dev
(
dn
->
parent
))
dn
=
dn
->
parent
;
dev
=
of_node_to_eeh_dev
(
dn
)
->
pdev
;
if
(
dev
)
*
freset
|=
dev
->
needs_freset
;
static
void
__rtas_set_slot_reset
(
struct
pci_dn
*
pdn
)
__eeh_set_pe_freset
(
dn
,
freset
);
}
/**
* eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
* @edev: pci device node to be reset.
*
* Assert the PCI #RST line for 1/4 second.
*/
static
void
eeh_reset_pe_once
(
struct
eeh_dev
*
edev
)
{
{
unsigned
int
freset
=
0
;
unsigned
int
freset
=
0
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
/* Determine type of EEH reset required for
/* Determine type of EEH reset required for
* Partitionable Endpoint, a hot-reset (1)
* Partitionable Endpoint, a hot-reset (1)
...
@@ -795,58 +658,68 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn)
...
@@ -795,58 +658,68 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn)
* A fundamental reset required by any device under
* A fundamental reset required by any device under
* Partitionable Endpoint trumps hot-reset.
* Partitionable Endpoint trumps hot-reset.
*/
*/
eeh_set_pe_freset
(
pdn
->
node
,
&
freset
);
eeh_set_pe_freset
(
dn
,
&
freset
);
if
(
freset
)
if
(
freset
)
rtas_pci_slot_reset
(
pdn
,
3
);
eeh_ops
->
reset
(
dn
,
EEH_RESET_FUNDAMENTAL
);
else
else
rtas_pci_slot_reset
(
pdn
,
1
);
eeh_ops
->
reset
(
dn
,
EEH_RESET_HOT
);
/* The PCI bus requires that the reset be held high for at least
/* The PCI bus requires that the reset be held high for at least
* a 100 milliseconds. We wait a bit longer 'just in case'.
*/
* a 100 milliseconds. We wait a bit longer 'just in case'.
*/
#define PCI_BUS_RST_HOLD_TIME_MSEC 250
#define PCI_BUS_RST_HOLD_TIME_MSEC 250
msleep
(
PCI_BUS_RST_HOLD_TIME_MSEC
);
msleep
(
PCI_BUS_RST_HOLD_TIME_MSEC
);
/* We might get hit with another EEH freeze as soon as the
/* We might get hit with another EEH freeze as soon as the
* pci slot reset line is dropped. Make sure we don't miss
* pci slot reset line is dropped. Make sure we don't miss
* these, and clear the flag now. */
* these, and clear the flag now.
eeh_clear_slot
(
pdn
->
node
,
EEH_MODE_ISOLATED
);
*/
eeh_clear_slot
(
dn
,
EEH_MODE_ISOLATED
);
rtas_pci_slot_reset
(
pdn
,
0
);
eeh_ops
->
reset
(
dn
,
EEH_RESET_DEACTIVATE
);
/* After a PCI slot has been reset, the PCI Express spec requires
/* 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
* a 1.5 second idle time for the bus to stabilize, before starting
* up traffic. */
* up traffic.
*/
#define PCI_BUS_SETTLE_TIME_MSEC 1800
#define PCI_BUS_SETTLE_TIME_MSEC 1800
msleep
(
PCI_BUS_SETTLE_TIME_MSEC
);
msleep
(
PCI_BUS_SETTLE_TIME_MSEC
);
}
}
int
rtas_set_slot_reset
(
struct
pci_dn
*
pdn
)
/**
* eeh_reset_pe - Reset the indicated PE
* @edev: PCI device associated EEH device
*
* 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.
*/
int
eeh_reset_pe
(
struct
eeh_dev
*
edev
)
{
{
int
i
,
rc
;
int
i
,
rc
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
/* Take three shots at resetting the bus */
/* Take three shots at resetting the bus */
for
(
i
=
0
;
i
<
3
;
i
++
)
{
for
(
i
=
0
;
i
<
3
;
i
++
)
{
__rtas_set_slot_reset
(
pdn
);
eeh_reset_pe_once
(
edev
);
rc
=
eeh_
wait_for_slot_status
(
p
dn
,
PCI_BUS_RESET_WAIT_MSEC
);
rc
=
eeh_
ops
->
wait_state
(
dn
,
PCI_BUS_RESET_WAIT_MSEC
);
if
(
rc
==
0
)
if
(
rc
==
(
EEH_STATE_MMIO_ACTIVE
|
EEH_STATE_DMA_ACTIVE
)
)
return
0
;
return
0
;
if
(
rc
<
0
)
{
if
(
rc
<
0
)
{
printk
(
KERN_ERR
"EEH: unrecoverable slot failure %s
\n
"
,
printk
(
KERN_ERR
"EEH: unrecoverable slot failure %s
\n
"
,
pdn
->
node
->
full_name
);
dn
->
full_name
);
return
-
1
;
return
-
1
;
}
}
printk
(
KERN_ERR
"EEH: bus reset %d failed on slot %s, rc=%d
\n
"
,
printk
(
KERN_ERR
"EEH: bus reset %d failed on slot %s, rc=%d
\n
"
,
i
+
1
,
pdn
->
node
->
full_name
,
rc
);
i
+
1
,
dn
->
full_name
,
rc
);
}
}
return
-
1
;
return
-
1
;
}
}
/* ------------------------------------------------------- */
/** Save and restore of PCI BARs
/** Save and restore of PCI BARs
*
*
* Although firmware will set up BARs during boot, it doesn't
* Although firmware will set up BARs during boot, it doesn't
...
@@ -856,181 +729,122 @@ int rtas_set_slot_reset(struct pci_dn *pdn)
...
@@ -856,181 +729,122 @@ int rtas_set_slot_reset(struct pci_dn *pdn)
*/
*/
/**
/**
*
__restore_bars - Restore the Base Address Registers
*
eeh_restore_one_device_bars - Restore the Base Address Registers for one device
* @
pdn: pci device nod
e
* @
edev: PCI device associated EEH devic
e
*
*
* Loads the PCI configuration space base address registers,
* Loads the PCI configuration space base address registers,
* the expansion ROM base address, the latency timer, and etc.
* the expansion ROM base address, the latency timer, and etc.
* from the saved values in the device node.
* from the saved values in the device node.
*/
*/
static
inline
void
__restore_bars
(
struct
pci_dn
*
pdn
)
static
inline
void
eeh_restore_one_device_bars
(
struct
eeh_dev
*
edev
)
{
{
int
i
;
int
i
;
u32
cmd
;
u32
cmd
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
if
(
!
edev
->
phb
)
return
;
if
(
NULL
==
pdn
->
phb
)
return
;
for
(
i
=
4
;
i
<
10
;
i
++
)
{
for
(
i
=
4
;
i
<
10
;
i
++
)
{
rtas_write_config
(
pdn
,
i
*
4
,
4
,
pdn
->
config_space
[
i
]);
eeh_ops
->
write_config
(
dn
,
i
*
4
,
4
,
edev
->
config_space
[
i
]);
}
}
/* 12 == Expansion ROM Address */
/* 12 == Expansion ROM Address */
rtas_write_config
(
pdn
,
12
*
4
,
4
,
pdn
->
config_space
[
12
]);
eeh_ops
->
write_config
(
dn
,
12
*
4
,
4
,
edev
->
config_space
[
12
]);
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
#define SAVED_BYTE(OFF) (((u8 *)(
pdn
->config_space))[BYTE_SWAP(OFF)])
#define SAVED_BYTE(OFF) (((u8 *)(
edev
->config_space))[BYTE_SWAP(OFF)])
rtas_write_config
(
p
dn
,
PCI_CACHE_LINE_SIZE
,
1
,
eeh_ops
->
write_config
(
dn
,
PCI_CACHE_LINE_SIZE
,
1
,
SAVED_BYTE
(
PCI_CACHE_LINE_SIZE
));
SAVED_BYTE
(
PCI_CACHE_LINE_SIZE
));
rtas_write_config
(
p
dn
,
PCI_LATENCY_TIMER
,
1
,
eeh_ops
->
write_config
(
dn
,
PCI_LATENCY_TIMER
,
1
,
SAVED_BYTE
(
PCI_LATENCY_TIMER
));
SAVED_BYTE
(
PCI_LATENCY_TIMER
));
/* max latency, min grant, interrupt pin and line */
/* max latency, min grant, interrupt pin and line */
rtas_write_config
(
pdn
,
15
*
4
,
4
,
pdn
->
config_space
[
15
]);
eeh_ops
->
write_config
(
dn
,
15
*
4
,
4
,
edev
->
config_space
[
15
]);
/* Restore PERR & SERR bits, some devices require it,
/* Restore PERR & SERR bits, some devices require it,
don't touch the other command bits */
* don't touch the other command bits
rtas_read_config
(
pdn
,
PCI_COMMAND
,
4
,
&
cmd
);
*/
if
(
pdn
->
config_space
[
1
]
&
PCI_COMMAND_PARITY
)
eeh_ops
->
read_config
(
dn
,
PCI_COMMAND
,
4
,
&
cmd
);
if
(
edev
->
config_space
[
1
]
&
PCI_COMMAND_PARITY
)
cmd
|=
PCI_COMMAND_PARITY
;
cmd
|=
PCI_COMMAND_PARITY
;
else
else
cmd
&=
~
PCI_COMMAND_PARITY
;
cmd
&=
~
PCI_COMMAND_PARITY
;
if
(
pdn
->
config_space
[
1
]
&
PCI_COMMAND_SERR
)
if
(
edev
->
config_space
[
1
]
&
PCI_COMMAND_SERR
)
cmd
|=
PCI_COMMAND_SERR
;
cmd
|=
PCI_COMMAND_SERR
;
else
else
cmd
&=
~
PCI_COMMAND_SERR
;
cmd
&=
~
PCI_COMMAND_SERR
;
rtas_write_config
(
p
dn
,
PCI_COMMAND
,
4
,
cmd
);
eeh_ops
->
write_config
(
dn
,
PCI_COMMAND
,
4
,
cmd
);
}
}
/**
/**
* eeh_restore_bars - restore the PCI config space info
* eeh_restore_bars - Restore the PCI config space info
* @edev: EEH device
*
*
* This routine performs a recursive walk to the children
* This routine performs a recursive walk to the children
* of this device as well.
* of this device as well.
*/
*/
void
eeh_restore_bars
(
struct
pci_dn
*
pdn
)
void
eeh_restore_bars
(
struct
eeh_dev
*
edev
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
if
(
!
pdn
)
if
(
!
edev
)
return
;
return
;
if
((
pdn
->
eeh_mode
&
EEH_MODE_SUPPORTED
)
&&
!
IS_BRIDGE
(
pdn
->
class_code
))
if
((
edev
->
mode
&
EEH_MODE_SUPPORTED
)
&&
!
IS_BRIDGE
(
edev
->
class_code
))
__restore_bars
(
pdn
);
eeh_restore_one_device_bars
(
edev
);
for_each_child_of_node
(
pdn
->
node
,
dn
)
for_each_child_of_node
(
eeh_dev_to_of_node
(
edev
)
,
dn
)
eeh_restore_bars
(
PCI_DN
(
dn
));
eeh_restore_bars
(
of_node_to_eeh_dev
(
dn
));
}
}
/**
/**
* eeh_save_bars - save device bars
* eeh_save_bars - Save device bars
* @edev: PCI device associated EEH device
*
*
* Save the values of the device bars. Unlike the restore
* Save the values of the device bars. Unlike the restore
* routine, this routine is *not* recursive. This is because
* routine, this routine is *not* recursive. This is because
* PCI devices are added individually; but, for the restore,
* PCI devices are added individually; but, for the restore,
* an entire slot is reset at a time.
* an entire slot is reset at a time.
*/
*/
static
void
eeh_save_bars
(
struct
pci_dn
*
pdn
)
static
void
eeh_save_bars
(
struct
eeh_dev
*
edev
)
{
{
int
i
;
int
i
;
struct
device_node
*
dn
;
if
(
!
pdn
)
if
(
!
edev
)
return
;
return
;
dn
=
eeh_dev_to_of_node
(
edev
);
for
(
i
=
0
;
i
<
16
;
i
++
)
for
(
i
=
0
;
i
<
16
;
i
++
)
rtas_read_config
(
pdn
,
i
*
4
,
4
,
&
pdn
->
config_space
[
i
]);
eeh_ops
->
read_config
(
dn
,
i
*
4
,
4
,
&
edev
->
config_space
[
i
]);
}
void
rtas_configure_bridge
(
struct
pci_dn
*
pdn
)
{
int
config_addr
;
int
rc
;
int
token
;
/* Use PE configuration address, if present */
config_addr
=
pdn
->
eeh_config_addr
;
if
(
pdn
->
eeh_pe_config_addr
)
config_addr
=
pdn
->
eeh_pe_config_addr
;
/* Use new configure-pe function, if supported */
if
(
ibm_configure_pe
!=
RTAS_UNKNOWN_SERVICE
)
token
=
ibm_configure_pe
;
else
token
=
ibm_configure_bridge
;
rc
=
rtas_call
(
token
,
3
,
1
,
NULL
,
config_addr
,
BUID_HI
(
pdn
->
phb
->
buid
),
BUID_LO
(
pdn
->
phb
->
buid
));
if
(
rc
)
{
printk
(
KERN_WARNING
"EEH: Unable to configure device bridge (%d) for %s
\n
"
,
rc
,
pdn
->
node
->
full_name
);
}
}
}
/* ------------------------------------------------------------- */
/**
/* The code below deals with enabling EEH for devices during the
* eeh_early_enable - Early enable EEH on the indicated device
* early boot sequence. EEH must be enabled before any PCI probing
* @dn: device node
* can be done.
* @data: BUID
*
* Enable EEH functionality on the specified PCI device. The function
* is expected to be called before real PCI probing is done. However,
* the PHBs have been initialized at this point.
*/
*/
static
void
*
eeh_early_enable
(
struct
device_node
*
dn
,
void
*
data
)
#define EEH_ENABLE 1
struct
eeh_early_enable_info
{
unsigned
int
buid_hi
;
unsigned
int
buid_lo
;
};
static
int
get_pe_addr
(
int
config_addr
,
struct
eeh_early_enable_info
*
info
)
{
{
unsigned
int
rets
[
3
];
int
ret
;
/* Use latest config-addr token on power6 */
if
(
ibm_get_config_addr_info2
!=
RTAS_UNKNOWN_SERVICE
)
{
/* Make sure we have a PE in hand */
ret
=
rtas_call
(
ibm_get_config_addr_info2
,
4
,
2
,
rets
,
config_addr
,
info
->
buid_hi
,
info
->
buid_lo
,
1
);
if
(
ret
||
(
rets
[
0
]
==
0
))
return
0
;
ret
=
rtas_call
(
ibm_get_config_addr_info2
,
4
,
2
,
rets
,
config_addr
,
info
->
buid_hi
,
info
->
buid_lo
,
0
);
if
(
ret
)
return
0
;
return
rets
[
0
];
}
/* Use older config-addr token on power5 */
if
(
ibm_get_config_addr_info
!=
RTAS_UNKNOWN_SERVICE
)
{
ret
=
rtas_call
(
ibm_get_config_addr_info
,
4
,
2
,
rets
,
config_addr
,
info
->
buid_hi
,
info
->
buid_lo
,
0
);
if
(
ret
)
return
0
;
return
rets
[
0
];
}
return
0
;
}
/* Enable eeh for the given device node. */
static
void
*
early_enable_eeh
(
struct
device_node
*
dn
,
void
*
data
)
{
unsigned
int
rets
[
3
];
struct
eeh_early_enable_info
*
info
=
data
;
int
ret
;
int
ret
;
const
u32
*
class_code
=
of_get_property
(
dn
,
"class-code"
,
NULL
);
const
u32
*
class_code
=
of_get_property
(
dn
,
"class-code"
,
NULL
);
const
u32
*
vendor_id
=
of_get_property
(
dn
,
"vendor-id"
,
NULL
);
const
u32
*
vendor_id
=
of_get_property
(
dn
,
"vendor-id"
,
NULL
);
const
u32
*
device_id
=
of_get_property
(
dn
,
"device-id"
,
NULL
);
const
u32
*
device_id
=
of_get_property
(
dn
,
"device-id"
,
NULL
);
const
u32
*
regs
;
const
u32
*
regs
;
int
enable
;
int
enable
;
struct
pci_dn
*
pdn
=
PCI_DN
(
dn
);
struct
eeh_dev
*
edev
=
of_node_to_eeh_dev
(
dn
);
pdn
->
class_code
=
0
;
edev
->
class_code
=
0
;
pdn
->
eeh_
mode
=
0
;
edev
->
mode
=
0
;
pdn
->
eeh_
check_count
=
0
;
edev
->
check_count
=
0
;
pdn
->
eeh_
freeze_count
=
0
;
edev
->
freeze_count
=
0
;
pdn
->
eeh_
false_positives
=
0
;
edev
->
false_positives
=
0
;
if
(
!
of_device_is_available
(
dn
))
if
(
!
of_device_is_available
(
dn
))
return
NULL
;
return
NULL
;
...
@@ -1041,54 +855,56 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
...
@@ -1041,54 +855,56 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
/* There is nothing to check on PCI to ISA bridges */
/* There is nothing to check on PCI to ISA bridges */
if
(
dn
->
type
&&
!
strcmp
(
dn
->
type
,
"isa"
))
{
if
(
dn
->
type
&&
!
strcmp
(
dn
->
type
,
"isa"
))
{
pdn
->
eeh_
mode
|=
EEH_MODE_NOCHECK
;
edev
->
mode
|=
EEH_MODE_NOCHECK
;
return
NULL
;
return
NULL
;
}
}
pdn
->
class_code
=
*
class_code
;
edev
->
class_code
=
*
class_code
;
/* Ok... see if this device supports EEH. Some do, some don't,
/* Ok... see if this device supports EEH. Some do, some don't,
* and the only way to find out is to check each and every one. */
* and the only way to find out is to check each and every one.
*/
regs
=
of_get_property
(
dn
,
"reg"
,
NULL
);
regs
=
of_get_property
(
dn
,
"reg"
,
NULL
);
if
(
regs
)
{
if
(
regs
)
{
/* First register entry is addr (00BBSS00) */
/* First register entry is addr (00BBSS00) */
/* Try to enable eeh */
/* Try to enable eeh */
ret
=
rtas_call
(
ibm_set_eeh_option
,
4
,
1
,
NULL
,
ret
=
eeh_ops
->
set_option
(
dn
,
EEH_OPT_ENABLE
);
regs
[
0
],
info
->
buid_hi
,
info
->
buid_lo
,
EEH_ENABLE
);
enable
=
0
;
enable
=
0
;
if
(
ret
==
0
)
{
if
(
ret
==
0
)
{
pdn
->
eeh_
config_addr
=
regs
[
0
];
edev
->
config_addr
=
regs
[
0
];
/* If the newer, better, ibm,get-config-addr-info is supported,
/* If the newer, better, ibm,get-config-addr-info is supported,
* then use that instead. */
* then use that instead.
pdn
->
eeh_pe_config_addr
=
get_pe_addr
(
pdn
->
eeh_config_addr
,
info
);
*/
edev
->
pe_config_addr
=
eeh_ops
->
get_pe_addr
(
dn
);
/* Some older systems (Power4) allow the
/* Some older systems (Power4) allow the
* ibm,set-eeh-option call to succeed even on nodes
* ibm,set-eeh-option call to succeed even on nodes
* where EEH is not supported. Verify support
* where EEH is not supported. Verify support
* explicitly. */
* explicitly.
ret
=
read_slot_reset_state
(
pdn
,
rets
);
*/
if
((
ret
==
0
)
&&
(
rets
[
1
]
==
1
))
ret
=
eeh_ops
->
get_state
(
dn
,
NULL
);
if
(
ret
>
0
&&
ret
!=
EEH_STATE_NOT_SUPPORT
)
enable
=
1
;
enable
=
1
;
}
}
if
(
enable
)
{
if
(
enable
)
{
eeh_subsystem_enabled
=
1
;
eeh_subsystem_enabled
=
1
;
pdn
->
eeh_
mode
|=
EEH_MODE_SUPPORTED
;
edev
->
mode
|=
EEH_MODE_SUPPORTED
;
pr_debug
(
"EEH: %s: eeh enabled, config=%x pe_config=%x
\n
"
,
pr_debug
(
"EEH: %s: eeh enabled, config=%x pe_config=%x
\n
"
,
dn
->
full_name
,
pdn
->
eeh_
config_addr
,
dn
->
full_name
,
edev
->
config_addr
,
pdn
->
eeh_
pe_config_addr
);
edev
->
pe_config_addr
);
}
else
{
}
else
{
/* This device doesn't support EEH, but it may have an
/* This device doesn't support EEH, but it may have an
* EEH parent, in which case we mark it as supported. */
* EEH parent, in which case we mark it as supported.
if
(
dn
->
parent
&&
PCI_DN
(
dn
->
parent
)
*/
&&
(
PCI_DN
(
dn
->
parent
)
->
eeh_mode
&
EEH_MODE_SUPPORTED
))
{
if
(
dn
->
parent
&&
of_node_to_eeh_dev
(
dn
->
parent
)
&&
(
of_node_to_eeh_dev
(
dn
->
parent
)
->
mode
&
EEH_MODE_SUPPORTED
))
{
/* Parent supports EEH. */
/* Parent supports EEH. */
pdn
->
eeh_
mode
|=
EEH_MODE_SUPPORTED
;
edev
->
mode
|=
EEH_MODE_SUPPORTED
;
pdn
->
eeh_config_addr
=
PCI_DN
(
dn
->
parent
)
->
eeh_
config_addr
;
edev
->
config_addr
=
of_node_to_eeh_dev
(
dn
->
parent
)
->
config_addr
;
return
NULL
;
return
NULL
;
}
}
}
}
...
@@ -1097,11 +913,63 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
...
@@ -1097,11 +913,63 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
dn
->
full_name
);
dn
->
full_name
);
}
}
eeh_save_bars
(
pdn
);
eeh_save_bars
(
edev
);
return
NULL
;
return
NULL
;
}
}
/*
/**
* 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
;
}
/**
* eeh_init - EEH initialization
*
* Initialize EEH by trying to enable it for all of the adapters in the system.
* 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.
* 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
* Note that we leave EEH on so failed config cycles won't cause a machine
...
@@ -1117,50 +985,35 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
...
@@ -1117,50 +985,35 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
void
__init
eeh_init
(
void
)
void
__init
eeh_init
(
void
)
{
{
struct
device_node
*
phb
,
*
np
;
struct
device_node
*
phb
,
*
np
;
struct
eeh_early_enable_info
info
;
int
ret
;
/* call platform initialization function */
if
(
!
eeh_ops
)
{
pr_warning
(
"%s: Platform EEH operation not found
\n
"
,
__func__
);
return
;
}
else
if
((
ret
=
eeh_ops
->
init
()))
{
pr_warning
(
"%s: Failed to call platform init function (%d)
\n
"
,
__func__
,
ret
);
return
;
}
raw_spin_lock_init
(
&
confirm_error_lock
);
raw_spin_lock_init
(
&
confirm_error_lock
);
spin_lock_init
(
&
slot_errbuf_lock
);
np
=
of_find_node_by_path
(
"/rtas"
);
np
=
of_find_node_by_path
(
"/rtas"
);
if
(
np
==
NULL
)
if
(
np
==
NULL
)
return
;
return
;
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_info
=
rtas_token
(
"ibm,get-config-addr-info"
);
ibm_get_config_addr_info2
=
rtas_token
(
"ibm,get-config-addr-info2"
);
ibm_configure_bridge
=
rtas_token
(
"ibm,configure-bridge"
);
ibm_configure_pe
=
rtas_token
(
"ibm,configure-pe"
);
if
(
ibm_set_eeh_option
==
RTAS_UNKNOWN_SERVICE
)
return
;
eeh_error_buf_size
=
rtas_token
(
"rtas-error-log-max"
);
if
(
eeh_error_buf_size
==
RTAS_UNKNOWN_SERVICE
)
{
eeh_error_buf_size
=
1024
;
}
if
(
eeh_error_buf_size
>
RTAS_ERROR_LOG_MAX
)
{
printk
(
KERN_WARNING
"EEH: rtas-error-log-max is bigger than allocated "
"buffer ! (%d vs %d)"
,
eeh_error_buf_size
,
RTAS_ERROR_LOG_MAX
);
eeh_error_buf_size
=
RTAS_ERROR_LOG_MAX
;
}
/* Enable EEH for all adapters. Note that eeh requires buid's */
/* Enable EEH for all adapters. Note that eeh requires buid's */
for
(
phb
=
of_find_node_by_name
(
NULL
,
"pci"
);
phb
;
for
(
phb
=
of_find_node_by_name
(
NULL
,
"pci"
);
phb
;
phb
=
of_find_node_by_name
(
phb
,
"pci"
))
{
phb
=
of_find_node_by_name
(
phb
,
"pci"
))
{
unsigned
long
buid
;
unsigned
long
buid
;
buid
=
get_phb_buid
(
phb
);
buid
=
get_phb_buid
(
phb
);
if
(
buid
==
0
||
PCI_DN
(
phb
)
==
NULL
)
if
(
buid
==
0
||
!
of_node_to_eeh_dev
(
phb
)
)
continue
;
continue
;
info
.
buid_lo
=
BUID_LO
(
buid
);
traverse_pci_devices
(
phb
,
eeh_early_enable
,
NULL
);
info
.
buid_hi
=
BUID_HI
(
buid
);
traverse_pci_devices
(
phb
,
early_enable_eeh
,
&
info
);
}
}
if
(
eeh_subsystem_enabled
)
if
(
eeh_subsystem_enabled
)
...
@@ -1170,7 +1023,7 @@ void __init eeh_init(void)
...
@@ -1170,7 +1023,7 @@ void __init eeh_init(void)
}
}
/**
/**
* eeh_add_device_early -
e
nable EEH for the indicated device_node
* eeh_add_device_early -
E
nable EEH for the indicated device_node
* @dn: device node for which to set up EEH
* @dn: device node for which to set up EEH
*
*
* This routine must be used to perform EEH initialization for PCI
* This routine must be used to perform EEH initialization for PCI
...
@@ -1184,21 +1037,26 @@ void __init eeh_init(void)
...
@@ -1184,21 +1037,26 @@ void __init eeh_init(void)
static
void
eeh_add_device_early
(
struct
device_node
*
dn
)
static
void
eeh_add_device_early
(
struct
device_node
*
dn
)
{
{
struct
pci_controller
*
phb
;
struct
pci_controller
*
phb
;
struct
eeh_early_enable_info
info
;
if
(
!
dn
||
!
PCI_DN
(
dn
))
if
(
!
dn
||
!
of_node_to_eeh_dev
(
dn
))
return
;
return
;
phb
=
PCI_DN
(
dn
)
->
phb
;
phb
=
of_node_to_eeh_dev
(
dn
)
->
phb
;
/* USB Bus children of PCI devices will not have BUID's */
/* USB Bus children of PCI devices will not have BUID's */
if
(
NULL
==
phb
||
0
==
phb
->
buid
)
if
(
NULL
==
phb
||
0
==
phb
->
buid
)
return
;
return
;
info
.
buid_hi
=
BUID_HI
(
phb
->
buid
);
eeh_early_enable
(
dn
,
NULL
);
info
.
buid_lo
=
BUID_LO
(
phb
->
buid
);
early_enable_eeh
(
dn
,
&
info
);
}
}
/**
* 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).
*/
void
eeh_add_device_tree_early
(
struct
device_node
*
dn
)
void
eeh_add_device_tree_early
(
struct
device_node
*
dn
)
{
{
struct
device_node
*
sib
;
struct
device_node
*
sib
;
...
@@ -1210,7 +1068,7 @@ void eeh_add_device_tree_early(struct device_node *dn)
...
@@ -1210,7 +1068,7 @@ void eeh_add_device_tree_early(struct device_node *dn)
EXPORT_SYMBOL_GPL
(
eeh_add_device_tree_early
);
EXPORT_SYMBOL_GPL
(
eeh_add_device_tree_early
);
/**
/**
* eeh_add_device_late -
p
erform EEH initialization for the indicated pci device
* eeh_add_device_late -
P
erform EEH initialization for the indicated pci device
* @dev: pci device for which to set up EEH
* @dev: pci device for which to set up EEH
*
*
* This routine must be used to complete EEH initialization for PCI
* This routine must be used to complete EEH initialization for PCI
...
@@ -1219,7 +1077,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
...
@@ -1219,7 +1077,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
static
void
eeh_add_device_late
(
struct
pci_dev
*
dev
)
static
void
eeh_add_device_late
(
struct
pci_dev
*
dev
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
struct
pci_dn
*
pdn
;
struct
eeh_dev
*
edev
;
if
(
!
dev
||
!
eeh_subsystem_enabled
)
if
(
!
dev
||
!
eeh_subsystem_enabled
)
return
;
return
;
...
@@ -1227,20 +1085,29 @@ static void eeh_add_device_late(struct pci_dev *dev)
...
@@ -1227,20 +1085,29 @@ static void eeh_add_device_late(struct pci_dev *dev)
pr_debug
(
"EEH: Adding device %s
\n
"
,
pci_name
(
dev
));
pr_debug
(
"EEH: Adding device %s
\n
"
,
pci_name
(
dev
));
dn
=
pci_device_to_OF_node
(
dev
);
dn
=
pci_device_to_OF_node
(
dev
);
pdn
=
PCI_DN
(
dn
);
edev
=
pci_dev_to_eeh_dev
(
dev
);
if
(
pdn
->
pci
dev
==
dev
)
{
if
(
edev
->
p
dev
==
dev
)
{
pr_debug
(
"EEH: Already referenced !
\n
"
);
pr_debug
(
"EEH: Already referenced !
\n
"
);
return
;
return
;
}
}
WARN_ON
(
pdn
->
pci
dev
);
WARN_ON
(
edev
->
p
dev
);
pci_dev_get
(
dev
);
pci_dev_get
(
dev
);
pdn
->
pcidev
=
dev
;
edev
->
pdev
=
dev
;
dev
->
dev
.
archdata
.
edev
=
edev
;
pci_addr_cache_insert_device
(
dev
);
pci_addr_cache_insert_device
(
dev
);
eeh_sysfs_add_device
(
dev
);
eeh_sysfs_add_device
(
dev
);
}
}
/**
* 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.
*/
void
eeh_add_device_tree_late
(
struct
pci_bus
*
bus
)
void
eeh_add_device_tree_late
(
struct
pci_bus
*
bus
)
{
{
struct
pci_dev
*
dev
;
struct
pci_dev
*
dev
;
...
@@ -1257,7 +1124,7 @@ void eeh_add_device_tree_late(struct pci_bus *bus)
...
@@ -1257,7 +1124,7 @@ void eeh_add_device_tree_late(struct pci_bus *bus)
EXPORT_SYMBOL_GPL
(
eeh_add_device_tree_late
);
EXPORT_SYMBOL_GPL
(
eeh_add_device_tree_late
);
/**
/**
* eeh_remove_device -
u
ndo EEH setup for the indicated pci device
* eeh_remove_device -
U
ndo EEH setup for the indicated pci device
* @dev: pci device to be removed
* @dev: pci device to be removed
*
*
* This routine should be called when a device is removed from
* This routine should be called when a device is removed from
...
@@ -1268,25 +1135,35 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
...
@@ -1268,25 +1135,35 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
*/
*/
static
void
eeh_remove_device
(
struct
pci_dev
*
dev
)
static
void
eeh_remove_device
(
struct
pci_dev
*
dev
)
{
{
struct
device_node
*
dn
;
struct
eeh_dev
*
edev
;
if
(
!
dev
||
!
eeh_subsystem_enabled
)
if
(
!
dev
||
!
eeh_subsystem_enabled
)
return
;
return
;
edev
=
pci_dev_to_eeh_dev
(
dev
);
/* Unregister the device with the EEH/PCI address search system */
/* Unregister the device with the EEH/PCI address search system */
pr_debug
(
"EEH: Removing device %s
\n
"
,
pci_name
(
dev
));
pr_debug
(
"EEH: Removing device %s
\n
"
,
pci_name
(
dev
));
dn
=
pci_device_to_OF_node
(
dev
);
if
(
!
edev
||
!
edev
->
pdev
)
{
if
(
PCI_DN
(
dn
)
->
pcidev
==
NULL
)
{
pr_debug
(
"EEH: Not referenced !
\n
"
);
pr_debug
(
"EEH: Not referenced !
\n
"
);
return
;
return
;
}
}
PCI_DN
(
dn
)
->
pcidev
=
NULL
;
edev
->
pdev
=
NULL
;
pci_dev_put
(
dev
);
dev
->
dev
.
archdata
.
edev
=
NULL
;
pci_dev_put
(
dev
);
pci_addr_cache_remove_device
(
dev
);
pci_addr_cache_remove_device
(
dev
);
eeh_sysfs_remove_device
(
dev
);
eeh_sysfs_remove_device
(
dev
);
}
}
/**
* eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
* @dev: PCI device
*
* 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.
*/
void
eeh_remove_bus_device
(
struct
pci_dev
*
dev
)
void
eeh_remove_bus_device
(
struct
pci_dev
*
dev
)
{
{
struct
pci_bus
*
bus
=
dev
->
subordinate
;
struct
pci_bus
*
bus
=
dev
->
subordinate
;
...
@@ -1305,21 +1182,24 @@ static int proc_eeh_show(struct seq_file *m, void *v)
...
@@ -1305,21 +1182,24 @@ static int proc_eeh_show(struct seq_file *m, void *v)
{
{
if
(
0
==
eeh_subsystem_enabled
)
{
if
(
0
==
eeh_subsystem_enabled
)
{
seq_printf
(
m
,
"EEH Subsystem is globally disabled
\n
"
);
seq_printf
(
m
,
"EEH Subsystem is globally disabled
\n
"
);
seq_printf
(
m
,
"eeh_total_mmio_ffs=%l
d
\n
"
,
total_mmio_ffs
);
seq_printf
(
m
,
"eeh_total_mmio_ffs=%l
lu
\n
"
,
eeh_stats
.
total_mmio_ffs
);
}
else
{
}
else
{
seq_printf
(
m
,
"EEH Subsystem is enabled
\n
"
);
seq_printf
(
m
,
"EEH Subsystem is enabled
\n
"
);
seq_printf
(
m
,
seq_printf
(
m
,
"no device=%ld
\n
"
"no device=%llu
\n
"
"no device node=%ld
\n
"
"no device node=%llu
\n
"
"no config address=%ld
\n
"
"no config address=%llu
\n
"
"check not wanted=%ld
\n
"
"check not wanted=%llu
\n
"
"eeh_total_mmio_ffs=%ld
\n
"
"eeh_total_mmio_ffs=%llu
\n
"
"eeh_false_positives=%ld
\n
"
"eeh_false_positives=%llu
\n
"
"eeh_slot_resets=%ld
\n
"
,
"eeh_slot_resets=%llu
\n
"
,
no_device
,
no_dn
,
no_cfg_addr
,
eeh_stats
.
no_device
,
ignored_check
,
total_mmio_ffs
,
eeh_stats
.
no_dn
,
false_positives
,
eeh_stats
.
no_cfg_addr
,
slot_resets
);
eeh_stats
.
ignored_check
,
eeh_stats
.
total_mmio_ffs
,
eeh_stats
.
false_positives
,
eeh_stats
.
slot_resets
);
}
}
return
0
;
return
0
;
...
...
arch/powerpc/platforms/pseries/eeh_cache.c
View file @
aba0eb84
/*
/*
* eeh_cache.c
* PCI address cache; allows the lookup of PCI devices based on I/O address
* PCI address cache; allows the lookup of PCI devices based on I/O address
*
*
* Copyright IBM Corporation 2004
* Copyright IBM Corporation 2004
...
@@ -47,8 +46,7 @@
...
@@ -47,8 +46,7 @@
* than any hash algo I could think of for this problem, even
* than any hash algo I could think of for this problem, even
* with the penalty of slow pointer chases for d-cache misses).
* with the penalty of slow pointer chases for d-cache misses).
*/
*/
struct
pci_io_addr_range
struct
pci_io_addr_range
{
{
struct
rb_node
rb_node
;
struct
rb_node
rb_node
;
unsigned
long
addr_lo
;
unsigned
long
addr_lo
;
unsigned
long
addr_hi
;
unsigned
long
addr_hi
;
...
@@ -56,13 +54,12 @@ struct pci_io_addr_range
...
@@ -56,13 +54,12 @@ struct pci_io_addr_range
unsigned
int
flags
;
unsigned
int
flags
;
};
};
static
struct
pci_io_addr_cache
static
struct
pci_io_addr_cache
{
{
struct
rb_root
rb_root
;
struct
rb_root
rb_root
;
spinlock_t
piar_lock
;
spinlock_t
piar_lock
;
}
pci_io_addr_cache_root
;
}
pci_io_addr_cache_root
;
static
inline
struct
pci_dev
*
__pci_
get_device_by_addr
(
unsigned
long
addr
)
static
inline
struct
pci_dev
*
__pci_
addr_cache_get_device
(
unsigned
long
addr
)
{
{
struct
rb_node
*
n
=
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
struct
rb_node
*
n
=
pci_io_addr_cache_root
.
rb_root
.
rb_node
;
...
@@ -86,7 +83,7 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr)
...
@@ -86,7 +83,7 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr)
}
}
/**
/**
* pci_
get_device_by_addr
- Get device, given only address
* pci_
addr_cache_get_device
- Get device, given only address
* @addr: mmio (PIO) phys address or i/o port number
* @addr: mmio (PIO) phys address or i/o port number
*
*
* Given an mmio phys address, or a port number, find a pci device
* Given an mmio phys address, or a port number, find a pci device
...
@@ -95,13 +92,13 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr)
...
@@ -95,13 +92,13 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr)
* from zero (that is, they do *not* have pci_io_addr added in).
* from zero (that is, they do *not* have pci_io_addr added in).
* It is safe to call this function within an interrupt.
* It is safe to call this function within an interrupt.
*/
*/
struct
pci_dev
*
pci_
get_device_by_addr
(
unsigned
long
addr
)
struct
pci_dev
*
pci_
addr_cache_get_device
(
unsigned
long
addr
)
{
{
struct
pci_dev
*
dev
;
struct
pci_dev
*
dev
;
unsigned
long
flags
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
spin_lock_irqsave
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
dev
=
__pci_
get_device_by_addr
(
addr
);
dev
=
__pci_
addr_cache_get_device
(
addr
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
spin_unlock_irqrestore
(
&
pci_io_addr_cache_root
.
piar_lock
,
flags
);
return
dev
;
return
dev
;
}
}
...
@@ -166,7 +163,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
...
@@ -166,7 +163,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
#ifdef DEBUG
#ifdef DEBUG
printk
(
KERN_DEBUG
"PIAR: insert range=[%lx:%lx] dev=%s
\n
"
,
printk
(
KERN_DEBUG
"PIAR: insert range=[%lx:%lx] dev=%s
\n
"
,
alo
,
ahi
,
pci_name
(
dev
));
alo
,
ahi
,
pci_name
(
dev
));
#endif
#endif
rb_link_node
(
&
piar
->
rb_node
,
parent
,
p
);
rb_link_node
(
&
piar
->
rb_node
,
parent
,
p
);
...
@@ -178,7 +175,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
...
@@ -178,7 +175,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
static
void
__pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
static
void
__pci_addr_cache_insert_device
(
struct
pci_dev
*
dev
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
struct
pci_dn
*
pdn
;
struct
eeh_dev
*
edev
;
int
i
;
int
i
;
dn
=
pci_device_to_OF_node
(
dev
);
dn
=
pci_device_to_OF_node
(
dev
);
...
@@ -187,13 +184,19 @@ static void __pci_addr_cache_insert_device(struct pci_dev *dev)
...
@@ -187,13 +184,19 @@ static void __pci_addr_cache_insert_device(struct pci_dev *dev)
return
;
return
;
}
}
edev
=
of_node_to_eeh_dev
(
dn
);
if
(
!
edev
)
{
pr_warning
(
"PCI: no EEH dev found for dn=%s
\n
"
,
dn
->
full_name
);
return
;
}
/* Skip any devices for which EEH is not enabled. */
/* Skip any devices for which EEH is not enabled. */
pdn
=
PCI_DN
(
dn
);
if
(
!
(
edev
->
mode
&
EEH_MODE_SUPPORTED
)
||
if
(
!
(
pdn
->
eeh_mode
&
EEH_MODE_SUPPORTED
)
||
edev
->
mode
&
EEH_MODE_NOCHECK
)
{
pdn
->
eeh_mode
&
EEH_MODE_NOCHECK
)
{
#ifdef DEBUG
#ifdef DEBUG
pr
intk
(
KERN_INFO
"PCI: skip building address cache for=%s - %s
\n
"
,
pr
_info
(
"PCI: skip building address cache for=%s - %s
\n
"
,
pci_name
(
dev
),
pdn
->
node
->
full_name
);
pci_name
(
dev
),
dn
->
full_name
);
#endif
#endif
return
;
return
;
}
}
...
@@ -284,6 +287,7 @@ void pci_addr_cache_remove_device(struct pci_dev *dev)
...
@@ -284,6 +287,7 @@ void pci_addr_cache_remove_device(struct pci_dev *dev)
void
__init
pci_addr_cache_build
(
void
)
void
__init
pci_addr_cache_build
(
void
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
struct
eeh_dev
*
edev
;
struct
pci_dev
*
dev
=
NULL
;
struct
pci_dev
*
dev
=
NULL
;
spin_lock_init
(
&
pci_io_addr_cache_root
.
piar_lock
);
spin_lock_init
(
&
pci_io_addr_cache_root
.
piar_lock
);
...
@@ -294,8 +298,14 @@ void __init pci_addr_cache_build(void)
...
@@ -294,8 +298,14 @@ void __init pci_addr_cache_build(void)
dn
=
pci_device_to_OF_node
(
dev
);
dn
=
pci_device_to_OF_node
(
dev
);
if
(
!
dn
)
if
(
!
dn
)
continue
;
continue
;
edev
=
of_node_to_eeh_dev
(
dn
);
if
(
!
edev
)
continue
;
pci_dev_get
(
dev
);
/* matching put is in eeh_remove_device() */
pci_dev_get
(
dev
);
/* matching put is in eeh_remove_device() */
PCI_DN
(
dn
)
->
pcidev
=
dev
;
dev
->
dev
.
archdata
.
edev
=
edev
;
edev
->
pdev
=
dev
;
eeh_sysfs_add_device
(
dev
);
eeh_sysfs_add_device
(
dev
);
}
}
...
...
arch/powerpc/platforms/pseries/eeh_dev.c
0 → 100644
View file @
aba0eb84
/*
* The file intends to implement dynamic creation of EEH device, which will
* be bound with OF node and PCI device simutaneously. The EEH devices would
* be foundamental information for EEH core components to work proerly. Besides,
* We have to support multiple situations where dynamic creation of EEH device
* is required:
*
* 1) Before PCI emunation starts, we need create EEH devices according to the
* PCI sensitive OF nodes.
* 2) When PCI emunation is done, we need do the binding between PCI device and
* the associated EEH device.
* 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device
* will be created while PCI sensitive OF node is detected from DR.
* 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If
* PHB is newly inserted, we also need create EEH devices accordingly.
*
* Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012.
*
* 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/export.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
/**
* eeh_dev_init - Create EEH device according to OF node
* @dn: device node
* @data: PHB
*
* It will create EEH device according to the given OF node. The function
* might be called by PCI emunation, DR, PHB hotplug.
*/
void
*
__devinit
eeh_dev_init
(
struct
device_node
*
dn
,
void
*
data
)
{
struct
pci_controller
*
phb
=
data
;
struct
eeh_dev
*
edev
;
/* Allocate EEH device */
edev
=
zalloc_maybe_bootmem
(
sizeof
(
*
edev
),
GFP_KERNEL
);
if
(
!
edev
)
{
pr_warning
(
"%s: out of memory
\n
"
,
__func__
);
return
NULL
;
}
/* Associate EEH device with OF node */
dn
->
edev
=
edev
;
edev
->
dn
=
dn
;
edev
->
phb
=
phb
;
return
NULL
;
}
/**
* eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB
* @phb: PHB
*
* Scan the PHB OF node and its child association, then create the
* EEH devices accordingly
*/
void
__devinit
eeh_dev_phb_init_dynamic
(
struct
pci_controller
*
phb
)
{
struct
device_node
*
dn
=
phb
->
dn
;
/* EEH device for PHB */
eeh_dev_init
(
dn
,
phb
);
/* EEH devices for children OF nodes */
traverse_pci_devices
(
dn
,
eeh_dev_init
,
phb
);
}
/**
* eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs
*
* Scan all the existing PHBs and create EEH devices for their OF
* nodes and their children OF nodes
*/
void
__init
eeh_dev_phb_init
(
void
)
{
struct
pci_controller
*
phb
,
*
tmp
;
list_for_each_entry_safe
(
phb
,
tmp
,
&
hose_list
,
list_node
)
eeh_dev_phb_init_dynamic
(
phb
);
}
arch/powerpc/platforms/pseries/eeh_driver.c
View file @
aba0eb84
...
@@ -33,8 +33,14 @@
...
@@ -33,8 +33,14 @@
#include <asm/prom.h>
#include <asm/prom.h>
#include <asm/rtas.h>
#include <asm/rtas.h>
/**
static
inline
const
char
*
pcid_name
(
struct
pci_dev
*
pdev
)
* eeh_pcid_name - Retrieve name of PCI device driver
* @pdev: PCI device
*
* This routine is used to retrieve the name of PCI device driver
* if that's valid.
*/
static
inline
const
char
*
eeh_pcid_name
(
struct
pci_dev
*
pdev
)
{
{
if
(
pdev
&&
pdev
->
dev
.
driver
)
if
(
pdev
&&
pdev
->
dev
.
driver
)
return
pdev
->
dev
.
driver
->
name
;
return
pdev
->
dev
.
driver
->
name
;
...
@@ -64,48 +70,59 @@ static void print_device_node_tree(struct pci_dn *pdn, int dent)
...
@@ -64,48 +70,59 @@ static void print_device_node_tree(struct pci_dn *pdn, int dent)
#endif
#endif
/**
/**
* eeh_disable_irq - disable interrupt for the recovering device
* eeh_disable_irq - Disable interrupt for the recovering device
* @dev: PCI device
*
* This routine must be called when reporting temporary or permanent
* error to the particular PCI device to disable interrupt of that
* device. If the device has enabled MSI or MSI-X interrupt, we needn't
* do real work because EEH should freeze DMA transfers for those PCI
* devices encountering EEH errors, which includes MSI or MSI-X.
*/
*/
static
void
eeh_disable_irq
(
struct
pci_dev
*
dev
)
static
void
eeh_disable_irq
(
struct
pci_dev
*
dev
)
{
{
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
struct
eeh_dev
*
edev
=
pci_dev_to_eeh_dev
(
dev
);
/* Don't disable MSI and MSI-X interrupts. They are
/* Don't disable MSI and MSI-X interrupts. They are
* effectively disabled by the DMA Stopped state
* effectively disabled by the DMA Stopped state
* when an EEH error occurs.
* when an EEH error occurs.
*/
*/
if
(
dev
->
msi_enabled
||
dev
->
msix_enabled
)
if
(
dev
->
msi_enabled
||
dev
->
msix_enabled
)
return
;
return
;
if
(
!
irq_has_action
(
dev
->
irq
))
if
(
!
irq_has_action
(
dev
->
irq
))
return
;
return
;
PCI_DN
(
dn
)
->
eeh_
mode
|=
EEH_MODE_IRQ_DISABLED
;
edev
->
mode
|=
EEH_MODE_IRQ_DISABLED
;
disable_irq_nosync
(
dev
->
irq
);
disable_irq_nosync
(
dev
->
irq
);
}
}
/**
/**
* eeh_enable_irq - enable interrupt for the recovering device
* eeh_enable_irq - Enable interrupt for the recovering device
* @dev: PCI device
*
* This routine must be called to enable interrupt while failed
* device could be resumed.
*/
*/
static
void
eeh_enable_irq
(
struct
pci_dev
*
dev
)
static
void
eeh_enable_irq
(
struct
pci_dev
*
dev
)
{
{
struct
device_node
*
dn
=
pci_device_to_OF_node
(
dev
);
struct
eeh_dev
*
edev
=
pci_dev_to_eeh_dev
(
dev
);
if
((
PCI_DN
(
dn
)
->
eeh_
mode
)
&
EEH_MODE_IRQ_DISABLED
)
{
if
((
edev
->
mode
)
&
EEH_MODE_IRQ_DISABLED
)
{
PCI_DN
(
dn
)
->
eeh_
mode
&=
~
EEH_MODE_IRQ_DISABLED
;
edev
->
mode
&=
~
EEH_MODE_IRQ_DISABLED
;
enable_irq
(
dev
->
irq
);
enable_irq
(
dev
->
irq
);
}
}
}
}
/* ------------------------------------------------------- */
/**
/**
* eeh_report_error - report pci error to each device driver
* eeh_report_error - Report pci error to each device driver
* @dev: PCI device
* @userdata: return value
*
*
* Report an EEH error to each device driver, collect up and
* Report an EEH error to each device driver, collect up and
* merge the device driver responses. Cumulative response
* merge the device driver responses. Cumulative response
* passed back in "userdata".
* passed back in "userdata".
*/
*/
static
int
eeh_report_error
(
struct
pci_dev
*
dev
,
void
*
userdata
)
static
int
eeh_report_error
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
{
enum
pci_ers_result
rc
,
*
res
=
userdata
;
enum
pci_ers_result
rc
,
*
res
=
userdata
;
...
@@ -122,7 +139,7 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata)
...
@@ -122,7 +139,7 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata)
!
driver
->
err_handler
->
error_detected
)
!
driver
->
err_handler
->
error_detected
)
return
0
;
return
0
;
rc
=
driver
->
err_handler
->
error_detected
(
dev
,
pci_channel_io_frozen
);
rc
=
driver
->
err_handler
->
error_detected
(
dev
,
pci_channel_io_frozen
);
/* A driver that needs a reset trumps all others */
/* A driver that needs a reset trumps all others */
if
(
rc
==
PCI_ERS_RESULT_NEED_RESET
)
*
res
=
rc
;
if
(
rc
==
PCI_ERS_RESULT_NEED_RESET
)
*
res
=
rc
;
...
@@ -132,13 +149,14 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata)
...
@@ -132,13 +149,14 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata)
}
}
/**
/**
* eeh_report_mmio_enabled - tell drivers that MMIO has been enabled
* eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
* @dev: PCI device
* @userdata: return value
*
*
* Tells each device driver that IO ports, MMIO and config space I/O
* Tells each device driver that IO ports, MMIO and config space I/O
* are now enabled. Collects up and merges the device driver responses.
* are now enabled. Collects up and merges the device driver responses.
* Cumulative response passed back in "userdata".
* Cumulative response passed back in "userdata".
*/
*/
static
int
eeh_report_mmio_enabled
(
struct
pci_dev
*
dev
,
void
*
userdata
)
static
int
eeh_report_mmio_enabled
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
{
enum
pci_ers_result
rc
,
*
res
=
userdata
;
enum
pci_ers_result
rc
,
*
res
=
userdata
;
...
@@ -149,7 +167,7 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
...
@@ -149,7 +167,7 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
!
driver
->
err_handler
->
mmio_enabled
)
!
driver
->
err_handler
->
mmio_enabled
)
return
0
;
return
0
;
rc
=
driver
->
err_handler
->
mmio_enabled
(
dev
);
rc
=
driver
->
err_handler
->
mmio_enabled
(
dev
);
/* A driver that needs a reset trumps all others */
/* A driver that needs a reset trumps all others */
if
(
rc
==
PCI_ERS_RESULT_NEED_RESET
)
*
res
=
rc
;
if
(
rc
==
PCI_ERS_RESULT_NEED_RESET
)
*
res
=
rc
;
...
@@ -159,9 +177,15 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
...
@@ -159,9 +177,15 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata)
}
}
/**
/**
* eeh_report_reset - tell device that slot has been reset
* eeh_report_reset - Tell device that slot has been reset
* @dev: PCI device
* @userdata: return value
*
* This routine must be called while EEH tries to reset particular
* PCI device so that the associated PCI device driver could take
* some actions, usually to save data the driver needs so that the
* driver can work again while the device is recovered.
*/
*/
static
int
eeh_report_reset
(
struct
pci_dev
*
dev
,
void
*
userdata
)
static
int
eeh_report_reset
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
{
enum
pci_ers_result
rc
,
*
res
=
userdata
;
enum
pci_ers_result
rc
,
*
res
=
userdata
;
...
@@ -188,9 +212,14 @@ static int eeh_report_reset(struct pci_dev *dev, void *userdata)
...
@@ -188,9 +212,14 @@ static int eeh_report_reset(struct pci_dev *dev, void *userdata)
}
}
/**
/**
* eeh_report_resume - tell device to resume normal operations
* eeh_report_resume - Tell device to resume normal operations
* @dev: PCI device
* @userdata: return value
*
* This routine must be called to notify the device driver that it
* could resume so that the device driver can do some initialization
* to make the recovered device work again.
*/
*/
static
int
eeh_report_resume
(
struct
pci_dev
*
dev
,
void
*
userdata
)
static
int
eeh_report_resume
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
{
struct
pci_driver
*
driver
=
dev
->
driver
;
struct
pci_driver
*
driver
=
dev
->
driver
;
...
@@ -212,12 +241,13 @@ static int eeh_report_resume(struct pci_dev *dev, void *userdata)
...
@@ -212,12 +241,13 @@ static int eeh_report_resume(struct pci_dev *dev, void *userdata)
}
}
/**
/**
* eeh_report_failure - tell device driver that device is dead.
* eeh_report_failure - Tell device driver that device is dead.
* @dev: PCI device
* @userdata: return value
*
*
* This informs the device driver that the device is permanently
* This informs the device driver that the device is permanently
* dead, and that no further recovery attempts will be made on it.
* dead, and that no further recovery attempts will be made on it.
*/
*/
static
int
eeh_report_failure
(
struct
pci_dev
*
dev
,
void
*
userdata
)
static
int
eeh_report_failure
(
struct
pci_dev
*
dev
,
void
*
userdata
)
{
{
struct
pci_driver
*
driver
=
dev
->
driver
;
struct
pci_driver
*
driver
=
dev
->
driver
;
...
@@ -238,65 +268,46 @@ static int eeh_report_failure(struct pci_dev *dev, void *userdata)
...
@@ -238,65 +268,46 @@ static int eeh_report_failure(struct pci_dev *dev, void *userdata)
return
0
;
return
0
;
}
}
/* ------------------------------------------------------- */
/**
/**
* handle_eeh_events -- reset a PCI device after hard lockup.
* eeh_reset_device - Perform actual reset of a pci slot
*
* @edev: PE associated EEH device
* pSeries systems will isolate a PCI slot if the PCI-Host
* @bus: PCI bus corresponding to the isolcated slot
* bridge detects address or data parity errors, DMA's
* occurring to wild addresses (which usually happen due to
* bugs in device drivers or in PCI adapter firmware).
* Slot isolations also occur if #SERR, #PERR or other misc
* PCI-related errors are detected.
*
*
* Recovery process consists of unplugging the device driver
* This routine must be called to do reset on the indicated PE.
* (which generated hotplug events to userspace), then issuing
* During the reset, udev might be invoked because those affected
* a PCI #RST to the device, then reconfiguring the PCI config
* PCI devices will be removed and then added.
* space for all bridges & devices under this slot, and then
* finally restarting the device drivers (which cause a second
* set of hotplug events to go out to userspace).
*/
*/
static
int
eeh_reset_device
(
struct
eeh_dev
*
edev
,
struct
pci_bus
*
bus
)
/**
* eeh_reset_device() -- perform actual reset of a pci slot
* @bus: pointer to the pci bus structure corresponding
* to the isolated slot. A non-null value will
* cause all devices under the bus to be removed
* and then re-added.
* @pe_dn: pointer to a "Partionable Endpoint" device node.
* This is the top-level structure on which pci
* bus resets can be performed.
*/
static
int
eeh_reset_device
(
struct
pci_dn
*
pe_dn
,
struct
pci_bus
*
bus
)
{
{
struct
device_node
*
dn
;
struct
device_node
*
dn
;
int
cnt
,
rc
;
int
cnt
,
rc
;
/* pcibios will clear the counter; save the value */
/* pcibios will clear the counter; save the value */
cnt
=
pe_dn
->
eeh_
freeze_count
;
cnt
=
edev
->
freeze_count
;
if
(
bus
)
if
(
bus
)
pcibios_remove_pci_devices
(
bus
);
pcibios_remove_pci_devices
(
bus
);
/* Reset the pci controller. (Asserts RST#; resets config space).
/* Reset the pci controller. (Asserts RST#; resets config space).
* Reconfigure bridges and devices. Don't try to bring the system
* Reconfigure bridges and devices. Don't try to bring the system
* up if the reset failed for some reason. */
* up if the reset failed for some reason.
rc
=
rtas_set_slot_reset
(
pe_dn
);
*/
rc
=
eeh_reset_pe
(
edev
);
if
(
rc
)
if
(
rc
)
return
rc
;
return
rc
;
/* Walk over all functions on this device.
*/
/* Walk over all functions on this device. */
dn
=
pe_dn
->
node
;
dn
=
eeh_dev_to_of_node
(
edev
)
;
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
PCI_DN
(
dn
->
parent
))
if
(
!
pcibios_find_pci_bus
(
dn
)
&&
of_node_to_eeh_dev
(
dn
->
parent
))
dn
=
dn
->
parent
->
child
;
dn
=
dn
->
parent
->
child
;
while
(
dn
)
{
while
(
dn
)
{
struct
pci_dn
*
ppe
=
PCI_DN
(
dn
);
struct
eeh_dev
*
pedev
=
of_node_to_eeh_dev
(
dn
);
/* On Power4, always true because eeh_pe_config_addr=0 */
/* On Power4, always true because eeh_pe_config_addr=0 */
if
(
pe_dn
->
eeh_pe_config_addr
==
ppe
->
eeh_
pe_config_addr
)
{
if
(
edev
->
pe_config_addr
==
pedev
->
pe_config_addr
)
{
rtas_configure_bridge
(
ppe
);
eeh_ops
->
configure_bridge
(
dn
);
eeh_restore_bars
(
p
pe
);
eeh_restore_bars
(
p
edev
);
}
}
dn
=
dn
->
sibling
;
dn
=
dn
->
sibling
;
}
}
...
@@ -308,10 +319,10 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus)
...
@@ -308,10 +319,10 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus)
* potentially weird things happen.
* potentially weird things happen.
*/
*/
if
(
bus
)
{
if
(
bus
)
{
ssleep
(
5
);
ssleep
(
5
);
pcibios_add_pci_devices
(
bus
);
pcibios_add_pci_devices
(
bus
);
}
}
pe_dn
->
eeh_
freeze_count
=
cnt
;
edev
->
freeze_count
=
cnt
;
return
0
;
return
0
;
}
}
...
@@ -321,23 +332,39 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus)
...
@@ -321,23 +332,39 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus)
*/
*/
#define MAX_WAIT_FOR_RECOVERY 150
#define MAX_WAIT_FOR_RECOVERY 150
struct
pci_dn
*
handle_eeh_events
(
struct
eeh_event
*
event
)
/**
* eeh_handle_event - Reset a PCI device after hard lockup.
* @event: EEH event
*
* While PHB detects address or data parity errors on particular PCI
* slot, the associated PE will be frozen. Besides, DMA's occurring
* to wild addresses (which usually happen due to bugs in device
* drivers or in PCI adapter firmware) can cause EEH error. #SERR,
* #PERR or other misc PCI-related errors also can trigger EEH errors.
*
* Recovery process consists of unplugging the device driver (which
* generated hotplug events to userspace), then issuing a PCI #RST to
* the device, then reconfiguring the PCI config space for all bridges
* & devices under this slot, and then finally restarting the device
* drivers (which cause a second set of hotplug events to go out to
* userspace).
*/
struct
eeh_dev
*
handle_eeh_events
(
struct
eeh_event
*
event
)
{
{
struct
device_node
*
frozen_dn
;
struct
device_node
*
frozen_dn
;
struct
pci_dn
*
frozen_pdn
;
struct
eeh_dev
*
frozen_edev
;
struct
pci_bus
*
frozen_bus
;
struct
pci_bus
*
frozen_bus
;
int
rc
=
0
;
int
rc
=
0
;
enum
pci_ers_result
result
=
PCI_ERS_RESULT_NONE
;
enum
pci_ers_result
result
=
PCI_ERS_RESULT_NONE
;
const
char
*
location
,
*
pci_str
,
*
drv_str
,
*
bus_pci_str
,
*
bus_drv_str
;
const
char
*
location
,
*
pci_str
,
*
drv_str
,
*
bus_pci_str
,
*
bus_drv_str
;
frozen_dn
=
find_device_pe
(
event
->
dn
);
frozen_dn
=
eeh_find_device_pe
(
eeh_dev_to_of_node
(
event
->
edev
)
);
if
(
!
frozen_dn
)
{
if
(
!
frozen_dn
)
{
location
=
of_get_property
(
eeh_dev_to_of_node
(
event
->
edev
),
"ibm,loc-code"
,
NULL
);
location
=
of_get_property
(
event
->
dn
,
"ibm,loc-code"
,
NULL
);
location
=
location
?
location
:
"unknown"
;
location
=
location
?
location
:
"unknown"
;
printk
(
KERN_ERR
"EEH: Error: Cannot find partition endpoint "
printk
(
KERN_ERR
"EEH: Error: Cannot find partition endpoint "
"for location=%s pci addr=%s
\n
"
,
"for location=%s pci addr=%s
\n
"
,
location
,
eeh_pci_name
(
event
->
dev
));
location
,
eeh_pci_name
(
eeh_dev_to_pci_dev
(
event
->
edev
)
));
return
NULL
;
return
NULL
;
}
}
...
@@ -350,9 +377,10 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -350,9 +377,10 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
* which was always an EADS pci bridge. In the new style,
* which was always an EADS pci bridge. In the new style,
* there might not be any EADS bridges, and even when there are,
* there might not be any EADS bridges, and even when there are,
* the firmware marks them as "EEH incapable". So another
* the firmware marks them as "EEH incapable". So another
* two-step is needed to find the pci bus.. */
* two-step is needed to find the pci bus..
*/
if
(
!
frozen_bus
)
if
(
!
frozen_bus
)
frozen_bus
=
pcibios_find_pci_bus
(
frozen_dn
->
parent
);
frozen_bus
=
pcibios_find_pci_bus
(
frozen_dn
->
parent
);
if
(
!
frozen_bus
)
{
if
(
!
frozen_bus
)
{
printk
(
KERN_ERR
"EEH: Cannot find PCI bus "
printk
(
KERN_ERR
"EEH: Cannot find PCI bus "
...
@@ -361,22 +389,21 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -361,22 +389,21 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
return
NULL
;
return
NULL
;
}
}
frozen_pdn
=
PCI_DN
(
frozen_dn
);
frozen_edev
=
of_node_to_eeh_dev
(
frozen_dn
);
frozen_pdn
->
eeh_freeze_count
++
;
frozen_edev
->
freeze_count
++
;
pci_str
=
eeh_pci_name
(
eeh_dev_to_pci_dev
(
event
->
edev
));
drv_str
=
eeh_pcid_name
(
eeh_dev_to_pci_dev
(
event
->
edev
));
pci_str
=
eeh_pci_name
(
event
->
dev
);
if
(
frozen_edev
->
freeze_count
>
EEH_MAX_ALLOWED_FREEZES
)
drv_str
=
pcid_name
(
event
->
dev
);
if
(
frozen_pdn
->
eeh_freeze_count
>
EEH_MAX_ALLOWED_FREEZES
)
goto
excess_failures
;
goto
excess_failures
;
printk
(
KERN_WARNING
printk
(
KERN_WARNING
"EEH: This PCI device has failed %d times in the last hour:
\n
"
,
"EEH: This PCI device has failed %d times in the last hour:
\n
"
,
frozen_
pdn
->
eeh_
freeze_count
);
frozen_
edev
->
freeze_count
);
if
(
frozen_
pdn
->
pci
dev
)
{
if
(
frozen_
edev
->
p
dev
)
{
bus_pci_str
=
pci_name
(
frozen_
pdn
->
pci
dev
);
bus_pci_str
=
pci_name
(
frozen_
edev
->
p
dev
);
bus_drv_str
=
pcid_name
(
frozen_pdn
->
pci
dev
);
bus_drv_str
=
eeh_pcid_name
(
frozen_edev
->
p
dev
);
printk
(
KERN_WARNING
printk
(
KERN_WARNING
"EEH: Bus location=%s driver=%s pci addr=%s
\n
"
,
"EEH: Bus location=%s driver=%s pci addr=%s
\n
"
,
location
,
bus_drv_str
,
bus_pci_str
);
location
,
bus_drv_str
,
bus_pci_str
);
...
@@ -395,9 +422,10 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -395,9 +422,10 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
pci_walk_bus
(
frozen_bus
,
eeh_report_error
,
&
result
);
pci_walk_bus
(
frozen_bus
,
eeh_report_error
,
&
result
);
/* Get the current PCI slot state. This can take a long time,
/* Get the current PCI slot state. This can take a long time,
* sometimes over 3 seconds for certain systems. */
* sometimes over 3 seconds for certain systems.
rc
=
eeh_wait_for_slot_status
(
frozen_pdn
,
MAX_WAIT_FOR_RECOVERY
*
1000
);
*/
if
(
rc
<
0
)
{
rc
=
eeh_ops
->
wait_state
(
eeh_dev_to_of_node
(
frozen_edev
),
MAX_WAIT_FOR_RECOVERY
*
1000
);
if
(
rc
<
0
||
rc
==
EEH_STATE_NOT_SUPPORT
)
{
printk
(
KERN_WARNING
"EEH: Permanent failure
\n
"
);
printk
(
KERN_WARNING
"EEH: Permanent failure
\n
"
);
goto
hard_fail
;
goto
hard_fail
;
}
}
...
@@ -406,14 +434,14 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -406,14 +434,14 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
* don't post the error log until after all dev drivers
* don't post the error log until after all dev drivers
* have been informed.
* have been informed.
*/
*/
eeh_slot_error_detail
(
frozen_
pdn
,
EEH_LOG_TEMP_FAILURE
);
eeh_slot_error_detail
(
frozen_
edev
,
EEH_LOG_TEMP
);
/* If all device drivers were EEH-unaware, then shut
/* If all device drivers were EEH-unaware, then shut
* down all of the device drivers, and hope they
* down all of the device drivers, and hope they
* go down willingly, without panicing the system.
* go down willingly, without panicing the system.
*/
*/
if
(
result
==
PCI_ERS_RESULT_NONE
)
{
if
(
result
==
PCI_ERS_RESULT_NONE
)
{
rc
=
eeh_reset_device
(
frozen_
pdn
,
frozen_bus
);
rc
=
eeh_reset_device
(
frozen_
edev
,
frozen_bus
);
if
(
rc
)
{
if
(
rc
)
{
printk
(
KERN_WARNING
"EEH: Unable to reset, rc=%d
\n
"
,
rc
);
printk
(
KERN_WARNING
"EEH: Unable to reset, rc=%d
\n
"
,
rc
);
goto
hard_fail
;
goto
hard_fail
;
...
@@ -422,7 +450,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -422,7 +450,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
/* If all devices reported they can proceed, then re-enable MMIO */
/* If all devices reported they can proceed, then re-enable MMIO */
if
(
result
==
PCI_ERS_RESULT_CAN_RECOVER
)
{
if
(
result
==
PCI_ERS_RESULT_CAN_RECOVER
)
{
rc
=
rtas_pci_enable
(
frozen_pdn
,
EEH
_THAW_MMIO
);
rc
=
eeh_pci_enable
(
frozen_edev
,
EEH_OPT
_THAW_MMIO
);
if
(
rc
<
0
)
if
(
rc
<
0
)
goto
hard_fail
;
goto
hard_fail
;
...
@@ -436,7 +464,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -436,7 +464,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
/* If all devices reported they can proceed, then re-enable DMA */
/* If all devices reported they can proceed, then re-enable DMA */
if
(
result
==
PCI_ERS_RESULT_CAN_RECOVER
)
{
if
(
result
==
PCI_ERS_RESULT_CAN_RECOVER
)
{
rc
=
rtas_pci_enable
(
frozen_pdn
,
EEH
_THAW_DMA
);
rc
=
eeh_pci_enable
(
frozen_edev
,
EEH_OPT
_THAW_DMA
);
if
(
rc
<
0
)
if
(
rc
<
0
)
goto
hard_fail
;
goto
hard_fail
;
...
@@ -454,7 +482,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -454,7 +482,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
/* If any device called out for a reset, then reset the slot */
/* If any device called out for a reset, then reset the slot */
if
(
result
==
PCI_ERS_RESULT_NEED_RESET
)
{
if
(
result
==
PCI_ERS_RESULT_NEED_RESET
)
{
rc
=
eeh_reset_device
(
frozen_
pdn
,
NULL
);
rc
=
eeh_reset_device
(
frozen_
edev
,
NULL
);
if
(
rc
)
{
if
(
rc
)
{
printk
(
KERN_WARNING
"EEH: Cannot reset, rc=%d
\n
"
,
rc
);
printk
(
KERN_WARNING
"EEH: Cannot reset, rc=%d
\n
"
,
rc
);
goto
hard_fail
;
goto
hard_fail
;
...
@@ -473,7 +501,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -473,7 +501,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
/* Tell all device drivers that they can resume operations */
/* Tell all device drivers that they can resume operations */
pci_walk_bus
(
frozen_bus
,
eeh_report_resume
,
NULL
);
pci_walk_bus
(
frozen_bus
,
eeh_report_resume
,
NULL
);
return
frozen_
pdn
;
return
frozen_
edev
;
excess_failures:
excess_failures:
/*
/*
...
@@ -486,7 +514,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -486,7 +514,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
"has failed %d times in the last hour "
"has failed %d times in the last hour "
"and has been permanently disabled.
\n
"
"and has been permanently disabled.
\n
"
"Please try reseating this device or replacing it.
\n
"
,
"Please try reseating this device or replacing it.
\n
"
,
location
,
drv_str
,
pci_str
,
frozen_
pdn
->
eeh_
freeze_count
);
location
,
drv_str
,
pci_str
,
frozen_
edev
->
freeze_count
);
goto
perm_error
;
goto
perm_error
;
hard_fail:
hard_fail:
...
@@ -497,7 +525,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -497,7 +525,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
location
,
drv_str
,
pci_str
);
location
,
drv_str
,
pci_str
);
perm_error:
perm_error:
eeh_slot_error_detail
(
frozen_
pdn
,
EEH_LOG_PERM_FAILURE
);
eeh_slot_error_detail
(
frozen_
edev
,
EEH_LOG_PERM
);
/* Notify all devices that they're about to go down. */
/* Notify all devices that they're about to go down. */
pci_walk_bus
(
frozen_bus
,
eeh_report_failure
,
NULL
);
pci_walk_bus
(
frozen_bus
,
eeh_report_failure
,
NULL
);
...
@@ -508,4 +536,3 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
...
@@ -508,4 +536,3 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event)
return
NULL
;
return
NULL
;
}
}
/* ---------- end of file ---------- */
arch/powerpc/platforms/pseries/eeh_event.c
View file @
aba0eb84
/*
/*
* eeh_event.c
*
* This program is free software; you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation; either version 2 of the License, or
...
@@ -46,7 +44,7 @@ DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
...
@@ -46,7 +44,7 @@ DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
DEFINE_MUTEX
(
eeh_event_mutex
);
DEFINE_MUTEX
(
eeh_event_mutex
);
/**
/**
* eeh_event_handler -
d
ispatch EEH events.
* eeh_event_handler -
D
ispatch EEH events.
* @dummy - unused
* @dummy - unused
*
*
* The detection of a frozen slot can occur inside an interrupt,
* The detection of a frozen slot can occur inside an interrupt,
...
@@ -58,10 +56,10 @@ DEFINE_MUTEX(eeh_event_mutex);
...
@@ -58,10 +56,10 @@ DEFINE_MUTEX(eeh_event_mutex);
static
int
eeh_event_handler
(
void
*
dummy
)
static
int
eeh_event_handler
(
void
*
dummy
)
{
{
unsigned
long
flags
;
unsigned
long
flags
;
struct
eeh_event
*
event
;
struct
eeh_event
*
event
;
struct
pci_dn
*
pdn
;
struct
eeh_dev
*
edev
;
daemonize
(
"eehd"
);
daemonize
(
"eehd"
);
set_current_state
(
TASK_INTERRUPTIBLE
);
set_current_state
(
TASK_INTERRUPTIBLE
);
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
...
@@ -79,31 +77,37 @@ static int eeh_event_handler(void * dummy)
...
@@ -79,31 +77,37 @@ static int eeh_event_handler(void * dummy)
/* Serialize processing of EEH events */
/* Serialize processing of EEH events */
mutex_lock
(
&
eeh_event_mutex
);
mutex_lock
(
&
eeh_event_mutex
);
eeh_mark_slot
(
event
->
dn
,
EEH_MODE_RECOVERING
);
edev
=
event
->
edev
;
eeh_mark_slot
(
eeh_dev_to_of_node
(
edev
),
EEH_MODE_RECOVERING
);
printk
(
KERN_INFO
"EEH: Detected PCI bus error on device %s
\n
"
,
printk
(
KERN_INFO
"EEH: Detected PCI bus error on device %s
\n
"
,
eeh_pci_name
(
event
->
dev
));
eeh_pci_name
(
edev
->
pdev
));
edev
=
handle_eeh_events
(
event
);
pdn
=
handle_eeh_events
(
event
);
eeh_clear_slot
(
eeh_dev_to_of_node
(
edev
),
EEH_MODE_RECOVERING
);
pci_dev_put
(
edev
->
pdev
);
eeh_clear_slot
(
event
->
dn
,
EEH_MODE_RECOVERING
);
pci_dev_put
(
event
->
dev
);
kfree
(
event
);
kfree
(
event
);
mutex_unlock
(
&
eeh_event_mutex
);
mutex_unlock
(
&
eeh_event_mutex
);
/* If there are no new errors after an hour, clear the counter. */
/* If there are no new errors after an hour, clear the counter. */
if
(
pdn
&&
pdn
->
eeh_freeze_count
>
0
)
{
if
(
edev
&&
edev
->
freeze_count
>
0
)
{
msleep_interruptible
(
3600
*
1000
);
msleep_interruptible
(
3600
*
1000
);
if
(
pdn
->
eeh_freeze_count
>
0
)
if
(
edev
->
freeze_count
>
0
)
pdn
->
eeh_freeze_count
--
;
edev
->
freeze_count
--
;
}
}
return
0
;
return
0
;
}
}
/**
/**
* eeh_thread_launcher
* eeh_thread_launcher
- Start kernel thread to handle EEH events
* @dummy - unused
* @dummy - unused
*
* This routine is called to start the kernel thread for processing
* EEH event.
*/
*/
static
void
eeh_thread_launcher
(
struct
work_struct
*
dummy
)
static
void
eeh_thread_launcher
(
struct
work_struct
*
dummy
)
{
{
...
@@ -112,18 +116,18 @@ static void eeh_thread_launcher(struct work_struct *dummy)
...
@@ -112,18 +116,18 @@ static void eeh_thread_launcher(struct work_struct *dummy)
}
}
/**
/**
* eeh_send_failure_event -
g
enerate a PCI error event
* eeh_send_failure_event -
G
enerate a PCI error event
* @
dev pci
device
* @
edev: EEH
device
*
*
* This routine can be called within an interrupt context;
* This routine can be called within an interrupt context;
* the actual event will be delivered in a normal context
* the actual event will be delivered in a normal context
* (from a workqueue).
* (from a workqueue).
*/
*/
int
eeh_send_failure_event
(
struct
device_node
*
dn
,
int
eeh_send_failure_event
(
struct
eeh_dev
*
edev
)
struct
pci_dev
*
dev
)
{
{
unsigned
long
flags
;
unsigned
long
flags
;
struct
eeh_event
*
event
;
struct
eeh_event
*
event
;
struct
device_node
*
dn
=
eeh_dev_to_of_node
(
edev
);
const
char
*
location
;
const
char
*
location
;
if
(
!
mem_init_done
)
{
if
(
!
mem_init_done
)
{
...
@@ -135,15 +139,14 @@ int eeh_send_failure_event (struct device_node *dn,
...
@@ -135,15 +139,14 @@ int eeh_send_failure_event (struct device_node *dn,
}
}
event
=
kmalloc
(
sizeof
(
*
event
),
GFP_ATOMIC
);
event
=
kmalloc
(
sizeof
(
*
event
),
GFP_ATOMIC
);
if
(
event
==
NULL
)
{
if
(
event
==
NULL
)
{
printk
(
KERN_ERR
"EEH: out of memory, event not handled
\n
"
);
printk
(
KERN_ERR
"EEH: out of memory, event not handled
\n
"
);
return
1
;
return
1
;
}
}
if
(
dev
)
if
(
edev
->
p
dev
)
pci_dev_get
(
dev
);
pci_dev_get
(
edev
->
p
dev
);
event
->
dn
=
dn
;
event
->
edev
=
edev
;
event
->
dev
=
dev
;
/* We may or may not be called in an interrupt context */
/* We may or may not be called in an interrupt context */
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
spin_lock_irqsave
(
&
eeh_eventlist_lock
,
flags
);
...
@@ -154,5 +157,3 @@ int eeh_send_failure_event (struct device_node *dn,
...
@@ -154,5 +157,3 @@ int eeh_send_failure_event (struct device_node *dn,
return
0
;
return
0
;
}
}
/********************** END OF FILE ******************************/
arch/powerpc/platforms/pseries/eeh_pseries.c
0 → 100644
View file @
aba0eb84
/*
* 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
* are devired from arch/powerpc/platforms/pseries/eeh.c and necessary cleanup has
* 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>
/* 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_bridge
;
static
int
ibm_configure_pe
;
/*
* 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
;
/**
* pseries_eeh_init - EEH platform dependent initialization
*
* EEH platform dependent initialization on pseries.
*/
static
int
pseries_eeh_init
(
void
)
{
/* 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"
);
ibm_configure_bridge
=
rtas_token
(
"ibm,configure-bridge"
);
/* necessary sanity check */
if
(
ibm_set_eeh_option
==
RTAS_UNKNOWN_SERVICE
)
{
pr_warning
(
"%s: RTAS service <ibm,set-eeh-option> invalid
\n
"
,
__func__
);
return
-
EINVAL
;
}
else
if
(
ibm_set_slot_reset
==
RTAS_UNKNOWN_SERVICE
)
{
pr_warning
(
"%s: RTAS service <ibm, set-slot-reset> invalid
\n
"
,
__func__
);
return
-
EINVAL
;
}
else
if
(
ibm_read_slot_reset_state2
==
RTAS_UNKNOWN_SERVICE
&&
ibm_read_slot_reset_state
==
RTAS_UNKNOWN_SERVICE
)
{
pr_warning
(
"%s: RTAS service <ibm,read-slot-reset-state2> and "
"<ibm,read-slot-reset-state> invalid
\n
"
,
__func__
);
return
-
EINVAL
;
}
else
if
(
ibm_slot_error_detail
==
RTAS_UNKNOWN_SERVICE
)
{
pr_warning
(
"%s: RTAS service <ibm,slot-error-detail> invalid
\n
"
,
__func__
);
return
-
EINVAL
;
}
else
if
(
ibm_get_config_addr_info2
==
RTAS_UNKNOWN_SERVICE
&&
ibm_get_config_addr_info
==
RTAS_UNKNOWN_SERVICE
)
{
pr_warning
(
"%s: RTAS service <ibm,get-config-addr-info2> and "
"<ibm,get-config-addr-info> invalid
\n
"
,
__func__
);
return
-
EINVAL
;
}
else
if
(
ibm_configure_pe
==
RTAS_UNKNOWN_SERVICE
&&
ibm_configure_bridge
==
RTAS_UNKNOWN_SERVICE
)
{
pr_warning
(
"%s: RTAS service <ibm,configure-pe> and "
"<ibm,configure-bridge> invalid
\n
"
,
__func__
);
return
-
EINVAL
;
}
/* 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
)
{
pr_warning
(
"%s: unknown EEH error log size
\n
"
,
__func__
);
eeh_error_buf_size
=
1024
;
}
else
if
(
eeh_error_buf_size
>
RTAS_ERROR_LOG_MAX
)
{
pr_warning
(
"%s: EEH error log size %d exceeds the maximal %d
\n
"
,
__func__
,
eeh_error_buf_size
,
RTAS_ERROR_LOG_MAX
);
eeh_error_buf_size
=
RTAS_ERROR_LOG_MAX
;
}
return
0
;
}
/**
* pseries_eeh_set_option - Initialize EEH or MMIO/DMA reenable
* @dn: device node
* @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
*/
static
int
pseries_eeh_set_option
(
struct
device_node
*
dn
,
int
option
)
{
int
ret
=
0
;
struct
eeh_dev
*
edev
;
const
u32
*
reg
;
int
config_addr
;
edev
=
of_node_to_eeh_dev
(
dn
);
/*
* 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
:
reg
=
of_get_property
(
dn
,
"reg"
,
NULL
);
config_addr
=
reg
[
0
];
break
;
case
EEH_OPT_THAW_MMIO
:
case
EEH_OPT_THAW_DMA
:
config_addr
=
edev
->
config_addr
;
if
(
edev
->
pe_config_addr
)
config_addr
=
edev
->
pe_config_addr
;
break
;
default:
pr_err
(
"%s: Invalid option %d
\n
"
,
__func__
,
option
);
return
-
EINVAL
;
}
ret
=
rtas_call
(
ibm_set_eeh_option
,
4
,
1
,
NULL
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
option
);
return
ret
;
}
/**
* pseries_eeh_get_pe_addr - Retrieve PE address
* @dn: device node
*
* 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.
*/
static
int
pseries_eeh_get_pe_addr
(
struct
device_node
*
dn
)
{
struct
eeh_dev
*
edev
;
int
ret
=
0
;
int
rets
[
3
];
edev
=
of_node_to_eeh_dev
(
dn
);
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
,
edev
->
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
1
);
if
(
ret
||
(
rets
[
0
]
==
0
))
return
0
;
/* Retrieve the associated PE config address */
ret
=
rtas_call
(
ibm_get_config_addr_info2
,
4
,
2
,
rets
,
edev
->
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
0
);
if
(
ret
)
{
pr_warning
(
"%s: Failed to get PE address for %s
\n
"
,
__func__
,
dn
->
full_name
);
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
,
edev
->
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
0
);
if
(
ret
)
{
pr_warning
(
"%s: Failed to get PE address for %s
\n
"
,
__func__
,
dn
->
full_name
);
return
0
;
}
return
rets
[
0
];
}
return
ret
;
}
/**
* pseries_eeh_get_state - Retrieve PE state
* @dn: PE associated device node
* @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.
*/
static
int
pseries_eeh_get_state
(
struct
device_node
*
dn
,
int
*
state
)
{
struct
eeh_dev
*
edev
;
int
config_addr
;
int
ret
;
int
rets
[
4
];
int
result
;
/* Figure out PE config address if possible */
edev
=
of_node_to_eeh_dev
(
dn
);
config_addr
=
edev
->
config_addr
;
if
(
edev
->
pe_config_addr
)
config_addr
=
edev
->
pe_config_addr
;
if
(
ibm_read_slot_reset_state2
!=
RTAS_UNKNOWN_SERVICE
)
{
ret
=
rtas_call
(
ibm_read_slot_reset_state2
,
3
,
4
,
rets
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
));
}
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
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
));
}
else
{
return
EEH_STATE_NOT_SUPPORT
;
}
if
(
ret
)
return
ret
;
/* Parse the result out */
result
=
0
;
if
(
rets
[
1
])
{
switch
(
rets
[
0
])
{
case
0
:
result
&=
~
EEH_STATE_RESET_ACTIVE
;
result
|=
EEH_STATE_MMIO_ACTIVE
;
result
|=
EEH_STATE_DMA_ACTIVE
;
break
;
case
1
:
result
|=
EEH_STATE_RESET_ACTIVE
;
result
|=
EEH_STATE_MMIO_ACTIVE
;
result
|=
EEH_STATE_DMA_ACTIVE
;
break
;
case
2
:
result
&=
~
EEH_STATE_RESET_ACTIVE
;
result
&=
~
EEH_STATE_MMIO_ACTIVE
;
result
&=
~
EEH_STATE_DMA_ACTIVE
;
break
;
case
4
:
result
&=
~
EEH_STATE_RESET_ACTIVE
;
result
&=
~
EEH_STATE_MMIO_ACTIVE
;
result
&=
~
EEH_STATE_DMA_ACTIVE
;
result
|=
EEH_STATE_MMIO_ENABLED
;
break
;
case
5
:
if
(
rets
[
2
])
{
if
(
state
)
*
state
=
rets
[
2
];
result
=
EEH_STATE_UNAVAILABLE
;
}
else
{
result
=
EEH_STATE_NOT_SUPPORT
;
}
default:
result
=
EEH_STATE_NOT_SUPPORT
;
}
}
else
{
result
=
EEH_STATE_NOT_SUPPORT
;
}
return
result
;
}
/**
* pseries_eeh_reset - Reset the specified PE
* @dn: PE associated device node
* @option: reset option
*
* Reset the specified PE
*/
static
int
pseries_eeh_reset
(
struct
device_node
*
dn
,
int
option
)
{
struct
eeh_dev
*
edev
;
int
config_addr
;
int
ret
;
/* Figure out PE address */
edev
=
of_node_to_eeh_dev
(
dn
);
config_addr
=
edev
->
config_addr
;
if
(
edev
->
pe_config_addr
)
config_addr
=
edev
->
pe_config_addr
;
/* Reset PE through RTAS call */
ret
=
rtas_call
(
ibm_set_slot_reset
,
4
,
1
,
NULL
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
option
);
/* If fundamental-reset not supported, try hot-reset */
if
(
option
==
EEH_RESET_FUNDAMENTAL
&&
ret
==
-
8
)
{
ret
=
rtas_call
(
ibm_set_slot_reset
,
4
,
1
,
NULL
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
EEH_RESET_HOT
);
}
return
ret
;
}
/**
* pseries_eeh_wait_state - Wait for PE state
* @dn: PE associated device node
* @max_wait: maximal period in microsecond
*
* Wait for the state of associated PE. It might take some time
* to retrieve the PE's state.
*/
static
int
pseries_eeh_wait_state
(
struct
device_node
*
dn
,
int
max_wait
)
{
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
)
{
ret
=
pseries_eeh_get_state
(
dn
,
&
mwait
);
/*
* 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
)
{
pr_warning
(
"%s: Timeout when getting PE's state (%d)
\n
"
,
__func__
,
max_wait
);
return
EEH_STATE_NOT_SUPPORT
;
}
if
(
mwait
<=
0
)
{
pr_warning
(
"%s: Firmware returned bad wait value %d
\n
"
,
__func__
,
mwait
);
mwait
=
EEH_STATE_MIN_WAIT_TIME
;
}
else
if
(
mwait
>
EEH_STATE_MAX_WAIT_TIME
)
{
pr_warning
(
"%s: Firmware returned too long wait value %d
\n
"
,
__func__
,
mwait
);
mwait
=
EEH_STATE_MAX_WAIT_TIME
;
}
max_wait
-=
mwait
;
msleep
(
mwait
);
}
return
EEH_STATE_NOT_SUPPORT
;
}
/**
* pseries_eeh_get_log - Retrieve error log
* @dn: device node
* @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.
*/
static
int
pseries_eeh_get_log
(
struct
device_node
*
dn
,
int
severity
,
char
*
drv_log
,
unsigned
long
len
)
{
struct
eeh_dev
*
edev
;
int
config_addr
;
unsigned
long
flags
;
int
ret
;
edev
=
of_node_to_eeh_dev
(
dn
);
spin_lock_irqsave
(
&
slot_errbuf_lock
,
flags
);
memset
(
slot_errbuf
,
0
,
eeh_error_buf_size
);
/* Figure out the PE address */
config_addr
=
edev
->
config_addr
;
if
(
edev
->
pe_config_addr
)
config_addr
=
edev
->
pe_config_addr
;
ret
=
rtas_call
(
ibm_slot_error_detail
,
8
,
1
,
NULL
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
),
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
;
}
/**
* pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE
* @dn: PE associated device node
*
* The function will be called to reconfigure the bridges included
* in the specified PE so that the mulfunctional PE would be recovered
* again.
*/
static
int
pseries_eeh_configure_bridge
(
struct
device_node
*
dn
)
{
struct
eeh_dev
*
edev
;
int
config_addr
;
int
ret
;
/* Figure out the PE address */
edev
=
of_node_to_eeh_dev
(
dn
);
config_addr
=
edev
->
config_addr
;
if
(
edev
->
pe_config_addr
)
config_addr
=
edev
->
pe_config_addr
;
/* Use new configure-pe function, if supported */
if
(
ibm_configure_pe
!=
RTAS_UNKNOWN_SERVICE
)
{
ret
=
rtas_call
(
ibm_configure_pe
,
3
,
1
,
NULL
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
));
}
else
if
(
ibm_configure_bridge
!=
RTAS_UNKNOWN_SERVICE
)
{
ret
=
rtas_call
(
ibm_configure_bridge
,
3
,
1
,
NULL
,
config_addr
,
BUID_HI
(
edev
->
phb
->
buid
),
BUID_LO
(
edev
->
phb
->
buid
));
}
else
{
return
-
EFAULT
;
}
if
(
ret
)
pr_warning
(
"%s: Unable to configure bridge %d for %s
\n
"
,
__func__
,
ret
,
dn
->
full_name
);
return
ret
;
}
/**
* pseries_eeh_read_config - Read PCI config space
* @dn: device node
* @where: PCI address
* @size: size to read
* @val: return value
*
* Read config space from the speicifed device
*/
static
int
pseries_eeh_read_config
(
struct
device_node
*
dn
,
int
where
,
int
size
,
u32
*
val
)
{
struct
pci_dn
*
pdn
;
pdn
=
PCI_DN
(
dn
);
return
rtas_read_config
(
pdn
,
where
,
size
,
val
);
}
/**
* pseries_eeh_write_config - Write PCI config space
* @dn: device node
* @where: PCI address
* @size: size to write
* @val: value to be written
*
* Write config space to the specified device
*/
static
int
pseries_eeh_write_config
(
struct
device_node
*
dn
,
int
where
,
int
size
,
u32
val
)
{
struct
pci_dn
*
pdn
;
pdn
=
PCI_DN
(
dn
);
return
rtas_write_config
(
pdn
,
where
,
size
,
val
);
}
static
struct
eeh_ops
pseries_eeh_ops
=
{
.
name
=
"pseries"
,
.
init
=
pseries_eeh_init
,
.
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
,
.
configure_bridge
=
pseries_eeh_configure_bridge
,
.
read_config
=
pseries_eeh_read_config
,
.
write_config
=
pseries_eeh_write_config
};
/**
* eeh_pseries_init - Register platform dependent EEH operations
*
* EEH initialization on pseries platform. This function should be
* called before any EEH related functions.
*/
int
__init
eeh_pseries_init
(
void
)
{
return
eeh_ops_register
(
&
pseries_eeh_ops
);
}
arch/powerpc/platforms/pseries/eeh_sysfs.c
View file @
aba0eb84
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
#include <asm/pci-bridge.h>
#include <asm/pci-bridge.h>
/**
/**
* EEH_SHOW_ATTR --
c
reate sysfs entry for eeh statistic
* EEH_SHOW_ATTR --
C
reate sysfs entry for eeh statistic
* @_name: name of file in sysfs directory
* @_name: name of file in sysfs directory
* @_memb: name of member in struct pci_dn to access
* @_memb: name of member in struct pci_dn to access
* @_format: printf format for display
* @_format: printf format for display
...
@@ -41,24 +41,21 @@ static ssize_t eeh_show_##_name(struct device *dev, \
...
@@ -41,24 +41,21 @@ static ssize_t eeh_show_##_name(struct device *dev, \
struct device_attribute *attr, char *buf) \
struct device_attribute *attr, char *buf) \
{ \
{ \
struct pci_dev *pdev = to_pci_dev(dev); \
struct pci_dev *pdev = to_pci_dev(dev); \
struct device_node *dn = pci_device_to_OF_node(pdev); \
struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \
struct pci_dn *pdn; \
\
\
if (!
dn || PCI_DN(dn) == NULL)
\
if (!
edev)
\
return 0;
\
return 0; \
\
\
pdn = PCI_DN(dn); \
return sprintf(buf, _format "\n", edev->_memb); \
return sprintf(buf, _format "\n", pdn->_memb); \
} \
} \
static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
EEH_SHOW_ATTR
(
eeh_mode
,
mode
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_mode
,
eeh_mode
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_config_addr
,
config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_config_addr
,
eeh_config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_pe_config_addr
,
pe_config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_pe_config_addr
,
eeh_pe_config_addr
,
"0x%x"
);
EEH_SHOW_ATTR
(
eeh_check_count
,
check_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_check_count
,
eeh_check_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_freeze_count
,
freeze_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_freeze_count
,
eeh_freeze_count
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_false_positives
,
false_positives
,
"%d"
);
EEH_SHOW_ATTR
(
eeh_false_positives
,
eeh_false_positives
,
"%d"
);
void
eeh_sysfs_add_device
(
struct
pci_dev
*
pdev
)
void
eeh_sysfs_add_device
(
struct
pci_dev
*
pdev
)
{
{
...
...
arch/powerpc/platforms/pseries/msi.c
View file @
aba0eb84
...
@@ -217,7 +217,7 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
...
@@ -217,7 +217,7 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)
if
(
!
dn
)
if
(
!
dn
)
return
NULL
;
return
NULL
;
dn
=
find_device_pe
(
dn
);
dn
=
eeh_
find_device_pe
(
dn
);
if
(
!
dn
)
if
(
!
dn
)
return
NULL
;
return
NULL
;
...
...
arch/powerpc/platforms/pseries/pci_dlpar.c
View file @
aba0eb84
...
@@ -147,6 +147,9 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
...
@@ -147,6 +147,9 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
pci_devs_phb_init_dynamic
(
phb
);
pci_devs_phb_init_dynamic
(
phb
);
/* Create EEH devices for the PHB */
eeh_dev_phb_init_dynamic
(
phb
);
if
(
dn
->
child
)
if
(
dn
->
child
)
eeh_add_device_tree_early
(
dn
);
eeh_add_device_tree_early
(
dn
);
...
...
arch/powerpc/platforms/pseries/setup.c
View file @
aba0eb84
...
@@ -260,8 +260,12 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act
...
@@ -260,8 +260,12 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act
switch
(
action
)
{
switch
(
action
)
{
case
PSERIES_RECONFIG_ADD
:
case
PSERIES_RECONFIG_ADD
:
pci
=
np
->
parent
->
data
;
pci
=
np
->
parent
->
data
;
if
(
pci
)
if
(
pci
)
{
update_dn_pci_info
(
np
,
pci
->
phb
);
update_dn_pci_info
(
np
,
pci
->
phb
);
/* Create EEH device for the OF node */
eeh_dev_init
(
np
,
pci
->
phb
);
}
break
;
break
;
default:
default:
err
=
NOTIFY_DONE
;
err
=
NOTIFY_DONE
;
...
@@ -381,6 +385,7 @@ static void __init pSeries_setup_arch(void)
...
@@ -381,6 +385,7 @@ static void __init pSeries_setup_arch(void)
/* Find and initialize PCI host bridges */
/* Find and initialize PCI host bridges */
init_pci_config_tokens
();
init_pci_config_tokens
();
eeh_pseries_init
();
find_and_init_phbs
();
find_and_init_phbs
();
pSeries_reconfig_notifier_register
(
&
pci_dn_reconfig_nb
);
pSeries_reconfig_notifier_register
(
&
pci_dn_reconfig_nb
);
eeh_init
();
eeh_init
();
...
...
include/linux/of.h
View file @
aba0eb84
...
@@ -58,6 +58,9 @@ struct device_node {
...
@@ -58,6 +58,9 @@ struct device_node {
struct
kref
kref
;
struct
kref
kref
;
unsigned
long
_flags
;
unsigned
long
_flags
;
void
*
data
;
void
*
data
;
#if defined(CONFIG_EEH)
struct
eeh_dev
*
edev
;
#endif
#if defined(CONFIG_SPARC)
#if defined(CONFIG_SPARC)
char
*
path_component_name
;
char
*
path_component_name
;
unsigned
int
unique_id
;
unsigned
int
unique_id
;
...
@@ -72,6 +75,13 @@ struct of_phandle_args {
...
@@ -72,6 +75,13 @@ struct of_phandle_args {
uint32_t
args
[
MAX_PHANDLE_ARGS
];
uint32_t
args
[
MAX_PHANDLE_ARGS
];
};
};
#if defined(CONFIG_EEH)
static
inline
struct
eeh_dev
*
of_node_to_eeh_dev
(
struct
device_node
*
dn
)
{
return
dn
->
edev
;
}
#endif
#if defined(CONFIG_SPARC) || !defined(CONFIG_OF)
#if defined(CONFIG_SPARC) || !defined(CONFIG_OF)
/* Dummy ref counting routines - to be implemented later */
/* Dummy ref counting routines - to be implemented later */
static
inline
struct
device_node
*
of_node_get
(
struct
device_node
*
node
)
static
inline
struct
device_node
*
of_node_get
(
struct
device_node
*
node
)
...
...
include/linux/pci.h
View file @
aba0eb84
...
@@ -1647,6 +1647,13 @@ static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
...
@@ -1647,6 +1647,13 @@ static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
static
inline
void
pci_release_bus_of_node
(
struct
pci_bus
*
bus
)
{
}
static
inline
void
pci_release_bus_of_node
(
struct
pci_bus
*
bus
)
{
}
#endif
/* CONFIG_OF */
#endif
/* CONFIG_OF */
#ifdef CONFIG_EEH
static
inline
struct
eeh_dev
*
pci_dev_to_eeh_dev
(
struct
pci_dev
*
pdev
)
{
return
pdev
->
dev
.
archdata
.
edev
;
}
#endif
/**
/**
* pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
* pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
* @pdev: the PCI device
* @pdev: the PCI device
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment