diff mbox

[01/31] powerpc/eeh: Move common part to kernel directory

Message ID 1371544435-4943-2-git-send-email-shangw@linux.vnet.ibm.com (mailing list archive)
State Superseded
Headers show

Commit Message

Gavin Shan June 18, 2013, 8:33 a.m. UTC
The patch moves the common part of EEH core into arch/powerpc/kernel
directory so that we needn't PPC_PSERIES while compiling POWERNV
platform:

	* Move the EEH common part into arch/powerpc/kernel
	* Move the functions for PCI hotplug from pSeries platform to
	  arch/powerpc/kernel/pci_hotplug.c
	* Move CONFIG_EEH from arch/powerpc/platforms/pseries/Kconfig to
	  arch/powerpc/platforms/Kconfig
	* Adjust makefile accordingly

Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
---
 arch/powerpc/kernel/Makefile                |    4 +-
 arch/powerpc/kernel/eeh.c                   |  942 +++++++++++++++++++++++++++
 arch/powerpc/kernel/eeh_cache.c             |  319 +++++++++
 arch/powerpc/kernel/eeh_dev.c               |  112 ++++
 arch/powerpc/kernel/eeh_driver.c            |  552 ++++++++++++++++
 arch/powerpc/kernel/eeh_event.c             |  142 ++++
 arch/powerpc/kernel/eeh_pe.c                |  653 +++++++++++++++++++
 arch/powerpc/kernel/eeh_sysfs.c             |   75 +++
 arch/powerpc/kernel/pci_hotplug.c           |  111 ++++
 arch/powerpc/platforms/Kconfig              |    5 +
 arch/powerpc/platforms/pseries/Kconfig      |    5 -
 arch/powerpc/platforms/pseries/Makefile     |    4 +-
 arch/powerpc/platforms/pseries/eeh.c        |  942 ---------------------------
 arch/powerpc/platforms/pseries/eeh_cache.c  |  319 ---------
 arch/powerpc/platforms/pseries/eeh_dev.c    |  112 ----
 arch/powerpc/platforms/pseries/eeh_driver.c |  552 ----------------
 arch/powerpc/platforms/pseries/eeh_event.c  |  142 ----
 arch/powerpc/platforms/pseries/eeh_pe.c     |  653 -------------------
 arch/powerpc/platforms/pseries/eeh_sysfs.c  |   75 ---
 arch/powerpc/platforms/pseries/pci_dlpar.c  |   85 ---
 20 files changed, 2915 insertions(+), 2889 deletions(-)
 create mode 100644 arch/powerpc/kernel/eeh.c
 create mode 100644 arch/powerpc/kernel/eeh_cache.c
 create mode 100644 arch/powerpc/kernel/eeh_dev.c
 create mode 100644 arch/powerpc/kernel/eeh_driver.c
 create mode 100644 arch/powerpc/kernel/eeh_event.c
 create mode 100644 arch/powerpc/kernel/eeh_pe.c
 create mode 100644 arch/powerpc/kernel/eeh_sysfs.c
 create mode 100644 arch/powerpc/kernel/pci_hotplug.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh_cache.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh_dev.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh_driver.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh_event.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh_pe.c
 delete mode 100644 arch/powerpc/platforms/pseries/eeh_sysfs.c

Comments

Michael Neuling June 19, 2013, 3:58 a.m. UTC | #1
Bunch of whitespace issues here:

% git am ~/Mail/linuxppc/31202
Applying: powerpc/eeh: Move common part to kernel directory
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:437: trailing whitespace.
 
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:594: space before tab in indent.
  	 */
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:607: trailing whitespace.
	
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:608: trailing whitespace.
	/* We might get hit with another EEH freeze as soon as the 
/home/mikey/src/powerpc-test/.git/rebase-apply/patch:673: trailing whitespace.
	
error: patch failed: arch/powerpc/platforms/pseries/eeh_pe.c:1
error: arch/powerpc/platforms/pseries/eeh_pe.c: patch does not apply
Patch failed at 0001 powerpc/eeh: Move common part to kernel directory
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

Mikey

Gavin Shan <shangw@linux.vnet.ibm.com> wrote:

> The patch moves the common part of EEH core into arch/powerpc/kernel
> directory so that we needn't PPC_PSERIES while compiling POWERNV
> platform:
> 
> 	* Move the EEH common part into arch/powerpc/kernel
> 	* Move the functions for PCI hotplug from pSeries platform to
> 	  arch/powerpc/kernel/pci_hotplug.c
> 	* Move CONFIG_EEH from arch/powerpc/platforms/pseries/Kconfig to
> 	  arch/powerpc/platforms/Kconfig
> 	* Adjust makefile accordingly
> 
> Signed-off-by: Gavin Shan <shangw@linux.vnet.ibm.com>
> ---
>  arch/powerpc/kernel/Makefile                |    4 +-
>  arch/powerpc/kernel/eeh.c                   |  942 +++++++++++++++++++++++++++
>  arch/powerpc/kernel/eeh_cache.c             |  319 +++++++++
>  arch/powerpc/kernel/eeh_dev.c               |  112 ++++
>  arch/powerpc/kernel/eeh_driver.c            |  552 ++++++++++++++++
>  arch/powerpc/kernel/eeh_event.c             |  142 ++++
>  arch/powerpc/kernel/eeh_pe.c                |  653 +++++++++++++++++++
>  arch/powerpc/kernel/eeh_sysfs.c             |   75 +++
>  arch/powerpc/kernel/pci_hotplug.c           |  111 ++++
>  arch/powerpc/platforms/Kconfig              |    5 +
>  arch/powerpc/platforms/pseries/Kconfig      |    5 -
>  arch/powerpc/platforms/pseries/Makefile     |    4 +-
>  arch/powerpc/platforms/pseries/eeh.c        |  942 ---------------------------
>  arch/powerpc/platforms/pseries/eeh_cache.c  |  319 ---------
>  arch/powerpc/platforms/pseries/eeh_dev.c    |  112 ----
>  arch/powerpc/platforms/pseries/eeh_driver.c |  552 ----------------
>  arch/powerpc/platforms/pseries/eeh_event.c  |  142 ----
>  arch/powerpc/platforms/pseries/eeh_pe.c     |  653 -------------------
>  arch/powerpc/platforms/pseries/eeh_sysfs.c  |   75 ---
>  arch/powerpc/platforms/pseries/pci_dlpar.c  |   85 ---
>  20 files changed, 2915 insertions(+), 2889 deletions(-)
>  create mode 100644 arch/powerpc/kernel/eeh.c
>  create mode 100644 arch/powerpc/kernel/eeh_cache.c
>  create mode 100644 arch/powerpc/kernel/eeh_dev.c
>  create mode 100644 arch/powerpc/kernel/eeh_driver.c
>  create mode 100644 arch/powerpc/kernel/eeh_event.c
>  create mode 100644 arch/powerpc/kernel/eeh_pe.c
>  create mode 100644 arch/powerpc/kernel/eeh_sysfs.c
>  create mode 100644 arch/powerpc/kernel/pci_hotplug.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh_cache.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh_dev.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh_driver.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh_event.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh_pe.c
>  delete mode 100644 arch/powerpc/platforms/pseries/eeh_sysfs.c
> 
> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
> index f960a79..5826906 100644
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -58,6 +58,8 @@ obj-$(CONFIG_RTAS_PROC)		+= rtas-proc.o
>  obj-$(CONFIG_LPARCFG)		+= lparcfg.o
>  obj-$(CONFIG_IBMVIO)		+= vio.o
>  obj-$(CONFIG_IBMEBUS)           += ibmebus.o
> +obj-$(CONFIG_EEH)		+= eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
> +				   eeh_driver.o eeh_event.o eeh_sysfs.o
>  obj-$(CONFIG_GENERIC_TBSYNC)	+= smp-tbsync.o
>  obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
>  obj-$(CONFIG_FA_DUMP)		+= fadump.o
> @@ -100,7 +102,7 @@ obj-$(CONFIG_PPC_UDBG_16550)	+= legacy_serial.o udbg_16550.o
>  obj-$(CONFIG_STACKTRACE)	+= stacktrace.o
>  obj-$(CONFIG_SWIOTLB)		+= dma-swiotlb.o
>  
> -pci64-$(CONFIG_PPC64)		+= pci_dn.o isa-bridge.o
> +pci64-$(CONFIG_PPC64)		+= pci_hotplug.o pci_dn.o isa-bridge.o
>  obj-$(CONFIG_PCI)		+= pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \
>  				   pci-common.o pci_of_scan.o
>  obj-$(CONFIG_PCI_MSI)		+= msi.o
> diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
> new file mode 100644
> index 0000000..6b73d6c
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh.c
> @@ -0,0 +1,942 @@
> +/*
> + * Copyright IBM Corporation 2001, 2005, 2006
> + * Copyright Dave Engebretsen & Todd Inglett 2001
> + * Copyright Linas Vepstas 2005, 2006
> + * Copyright 2001-2012 IBM Corporation.
> + *
> + * 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
> + *
> + * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/pci.h>
> +#include <linux/proc_fs.h>
> +#include <linux/rbtree.h>
> +#include <linux/seq_file.h>
> +#include <linux/spinlock.h>
> +#include <linux/export.h>
> +#include <linux/of.h>
> +
> +#include <linux/atomic.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>
> +
> +
> +/** Overview:
> + *  EEH, or "Extended Error Handling" is a PCI bridge technology for
> + *  dealing with PCI bus errors that can't be dealt with within the
> + *  usual PCI framework, except by check-stopping the CPU.  Systems
> + *  that are designed for high-availability/reliability cannot afford
> + *  to crash due to a "mere" PCI error, thus the need for EEH.
> + *  An EEH-capable bridge operates by converting a detected error
> + *  into a "slot freeze", taking the PCI adapter off-line, making
> + *  the slot behave, from the OS'es point of view, as if the slot
> + *  were "empty": all reads return 0xff's and all writes are silently
> + *  ignored.  EEH slot isolation events can be triggered by parity
> + *  errors on the address or data busses (e.g. during posted writes),
> + *  which in turn might be caused by low voltage on the bus, dust,
> + *  vibration, humidity, radioactivity or plain-old failed hardware.
> + *
> + *  Note, however, that one of the leading causes of EEH slot
> + *  freeze events are buggy device drivers, buggy device microcode,
> + *  or buggy device hardware.  This is because any attempt by the
> + *  device to bus-master data to a memory address that is not
> + *  assigned to the device will trigger a slot freeze.   (The idea
> + *  is to prevent devices-gone-wild from corrupting system memory).
> + *  Buggy hardware/drivers will have a miserable time co-existing
> + *  with EEH.
> + *
> + *  Ideally, a PCI device driver, when suspecting that an isolation
> + *  event has occurred (e.g. by reading 0xff's), will then ask EEH
> + *  whether this is the case, and then take appropriate steps to
> + *  reset the PCI slot, the PCI device, and then resume operations.
> + *  However, until that day,  the checking is done here, with the
> + *  eeh_check_failure() routine embedded in the MMIO macros.  If
> + *  the slot is found to be isolated, an "EEH Event" is synthesized
> + *  and sent out for processing.
> + */
> +
> +/* If a device driver keeps reading an MMIO register in an interrupt
> + * handler after a slot isolation event, it might be broken.
> + * This sets the threshold for how many read attempts we allow
> + * before printing an error message.
> + */
> +#define EEH_MAX_FAILS	2100000
> +
> +/* Time to wait for a PCI slot to report status, in milliseconds */
> +#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
> +
> +/* Platform dependent EEH operations */
> +struct eeh_ops *eeh_ops = NULL;
> +
> +int eeh_subsystem_enabled;
> +EXPORT_SYMBOL(eeh_subsystem_enabled);
> +
> +/*
> + * EEH probe mode support. The intention is to support multiple
> + * platforms for EEH. Some platforms like pSeries do PCI emunation
> + * based on device tree. However, other platforms like powernv probe
> + * PCI devices from hardware. The flag is used to distinguish that.
> + * In addition, struct eeh_ops::probe would be invoked for particular
> + * OF node or PCI device so that the corresponding PE would be created
> + * there.
> + */
> +int eeh_probe_mode;
> +
> +/* Global EEH mutex */
> +DEFINE_MUTEX(eeh_mutex);
> +
> +/* Lock to avoid races due to multiple reports of an error */
> +static DEFINE_RAW_SPINLOCK(confirm_error_lock);
> +
> +/* Buffer for reporting pci register dumps. Its here in BSS, and
> + * not dynamically alloced, so that it ends up in RMO where RTAS
> + * can access it.
> + */
> +#define EEH_PCI_REGS_LOG_LEN 4096
> +static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
> +
> +/*
> + * The struct is used to maintain the EEH global statistic
> + * information. Besides, the EEH global statistics will be
> + * exported to user space through procfs
> + */
> +struct eeh_stats {
> +	u64 no_device;		/* PCI device not found		*/
> +	u64 no_dn;		/* OF node not found		*/
> +	u64 no_cfg_addr;	/* Config address not found	*/
> +	u64 ignored_check;	/* EEH check skipped		*/
> +	u64 total_mmio_ffs;	/* Total EEH checks		*/
> +	u64 false_positives;	/* Unnecessary EEH checks	*/
> +	u64 slot_resets;	/* PE reset			*/
> +};
> +
> +static struct eeh_stats eeh_stats;
> +
> +#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
> +
> +/**
> + * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
> + * @edev: device to report data for
> + * @buf: point to buffer in which to log
> + * @len: amount of room in buffer
> + *
> + * This routine captures assorted PCI configuration space data,
> + * and puts them into a buffer for RTAS error logging.
> + */
> +static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
> +{
> +	struct device_node *dn = eeh_dev_to_of_node(edev);
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	u32 cfg;
> +	int cap, i;
> +	int n = 0;
> +
> +	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
> +	printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
> +
> +	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
> +	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
> +	printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
> +
> +	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
> +	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
> +	printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
> +
> +	if (!dev) {
> +		printk(KERN_WARNING "EEH: no PCI device for this of node\n");
> +		return n;
> +	}
> +
> +	/* Gather bridge-specific registers */
> +	if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
> +		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
> +		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
> +
> +		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
> +		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
> +	}
> +
> +	/* Dump out the PCI-X command and status regs */
> +	cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
> +	if (cap) {
> +		eeh_ops->read_config(dn, cap, 4, &cfg);
> +		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
> +
> +		eeh_ops->read_config(dn, cap+4, 4, &cfg);
> +		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
> +		printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
> +	}
> +
> +	/* If PCI-E capable, dump PCI-E cap 10, and the AER */
> +	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
> +	if (cap) {
> +		n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
> +		printk(KERN_WARNING
> +		       "EEH: PCI-E capabilities and status follow:\n");
> +
> +		for (i=0; i<=8; i++) {
> +			eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> +			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> +			printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
> +		}
> +
> +		cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
> +		if (cap) {
> +			n += scnprintf(buf+n, len-n, "pci-e AER:\n");
> +			printk(KERN_WARNING
> +			       "EEH: PCI-E AER capability register set follows:\n");
> +
> +			for (i=0; i<14; i++) {
> +				eeh_ops->read_config(dn, cap+4*i, 4, &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);
> +			}
> +		}
> +	}
> +
> +	return n;
> +}
> +
> +/**
> + * eeh_slot_error_detail - Generate combined log including driver log and error log
> + * @pe: EEH PE
> + * @severity: temporary or permanent error log
> + *
> + * This routine should be called to generate the combined log, which
> + * is comprised of driver log and error log. The driver log is figured
> + * out from the config space of the corresponding PCI device, while
> + * the error log is fetched through platform dependent function call.
> + */
> +void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
> +{
> +	size_t loglen = 0;
> +	struct eeh_dev *edev;
> +
> +	eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> +	eeh_ops->configure_bridge(pe);
> +	eeh_pe_restore_bars(pe);
> +
> +	pci_regs_buf[0] = 0;
> +	eeh_pe_for_each_dev(pe, edev) {
> +		loglen += eeh_gather_pci_data(edev, pci_regs_buf,
> +				EEH_PCI_REGS_LOG_LEN);
> +        }
> +
> +	eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
> +}
> +
> +/**
> + * eeh_token_to_phys - Convert EEH address token to phys address
> + * @token: I/O token, should be address in the form 0xA....
> + *
> + * This routine should be called to convert virtual I/O address
> + * to physical one.
> + */
> +static inline unsigned long eeh_token_to_phys(unsigned long token)
> +{
> +	pte_t *ptep;
> +	unsigned long pa;
> +
> +	ptep = find_linux_pte(init_mm.pgd, token);
> +	if (!ptep)
> +		return token;
> +	pa = pte_pfn(*ptep) << PAGE_SHIFT;
> +
> +	return pa | (token & (PAGE_SIZE-1));
> +}
> +
> +/**
> + * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
> + * @edev: eeh device
> + *
> + * Check for an EEH failure for the given device node.  Call this
> + * routine if the result of a read was all 0xff's and you want to
> + * find out if this is due to an EEH slot freeze.  This routine
> + * will query firmware for the EEH status.
> + *
> + * Returns 0 if there has not been an EEH error; otherwise returns
> + * a non-zero value and queues up a slot isolation event notification.
> + *
> + * It is safe to call this routine in an interrupt context.
> + */
> +int eeh_dev_check_failure(struct eeh_dev *edev)
> +{
> +	int ret;
> +	unsigned long flags;
> +	struct device_node *dn;
> +	struct pci_dev *dev;
> +	struct eeh_pe *pe;
> +	int rc = 0;
> +	const char *location;
> +
> +	eeh_stats.total_mmio_ffs++;
> +
> +	if (!eeh_subsystem_enabled)
> +		return 0;
> +
> +	if (!edev) {
> +		eeh_stats.no_dn++;
> +		return 0;
> +	}
> +	dn = eeh_dev_to_of_node(edev);
> +	dev = eeh_dev_to_pci_dev(edev);
> +	pe = edev->pe;
> +
> +	/* Access to IO BARs might get this far and still not want checking. */
> +	if (!pe) {
> +		eeh_stats.ignored_check++;
> +		pr_debug("EEH: Ignored check for %s %s\n",
> +			eeh_pci_name(dev), dn->full_name);
> +		return 0;
> +	}
> +
> +	if (!pe->addr && !pe->config_addr) {
> +		eeh_stats.no_cfg_addr++;
> +		return 0;
> +	}
> +
> +	/* If we already have a pending isolation event for this
> +	 * slot, we know it's bad already, we don't need to check.
> +	 * Do this checking under a lock; as multiple PCI devices
> +	 * in one slot might report errors simultaneously, and we
> +	 * only want one error recovery routine running.
> +	 */
> +	raw_spin_lock_irqsave(&confirm_error_lock, flags);
> +	rc = 1;
> +	if (pe->state & EEH_PE_ISOLATED) {
> +		pe->check_count++;
> +		if (pe->check_count % EEH_MAX_FAILS == 0) {
> +			location = of_get_property(dn, "ibm,loc-code", NULL);
> +			printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
> +				"location=%s driver=%s pci addr=%s\n",
> +				pe->check_count, location,
> +				eeh_driver_name(dev), eeh_pci_name(dev));
> +			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
> +				eeh_driver_name(dev));
> +			dump_stack();
> +		}
> +		goto dn_unlock;
> +	}
> +
> +	/*
> +	 * Now test for an EEH failure.  This is VERY expensive.
> +	 * Note that the eeh_config_addr may be a parent device
> +	 * in the case of a device behind a bridge, or it may be
> +	 * function zero of a multi-function device.
> +	 * In any case they must share a common PHB.
> +	 */
> +	ret = eeh_ops->get_state(pe, NULL);
> +
> +	/* Note that config-io to empty slots may fail;
> +	 * they are empty when they don't have children.
> +	 * We will punt with the following conditions: Failure to get
> +	 * PE's state, EEH not support and Permanently unavailable
> +	 * state, PE is in good state.
> +	 */
> +	if ((ret < 0) ||
> +	    (ret == EEH_STATE_NOT_SUPPORT) ||
> +	    (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
> +	    (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
> +		eeh_stats.false_positives++;
> +		pe->false_positives++;
> +		rc = 0;
> +		goto dn_unlock;
> +	}
> +
> +	eeh_stats.slot_resets++;
> + 
> +	/* Avoid repeated reports of this failure, including problems
> +	 * with other functions on this device, and functions under
> +	 * bridges.
> +	 */
> +	eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
> +	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> +
> +	eeh_send_failure_event(pe);
> +
> +	/* Most EEH events are due to device driver bugs.  Having
> +	 * a stack trace will help the device-driver authors figure
> +	 * out what happened.  So print that out.
> +	 */
> +	WARN(1, "EEH: failure detected\n");
> +	return 1;
> +
> +dn_unlock:
> +	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> +	return rc;
> +}
> +
> +EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
> +
> +/**
> + * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
> + * @token: I/O token, should be address in the form 0xA....
> + * @val: value, should be all 1's (XXX why do we need this arg??)
> + *
> + * Check for an EEH failure at the given token address.  Call this
> + * routine if the result of a read was all 0xff's and you want to
> + * find out if this is due to an EEH slot freeze event.  This routine
> + * will query firmware for the EEH status.
> + *
> + * Note this routine is safe to call in an interrupt context.
> + */
> +unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
> +{
> +	unsigned long addr;
> +	struct eeh_dev *edev;
> +
> +	/* Finding the phys addr + pci device; this is pretty quick. */
> +	addr = eeh_token_to_phys((unsigned long __force) token);
> +	edev = eeh_addr_cache_get_dev(addr);
> +	if (!edev) {
> +		eeh_stats.no_device++;
> +		return val;
> +	}
> +
> +	eeh_dev_check_failure(edev);
> +
> +	pci_dev_put(eeh_dev_to_pci_dev(edev));
> +	return val;
> +}
> +
> +EXPORT_SYMBOL(eeh_check_failure);
> +
> +
> +/**
> + * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
> + * @pe: EEH PE
> + *
> + * 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_pe *pe, int function)
> +{
> +	int rc;
> +
> +	rc = eeh_ops->set_option(pe, function);
> +	if (rc)
> +		pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
> +			__func__, function, pe->phb->global_number, pe->addr, rc);
> +
> +	rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> +	if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
> +	   (function == EEH_OPT_THAW_MMIO))
> +		return 0;
> +
> +	return rc;
> +}
> +
> +/**
> + * pcibios_set_pcie_slot_reset - Set PCI-E reset state
> + * @dev: pci device struct
> + * @state: reset state to enter
> + *
> + * Return value:
> + * 	0 if success
> + */
> +int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
> +{
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +	struct eeh_pe *pe = edev->pe;
> +
> +	if (!pe) {
> +		pr_err("%s: No PE found on PCI device %s\n",
> +			__func__, pci_name(dev));
> +		return -EINVAL;
> +	}
> +
> +	switch (state) {
> +	case pcie_deassert_reset:
> +		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> +		break;
> +	case pcie_hot_reset:
> +		eeh_ops->reset(pe, EEH_RESET_HOT);
> +		break;
> +	case pcie_warm_reset:
> +		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> +		break;
> +	default:
> +		return -EINVAL;
> +	};
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_set_pe_freset - Check the required reset for the indicated device
> + * @data: EEH device
> + * @flag: 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.
> + */
> +static void *eeh_set_dev_freset(void *data, void *flag)
> +{
> +	struct pci_dev *dev;
> +	unsigned int *freset = (unsigned int *)flag;
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +
> +	dev = eeh_dev_to_pci_dev(edev);
> +	if (dev)
> +		*freset |= dev->needs_freset;
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
> + * @pe: EEH PE
> + *
> + * Assert the PCI #RST line for 1/4 second.
> + */
> +static void eeh_reset_pe_once(struct eeh_pe *pe)
> +{
> +	unsigned int freset = 0;
> +
> +	/* Determine type of EEH reset required for
> +	 * Partitionable Endpoint, a hot-reset (1)
> +	 * or a fundamental reset (3).
> +	 * A fundamental reset required by any device under
> +	 * Partitionable Endpoint trumps hot-reset.
> +  	 */
> +	eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
> +
> +	if (freset)
> +		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> +	else
> +		eeh_ops->reset(pe, EEH_RESET_HOT);
> +
> +	/* The PCI bus requires that the reset be held high for at least
> +	 * a 100 milliseconds. We wait a bit longer 'just in case'.
> +	 */
> +#define PCI_BUS_RST_HOLD_TIME_MSEC 250
> +	msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
> +	
> +	/* We might get hit with another EEH freeze as soon as the 
> +	 * pci slot reset line is dropped. Make sure we don't miss
> +	 * these, and clear the flag now.
> +	 */
> +	eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
> +
> +	eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> +
> +	/* 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
> +	 * up traffic.
> +	 */
> +#define PCI_BUS_SETTLE_TIME_MSEC 1800
> +	msleep(PCI_BUS_SETTLE_TIME_MSEC);
> +}
> +
> +/**
> + * eeh_reset_pe - Reset the indicated PE
> + * @pe: EEH PE
> + *
> + * 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_pe *pe)
> +{
> +	int i, rc;
> +
> +	/* Take three shots at resetting the bus */
> +	for (i=0; i<3; i++) {
> +		eeh_reset_pe_once(pe);
> +
> +		rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> +		if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
> +			return 0;
> +
> +		if (rc < 0) {
> +			pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
> +				__func__, pe->phb->global_number, pe->addr);
> +			return -1;
> +		}
> +		pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
> +			i+1, pe->phb->global_number, pe->addr, rc);
> +	}
> +
> +	return -1;
> +}
> +
> +/**
> + * eeh_save_bars - Save device bars
> + * @edev: PCI device associated EEH device
> + *
> + * Save the values of the device bars. Unlike the restore
> + * routine, this routine is *not* recursive. This is because
> + * PCI devices are added individually; but, for the restore,
> + * an entire slot is reset at a time.
> + */
> +void eeh_save_bars(struct eeh_dev *edev)
> +{
> +	int i;
> +	struct device_node *dn;
> +
> +	if (!edev)
> +		return;
> +	dn = eeh_dev_to_of_node(edev);
> +	
> +	for (i = 0; i < 16; i++)
> +		eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
> +}
> +
> +/**
> + * 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.
> + * As a side effect we can determine here if eeh is supported at all.
> + * Note that we leave EEH on so failed config cycles won't cause a machine
> + * check.  If a user turns off EEH for a particular adapter they are really
> + * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't
> + * grant access to a slot if EEH isn't enabled, and so we always enable
> + * EEH for all slots/all devices.
> + *
> + * The eeh-force-off option disables EEH checking globally, for all slots.
> + * Even if force-off is set, the EEH hardware is still enabled, so that
> + * newer systems can boot.
> + */
> +static int __init eeh_init(void)
> +{
> +	struct pci_controller *hose, *tmp;
> +	struct device_node *phb;
> +	int ret;
> +
> +	/* call platform initialization function */
> +	if (!eeh_ops) {
> +		pr_warning("%s: Platform EEH operation not found\n",
> +			__func__);
> +		return -EEXIST;
> +	} else if ((ret = eeh_ops->init())) {
> +		pr_warning("%s: Failed to call platform init function (%d)\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	raw_spin_lock_init(&confirm_error_lock);
> +
> +	/* Enable EEH for all adapters */
> +	if (eeh_probe_mode_devtree()) {
> +		list_for_each_entry_safe(hose, tmp,
> +			&hose_list, list_node) {
> +			phb = hose->dn;
> +			traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
> +		}
> +	}
> +
> +	if (eeh_subsystem_enabled)
> +		pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
> +	else
> +		pr_warning("EEH: No capable adapters found\n");
> +
> +	return ret;
> +}
> +
> +core_initcall_sync(eeh_init);
> +
> +/**
> + * eeh_add_device_early - Enable EEH for the indicated device_node
> + * @dn: device node for which to set up EEH
> + *
> + * This routine must be used to perform EEH initialization for PCI
> + * devices that were added after system boot (e.g. hotplug, dlpar).
> + * This routine must be called before any i/o is performed to the
> + * adapter (inluding any config-space i/o).
> + * Whether this actually enables EEH or not for this device depends
> + * on the CEC architecture, type of the device, on earlier boot
> + * command-line arguments & etc.
> + */
> +static void eeh_add_device_early(struct device_node *dn)
> +{
> +	struct pci_controller *phb;
> +
> +	if (!of_node_to_eeh_dev(dn))
> +		return;
> +	phb = of_node_to_eeh_dev(dn)->phb;
> +
> +	/* USB Bus children of PCI devices will not have BUID's */
> +	if (NULL == phb || 0 == phb->buid)
> +		return;
> +
> +	/* FIXME: hotplug support on POWERNV */
> +	eeh_ops->of_probe(dn, NULL);
> +}
> +
> +/**
> + * 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)
> +{
> +	struct device_node *sib;
> +
> +	for_each_child_of_node(dn, sib)
> +		eeh_add_device_tree_early(sib);
> +	eeh_add_device_early(dn);
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
> +
> +/**
> + * eeh_add_device_late - Perform EEH initialization for the indicated pci device
> + * @dev: pci device for which to set up EEH
> + *
> + * This routine must be used to complete EEH initialization for PCI
> + * devices that were added after system boot (e.g. hotplug, dlpar).
> + */
> +static void eeh_add_device_late(struct pci_dev *dev)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *edev;
> +
> +	if (!dev || !eeh_subsystem_enabled)
> +		return;
> +
> +	pr_debug("EEH: Adding device %s\n", pci_name(dev));
> +
> +	dn = pci_device_to_OF_node(dev);
> +	edev = of_node_to_eeh_dev(dn);
> +	if (edev->pdev == dev) {
> +		pr_debug("EEH: Already referenced !\n");
> +		return;
> +	}
> +	WARN_ON(edev->pdev);
> +
> +	pci_dev_get(dev);
> +	edev->pdev = dev;
> +	dev->dev.archdata.edev = edev;
> +
> +	eeh_addr_cache_insert_dev(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)
> +{
> +	struct pci_dev *dev;
> +
> +	list_for_each_entry(dev, &bus->devices, bus_list) {
> + 		eeh_add_device_late(dev);
> + 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> + 			struct pci_bus *subbus = dev->subordinate;
> + 			if (subbus)
> + 				eeh_add_device_tree_late(subbus);
> + 		}
> +	}
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
> +
> +/**
> + * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
> + * @bus: PCI bus
> + *
> + * This routine must be used to add EEH sysfs files for PCI
> + * devices which are attached to the indicated PCI bus. The PCI bus
> + * is added after system boot through hotplug or dlpar.
> + */
> +void eeh_add_sysfs_files(struct pci_bus *bus)
> +{
> +	struct pci_dev *dev;
> +
> +	list_for_each_entry(dev, &bus->devices, bus_list) {
> +		eeh_sysfs_add_device(dev);
> +		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> +			struct pci_bus *subbus = dev->subordinate;
> +			if (subbus)
> +				eeh_add_sysfs_files(subbus);
> +		}
> +	}
> +}
> +EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
> +
> +/**
> + * eeh_remove_device - Undo EEH setup for the indicated pci device
> + * @dev: pci device to be removed
> + * @purge_pe: remove the PE or not
> + *
> + * This routine should be called when a device is removed from
> + * a running system (e.g. by hotplug or dlpar).  It unregisters
> + * the PCI device from the EEH subsystem.  I/O errors affecting
> + * this device will no longer be detected after this call; thus,
> + * i/o errors affecting this slot may leave this device unusable.
> + */
> +static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
> +{
> +	struct eeh_dev *edev;
> +
> +	if (!dev || !eeh_subsystem_enabled)
> +		return;
> +	edev = pci_dev_to_eeh_dev(dev);
> +
> +	/* Unregister the device with the EEH/PCI address search system */
> +	pr_debug("EEH: Removing device %s\n", pci_name(dev));
> +
> +	if (!edev || !edev->pdev) {
> +		pr_debug("EEH: Not referenced !\n");
> +		return;
> +	}
> +	edev->pdev = NULL;
> +	dev->dev.archdata.edev = NULL;
> +	pci_dev_put(dev);
> +
> +	eeh_rmv_from_parent_pe(edev, purge_pe);
> +	eeh_addr_cache_rmv_dev(dev);
> +	eeh_sysfs_remove_device(dev);
> +}
> +
> +/**
> + * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
> + * @dev: PCI device
> + * @purge_pe: remove the corresponding PE or not
> + *
> + * 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, int purge_pe)
> +{
> +	struct pci_bus *bus = dev->subordinate;
> +	struct pci_dev *child, *tmp;
> +
> +	eeh_remove_device(dev, purge_pe);
> +
> +	if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> +		list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
> +			 eeh_remove_bus_device(child, purge_pe);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
> +
> +static int proc_eeh_show(struct seq_file *m, void *v)
> +{
> +	if (0 == eeh_subsystem_enabled) {
> +		seq_printf(m, "EEH Subsystem is globally disabled\n");
> +		seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
> +	} else {
> +		seq_printf(m, "EEH Subsystem is enabled\n");
> +		seq_printf(m,
> +				"no device=%llu\n"
> +				"no device node=%llu\n"
> +				"no config address=%llu\n"
> +				"check not wanted=%llu\n"
> +				"eeh_total_mmio_ffs=%llu\n"
> +				"eeh_false_positives=%llu\n"
> +				"eeh_slot_resets=%llu\n",
> +				eeh_stats.no_device,
> +				eeh_stats.no_dn,
> +				eeh_stats.no_cfg_addr,
> +				eeh_stats.ignored_check,
> +				eeh_stats.total_mmio_ffs,
> +				eeh_stats.false_positives,
> +				eeh_stats.slot_resets);
> +	}
> +
> +	return 0;
> +}
> +
> +static int proc_eeh_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, proc_eeh_show, NULL);
> +}
> +
> +static const struct file_operations proc_eeh_operations = {
> +	.open      = proc_eeh_open,
> +	.read      = seq_read,
> +	.llseek    = seq_lseek,
> +	.release   = single_release,
> +};
> +
> +static int __init eeh_init_proc(void)
> +{
> +	if (machine_is(pseries))
> +		proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
> +	return 0;
> +}
> +__initcall(eeh_init_proc);
> diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c
> new file mode 100644
> index 0000000..5a4c879
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_cache.c
> @@ -0,0 +1,319 @@
> +/*
> + * PCI address cache; allows the lookup of PCI devices based on I/O address
> + *
> + * Copyright IBM Corporation 2004
> + * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
> + *
> + * 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/list.h>
> +#include <linux/pci.h>
> +#include <linux/rbtree.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/atomic.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +
> +
> +/**
> + * The pci address cache subsystem.  This subsystem places
> + * PCI device address resources into a red-black tree, sorted
> + * according to the address range, so that given only an i/o
> + * address, the corresponding PCI device can be **quickly**
> + * found. It is safe to perform an address lookup in an interrupt
> + * context; this ability is an important feature.
> + *
> + * Currently, the only customer of this code is the EEH subsystem;
> + * thus, this code has been somewhat tailored to suit EEH better.
> + * In particular, the cache does *not* hold the addresses of devices
> + * for which EEH is not enabled.
> + *
> + * (Implementation Note: The RB tree seems to be better/faster
> + * than any hash algo I could think of for this problem, even
> + * with the penalty of slow pointer chases for d-cache misses).
> + */
> +struct pci_io_addr_range {
> +	struct rb_node rb_node;
> +	unsigned long addr_lo;
> +	unsigned long addr_hi;
> +	struct eeh_dev *edev;
> +	struct pci_dev *pcidev;
> +	unsigned int flags;
> +};
> +
> +static struct pci_io_addr_cache {
> +	struct rb_root rb_root;
> +	spinlock_t piar_lock;
> +} pci_io_addr_cache_root;
> +
> +static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
> +{
> +	struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
> +
> +	while (n) {
> +		struct pci_io_addr_range *piar;
> +		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +
> +		if (addr < piar->addr_lo) {
> +			n = n->rb_left;
> +		} else {
> +			if (addr > piar->addr_hi) {
> +				n = n->rb_right;
> +			} else {
> +				pci_dev_get(piar->pcidev);
> +				return piar->edev;
> +			}
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_addr_cache_get_dev - Get device, given only address
> + * @addr: mmio (PIO) phys address or i/o port number
> + *
> + * Given an mmio phys address, or a port number, find a pci device
> + * that implements this address.  Be sure to pci_dev_put the device
> + * when finished.  I/O port numbers are assumed to be offset
> + * from zero (that is, they do *not* have pci_io_addr added in).
> + * It is safe to call this function within an interrupt.
> + */
> +struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
> +{
> +	struct eeh_dev *edev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> +	edev = __eeh_addr_cache_get_device(addr);
> +	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +	return edev;
> +}
> +
> +#ifdef DEBUG
> +/*
> + * Handy-dandy debug print routine, does nothing more
> + * than print out the contents of our addr cache.
> + */
> +static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
> +{
> +	struct rb_node *n;
> +	int cnt = 0;
> +
> +	n = rb_first(&cache->rb_root);
> +	while (n) {
> +		struct pci_io_addr_range *piar;
> +		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +		pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
> +		       (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
> +		       piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
> +		cnt++;
> +		n = rb_next(n);
> +	}
> +}
> +#endif
> +
> +/* Insert address range into the rb tree. */
> +static struct pci_io_addr_range *
> +eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
> +		      unsigned long ahi, unsigned int flags)
> +{
> +	struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
> +	struct rb_node *parent = NULL;
> +	struct pci_io_addr_range *piar;
> +
> +	/* Walk tree, find a place to insert into tree */
> +	while (*p) {
> +		parent = *p;
> +		piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
> +		if (ahi < piar->addr_lo) {
> +			p = &parent->rb_left;
> +		} else if (alo > piar->addr_hi) {
> +			p = &parent->rb_right;
> +		} else {
> +			if (dev != piar->pcidev ||
> +			    alo != piar->addr_lo || ahi != piar->addr_hi) {
> +				pr_warning("PIAR: overlapping address range\n");
> +			}
> +			return piar;
> +		}
> +	}
> +	piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
> +	if (!piar)
> +		return NULL;
> +
> +	pci_dev_get(dev);
> +	piar->addr_lo = alo;
> +	piar->addr_hi = ahi;
> +	piar->edev = pci_dev_to_eeh_dev(dev);
> +	piar->pcidev = dev;
> +	piar->flags = flags;
> +
> +#ifdef DEBUG
> +	pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
> +	                  alo, ahi, pci_name(dev));
> +#endif
> +
> +	rb_link_node(&piar->rb_node, parent, p);
> +	rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
> +
> +	return piar;
> +}
> +
> +static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *edev;
> +	int i;
> +
> +	dn = pci_device_to_OF_node(dev);
> +	if (!dn) {
> +		pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
> +		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. */
> +	if (!edev->pe) {
> +#ifdef DEBUG
> +		pr_info("PCI: skip building address cache for=%s - %s\n",
> +			pci_name(dev), dn->full_name);
> +#endif
> +		return;
> +	}
> +
> +	/* Walk resources on this device, poke them into the tree */
> +	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
> +		unsigned long start = pci_resource_start(dev,i);
> +		unsigned long end = pci_resource_end(dev,i);
> +		unsigned int flags = pci_resource_flags(dev,i);
> +
> +		/* We are interested only bus addresses, not dma or other stuff */
> +		if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
> +			continue;
> +		if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
> +			 continue;
> +		eeh_addr_cache_insert(dev, start, end, flags);
> +	}
> +}
> +
> +/**
> + * eeh_addr_cache_insert_dev - Add a device to the address cache
> + * @dev: PCI device whose I/O addresses we are interested in.
> + *
> + * In order to support the fast lookup of devices based on addresses,
> + * we maintain a cache of devices that can be quickly searched.
> + * This routine adds a device to that cache.
> + */
> +void eeh_addr_cache_insert_dev(struct pci_dev *dev)
> +{
> +	unsigned long flags;
> +
> +	/* Ignore PCI bridges */
> +	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
> +		return;
> +
> +	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> +	__eeh_addr_cache_insert_dev(dev);
> +	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +}
> +
> +static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> +{
> +	struct rb_node *n;
> +
> +restart:
> +	n = rb_first(&pci_io_addr_cache_root.rb_root);
> +	while (n) {
> +		struct pci_io_addr_range *piar;
> +		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> +
> +		if (piar->pcidev == dev) {
> +			rb_erase(n, &pci_io_addr_cache_root.rb_root);
> +			pci_dev_put(piar->pcidev);
> +			kfree(piar);
> +			goto restart;
> +		}
> +		n = rb_next(n);
> +	}
> +}
> +
> +/**
> + * eeh_addr_cache_rmv_dev - remove pci device from addr cache
> + * @dev: device to remove
> + *
> + * Remove a device from the addr-cache tree.
> + * This is potentially expensive, since it will walk
> + * the tree multiple times (once per resource).
> + * But so what; device removal doesn't need to be that fast.
> + */
> +void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> +	__eeh_addr_cache_rmv_dev(dev);
> +	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> +}
> +
> +/**
> + * eeh_addr_cache_build - Build a cache of I/O addresses
> + *
> + * Build a cache of pci i/o addresses.  This cache will be used to
> + * find the pci device that corresponds to a given address.
> + * This routine scans all pci busses to build the cache.
> + * Must be run late in boot process, after the pci controllers
> + * have been scanned for devices (after all device resources are known).
> + */
> +void __init eeh_addr_cache_build(void)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *edev;
> +	struct pci_dev *dev = NULL;
> +
> +	spin_lock_init(&pci_io_addr_cache_root.piar_lock);
> +
> +	for_each_pci_dev(dev) {
> +		eeh_addr_cache_insert_dev(dev);
> +
> +		dn = pci_device_to_OF_node(dev);
> +		if (!dn)
> +			continue;
> +
> +		edev = of_node_to_eeh_dev(dn);
> +		if (!edev)
> +			continue;
> +
> +		pci_dev_get(dev);  /* matching put is in eeh_remove_device() */
> +		dev->dev.archdata.edev = edev;
> +		edev->pdev = dev;
> +
> +		eeh_sysfs_add_device(dev);
> +	}
> +
> +#ifdef DEBUG
> +	/* Verify tree built up above, echo back the list of addrs. */
> +	eeh_addr_cache_print(&pci_io_addr_cache_root);
> +#endif
> +}
> +
> diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c
> new file mode 100644
> index 0000000..1efa28f
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_dev.c
> @@ -0,0 +1,112 @@
> +/*
> + * 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 *eeh_dev_init(struct device_node *dn, void *data)
> +{
> +	struct pci_controller *phb = data;
> +	struct eeh_dev *edev;
> +
> +	/* Allocate EEH device */
> +	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> +	if (!edev) {
> +		pr_warning("%s: out of memory\n", __func__);
> +		return NULL;
> +	}
> +
> +	/* Associate EEH device with OF node */
> +	PCI_DN(dn)->edev = edev;
> +	edev->dn  = dn;
> +	edev->phb = phb;
> +	INIT_LIST_HEAD(&edev->list);
> +
> +	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 eeh_dev_phb_init_dynamic(struct pci_controller *phb)
> +{
> +	struct device_node *dn = phb->dn;
> +
> +	/* EEH PE for PHB */
> +	eeh_phb_pe_create(phb);
> +
> +	/* 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
> + */
> +static int __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);
> +
> +	pr_info("EEH: devices created\n");
> +
> +	return 0;
> +}
> +
> +core_initcall(eeh_dev_phb_init);
> diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
> new file mode 100644
> index 0000000..a3fefb6
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_driver.c
> @@ -0,0 +1,552 @@
> +/*
> + * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
> + * Copyright IBM Corp. 2004 2005
> + * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
> + *
> + * All rights reserved.
> + *
> + * 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, GOOD TITLE or
> + * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <asm/eeh.h>
> +#include <asm/eeh_event.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/prom.h>
> +#include <asm/rtas.h>
> +
> +/**
> + * 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)
> +		return pdev->dev.driver->name;
> +	return "";
> +}
> +
> +/**
> + * eeh_pcid_get - Get the PCI device driver
> + * @pdev: PCI device
> + *
> + * The function is used to retrieve the PCI device driver for
> + * the indicated PCI device. Besides, we will increase the reference
> + * of the PCI device driver to prevent that being unloaded on
> + * the fly. Otherwise, kernel crash would be seen.
> + */
> +static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
> +{
> +	if (!pdev || !pdev->driver)
> +		return NULL;
> +
> +	if (!try_module_get(pdev->driver->driver.owner))
> +		return NULL;
> +
> +	return pdev->driver;
> +}
> +
> +/**
> + * eeh_pcid_put - Dereference on the PCI device driver
> + * @pdev: PCI device
> + *
> + * The function is called to do dereference on the PCI device
> + * driver of the indicated PCI device.
> + */
> +static inline void eeh_pcid_put(struct pci_dev *pdev)
> +{
> +	if (!pdev || !pdev->driver)
> +		return;
> +
> +	module_put(pdev->driver->driver.owner);
> +}
> +
> +#if 0
> +static void print_device_node_tree(struct pci_dn *pdn, int dent)
> +{
> +	int i;
> +	struct device_node *pc;
> +
> +	if (!pdn)
> +		return;
> +	for (i = 0; i < dent; i++)
> +		printk(" ");
> +	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
> +		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
> +		pdn->eeh_pe_config_addr, pdn->node->full_name);
> +	dent += 3;
> +	pc = pdn->node->child;
> +	while (pc) {
> +		print_device_node_tree(PCI_DN(pc), dent);
> +		pc = pc->sibling;
> +	}
> +}
> +#endif
> +
> +/**
> + * 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)
> +{
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +
> +	/* Don't disable MSI and MSI-X interrupts. They are
> +	 * effectively disabled by the DMA Stopped state
> +	 * when an EEH error occurs.
> +	 */
> +	if (dev->msi_enabled || dev->msix_enabled)
> +		return;
> +
> +	if (!irq_has_action(dev->irq))
> +		return;
> +
> +	edev->mode |= EEH_DEV_IRQ_DISABLED;
> +	disable_irq_nosync(dev->irq);
> +}
> +
> +/**
> + * 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)
> +{
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> +
> +	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
> +		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
> +		enable_irq(dev->irq);
> +	}
> +}
> +
> +/**
> + * eeh_report_error - Report pci error to each device driver
> + * @data: eeh device
> + * @userdata: return value
> + * 
> + * Report an EEH error to each device driver, collect up and 
> + * merge the device driver responses. Cumulative response 
> + * passed back in "userdata".
> + */
> +static void *eeh_report_error(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	enum pci_ers_result rc, *res = userdata;
> +	struct pci_driver *driver;
> +
> +	/* We might not have the associated PCI device,
> +	 * then we should continue for next one.
> +	 */
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_frozen;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_disable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->error_detected) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
> +
> +	/* A driver that needs a reset trumps all others */
> +	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * Tells each device driver that IO ports, MMIO and config space I/O
> + * are now enabled. Collects up and merges the device driver responses.
> + * Cumulative response passed back in "userdata".
> + */
> +static void *eeh_report_mmio_enabled(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	enum pci_ers_result rc, *res = userdata;
> +	struct pci_driver *driver;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->mmio_enabled) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	rc = driver->err_handler->mmio_enabled(dev);
> +
> +	/* A driver that needs a reset trumps all others */
> +	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_reset - Tell device that slot has been reset
> + * @data: eeh 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 void *eeh_report_reset(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	enum pci_ers_result rc, *res = userdata;
> +	struct pci_driver *driver;
> +
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_normal;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_enable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->slot_reset) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	rc = driver->err_handler->slot_reset(dev);
> +	if ((*res == PCI_ERS_RESULT_NONE) ||
> +	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
> +	if (*res == PCI_ERS_RESULT_DISCONNECT &&
> +	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_resume - Tell device to resume normal operations
> + * @data: eeh 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 void *eeh_report_resume(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	struct pci_driver *driver;
> +
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_normal;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_enable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->resume) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	driver->err_handler->resume(dev);
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_report_failure - Tell device driver that device is dead.
> + * @data: eeh device
> + * @userdata: return value
> + *
> + * This informs the device driver that the device is permanently
> + * dead, and that no further recovery attempts will be made on it.
> + */
> +static void *eeh_report_failure(void *data, void *userdata)
> +{
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> +	struct pci_driver *driver;
> +
> +	if (!dev) return NULL;
> +	dev->error_state = pci_channel_io_perm_failure;
> +
> +	driver = eeh_pcid_get(dev);
> +	if (!driver) return NULL;
> +
> +	eeh_disable_irq(dev);
> +
> +	if (!driver->err_handler ||
> +	    !driver->err_handler->error_detected) {
> +		eeh_pcid_put(dev);
> +		return NULL;
> +	}
> +
> +	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
> +
> +	eeh_pcid_put(dev);
> +	return NULL;
> +}
> +
> +/**
> + * eeh_reset_device - Perform actual reset of a pci slot
> + * @pe: EEH PE
> + * @bus: PCI bus corresponding to the isolcated slot
> + *
> + * This routine must be called to do reset on the indicated PE.
> + * During the reset, udev might be invoked because those affected
> + * PCI devices will be removed and then added.
> + */
> +static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> +{
> +	int cnt, rc;
> +
> +	/* pcibios will clear the counter; save the value */
> +	cnt = pe->freeze_count;
> +
> +	/*
> +	 * We don't remove the corresponding PE instances because
> +	 * we need the information afterwords. The attached EEH
> +	 * devices are expected to be attached soon when calling
> +	 * into pcibios_add_pci_devices().
> +	 */
> +	if (bus)
> +		__pcibios_remove_pci_devices(bus, 0);
> +
> +	/* Reset the pci controller. (Asserts RST#; resets config space).
> +	 * Reconfigure bridges and devices. Don't try to bring the system
> +	 * up if the reset failed for some reason.
> +	 */
> +	rc = eeh_reset_pe(pe);
> +	if (rc)
> +		return rc;
> +
> +	/* Restore PE */
> +	eeh_ops->configure_bridge(pe);
> +	eeh_pe_restore_bars(pe);
> +
> +	/* Give the system 5 seconds to finish running the user-space
> +	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes, 
> +	 * this is a hack, but if we don't do this, and try to bring 
> +	 * the device up before the scripts have taken it down, 
> +	 * potentially weird things happen.
> +	 */
> +	if (bus) {
> +		ssleep(5);
> +		pcibios_add_pci_devices(bus);
> +	}
> +	pe->freeze_count = cnt;
> +
> +	return 0;
> +}
> +
> +/* The longest amount of time to wait for a pci device
> + * to come back on line, in seconds.
> + */
> +#define MAX_WAIT_FOR_RECOVERY 150
> +
> +/**
> + * eeh_handle_event - Reset a PCI device after hard lockup.
> + * @pe: EEH PE
> + *
> + * 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).
> + */
> +void eeh_handle_event(struct eeh_pe *pe)
> +{
> +	struct pci_bus *frozen_bus;
> +	int rc = 0;
> +	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
> +
> +	frozen_bus = eeh_pe_bus_get(pe);
> +	if (!frozen_bus) {
> +		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
> +			__func__, pe->phb->global_number, pe->addr);
> +		return;
> +	}
> +
> +	pe->freeze_count++;
> +	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
> +		goto excess_failures;
> +	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
> +		pe->freeze_count);
> +
> +	/* Walk the various device drivers attached to this slot through
> +	 * a reset sequence, giving each an opportunity to do what it needs
> +	 * to accomplish the reset.  Each child gets a report of the
> +	 * status ... if any child can't handle the reset, then the entire
> +	 * slot is dlpar removed and added.
> +	 */
> +	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
> +
> +	/* Get the current PCI slot state. This can take a long time,
> +	 * sometimes over 3 seconds for certain systems.
> +	 */
> +	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
> +	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
> +		printk(KERN_WARNING "EEH: Permanent failure\n");
> +		goto hard_fail;
> +	}
> +
> +	/* Since rtas may enable MMIO when posting the error log,
> +	 * don't post the error log until after all dev drivers
> +	 * have been informed.
> +	 */
> +	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
> +
> +	/* If all device drivers were EEH-unaware, then shut
> +	 * down all of the device drivers, and hope they
> +	 * go down willingly, without panicing the system.
> +	 */
> +	if (result == PCI_ERS_RESULT_NONE) {
> +		rc = eeh_reset_device(pe, frozen_bus);
> +		if (rc) {
> +			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
> +			goto hard_fail;
> +		}
> +	}
> +
> +	/* If all devices reported they can proceed, then re-enable MMIO */
> +	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> +		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> +
> +		if (rc < 0)
> +			goto hard_fail;
> +		if (rc) {
> +			result = PCI_ERS_RESULT_NEED_RESET;
> +		} else {
> +			result = PCI_ERS_RESULT_NONE;
> +			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
> +		}
> +	}
> +
> +	/* If all devices reported they can proceed, then re-enable DMA */
> +	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> +		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
> +
> +		if (rc < 0)
> +			goto hard_fail;
> +		if (rc)
> +			result = PCI_ERS_RESULT_NEED_RESET;
> +		else
> +			result = PCI_ERS_RESULT_RECOVERED;
> +	}
> +
> +	/* If any device has a hard failure, then shut off everything. */
> +	if (result == PCI_ERS_RESULT_DISCONNECT) {
> +		printk(KERN_WARNING "EEH: Device driver gave up\n");
> +		goto hard_fail;
> +	}
> +
> +	/* If any device called out for a reset, then reset the slot */
> +	if (result == PCI_ERS_RESULT_NEED_RESET) {
> +		rc = eeh_reset_device(pe, NULL);
> +		if (rc) {
> +			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
> +			goto hard_fail;
> +		}
> +		result = PCI_ERS_RESULT_NONE;
> +		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
> +	}
> +
> +	/* All devices should claim they have recovered by now. */
> +	if ((result != PCI_ERS_RESULT_RECOVERED) &&
> +	    (result != PCI_ERS_RESULT_NONE)) {
> +		printk(KERN_WARNING "EEH: Not recovered\n");
> +		goto hard_fail;
> +	}
> +
> +	/* Tell all device drivers that they can resume operations */
> +	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
> +
> +	return;
> +	
> +excess_failures:
> +	/*
> +	 * About 90% of all real-life EEH failures in the field
> +	 * are due to poorly seated PCI cards. Only 10% or so are
> +	 * due to actual, failed cards.
> +	 */
> +	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
> +	       "last hour and has been permanently disabled.\n"
> +	       "Please try reseating or replacing it.\n",
> +		pe->phb->global_number, pe->addr,
> +		pe->freeze_count);
> +	goto perm_error;
> +
> +hard_fail:
> +	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
> +	       "Please try reseating or replacing it\n",
> +		pe->phb->global_number, pe->addr);
> +
> +perm_error:
> +	eeh_slot_error_detail(pe, EEH_LOG_PERM);
> +
> +	/* Notify all devices that they're about to go down. */
> +	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
> +
> +	/* Shut down the device drivers for good. */
> +	if (frozen_bus)
> +		pcibios_remove_pci_devices(frozen_bus);
> +}
> +
> diff --git a/arch/powerpc/kernel/eeh_event.c b/arch/powerpc/kernel/eeh_event.c
> new file mode 100644
> index 0000000..185bedd
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_event.c
> @@ -0,0 +1,142 @@
> +/*
> + * 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
> + *
> + * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <linux/kthread.h>
> +#include <asm/eeh_event.h>
> +#include <asm/ppc-pci.h>
> +
> +/** Overview:
> + *  EEH error states may be detected within exception handlers;
> + *  however, the recovery processing needs to occur asynchronously
> + *  in a normal kernel context and not an interrupt context.
> + *  This pair of routines creates an event and queues it onto a
> + *  work-queue, where a worker thread can drive recovery.
> + */
> +
> +/* EEH event workqueue setup. */
> +static DEFINE_SPINLOCK(eeh_eventlist_lock);
> +LIST_HEAD(eeh_eventlist);
> +static void eeh_thread_launcher(struct work_struct *);
> +DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
> +
> +/* Serialize reset sequences for a given pci device */
> +DEFINE_MUTEX(eeh_event_mutex);
> +
> +/**
> + * eeh_event_handler - Dispatch EEH events.
> + * @dummy - unused
> + *
> + * The detection of a frozen slot can occur inside an interrupt,
> + * where it can be hard to do anything about it.  The goal of this
> + * routine is to pull these detection events out of the context
> + * of the interrupt handler, and re-dispatch them for processing
> + * at a later time in a normal context.
> + */
> +static int eeh_event_handler(void * dummy)
> +{
> +	unsigned long flags;
> +	struct eeh_event *event;
> +	struct eeh_pe *pe;
> +
> +	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> +	event = NULL;
> +
> +	/* Unqueue the event, get ready to process. */
> +	if (!list_empty(&eeh_eventlist)) {
> +		event = list_entry(eeh_eventlist.next, struct eeh_event, list);
> +		list_del(&event->list);
> +	}
> +	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> +
> +	if (event == NULL)
> +		return 0;
> +
> +	/* Serialize processing of EEH events */
> +	mutex_lock(&eeh_event_mutex);
> +	pe = event->pe;
> +	eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
> +	pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
> +		pe->phb->global_number, pe->addr);
> +
> +	set_current_state(TASK_INTERRUPTIBLE);	/* Don't add to load average */
> +	eeh_handle_event(pe);
> +	eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> +
> +	kfree(event);
> +	mutex_unlock(&eeh_event_mutex);
> +
> +	/* If there are no new errors after an hour, clear the counter. */
> +	if (pe && pe->freeze_count > 0) {
> +		msleep_interruptible(3600*1000);
> +		if (pe->freeze_count > 0)
> +			pe->freeze_count--;
> +
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_thread_launcher - Start kernel thread to handle EEH events
> + * @dummy - unused
> + *
> + * This routine is called to start the kernel thread for processing
> + * EEH event.
> + */
> +static void eeh_thread_launcher(struct work_struct *dummy)
> +{
> +	if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
> +		printk(KERN_ERR "Failed to start EEH daemon\n");
> +}
> +
> +/**
> + * eeh_send_failure_event - Generate a PCI error event
> + * @pe: EEH PE
> + *
> + * 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 eeh_pe *pe)
> +{
> +	unsigned long flags;
> +	struct eeh_event *event;
> +
> +	event = kzalloc(sizeof(*event), GFP_ATOMIC);
> +	if (!event) {
> +		pr_err("EEH: out of memory, event not handled\n");
> +		return -ENOMEM;
> +	}
> +	event->pe = pe;
> +
> +	/* We may or may not be called in an interrupt context */
> +	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> +	list_add(&event->list, &eeh_eventlist);
> +	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> +
> +	schedule_work(&eeh_event_wq);
> +
> +	return 0;
> +}
> diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
> new file mode 100644
> index 0000000..9d4a9e8
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_pe.c
> @@ -0,0 +1,653 @@
> +/*
> + * The file intends to implement PE based on the information from
> + * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
> + * All the PEs should be organized as hierarchy tree. The first level
> + * of the tree will be associated to existing PHBs since the particular
> + * PE is only meaningful in one PHB domain.
> + *
> + * 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>
> +
> +static LIST_HEAD(eeh_phb_pe);
> +
> +/**
> + * eeh_pe_alloc - Allocate PE
> + * @phb: PCI controller
> + * @type: PE type
> + *
> + * Allocate PE instance dynamically.
> + */
> +static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
> +{
> +	struct eeh_pe *pe;
> +
> +	/* Allocate PHB PE */
> +	pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
> +	if (!pe) return NULL;
> +
> +	/* Initialize PHB PE */
> +	pe->type = type;
> +	pe->phb = phb;
> +	INIT_LIST_HEAD(&pe->child_list);
> +	INIT_LIST_HEAD(&pe->child);
> +	INIT_LIST_HEAD(&pe->edevs);
> +
> +	return pe;
> +}
> +
> +/**
> + * eeh_phb_pe_create - Create PHB PE
> + * @phb: PCI controller
> + *
> + * The function should be called while the PHB is detected during
> + * system boot or PCI hotplug in order to create PHB PE.
> + */
> +int eeh_phb_pe_create(struct pci_controller *phb)
> +{
> +	struct eeh_pe *pe;
> +
> +	/* Allocate PHB PE */
> +	pe = eeh_pe_alloc(phb, EEH_PE_PHB);
> +	if (!pe) {
> +		pr_err("%s: out of memory!\n", __func__);
> +		return -ENOMEM;
> +	}
> +
> +	/* Put it into the list */
> +	eeh_lock();
> +	list_add_tail(&pe->child, &eeh_phb_pe);
> +	eeh_unlock();
> +
> +	pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
> + * @phb: PCI controller
> + *
> + * The overall PEs form hierarchy tree. The first layer of the
> + * hierarchy tree is composed of PHB PEs. The function is used
> + * to retrieve the corresponding PHB PE according to the given PHB.
> + */
> +static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
> +{
> +	struct eeh_pe *pe;
> +
> +	list_for_each_entry(pe, &eeh_phb_pe, child) {
> +		/*
> +		 * Actually, we needn't check the type since
> +		 * the PE for PHB has been determined when that
> +		 * was created.
> +		 */
> +		if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
> +			return pe;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_next - Retrieve the next PE in the tree
> + * @pe: current PE
> + * @root: root PE
> + *
> + * The function is used to retrieve the next PE in the
> + * hierarchy PE tree.
> + */
> +static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
> +				  struct eeh_pe *root)
> +{
> +	struct list_head *next = pe->child_list.next;
> +
> +	if (next == &pe->child_list) {
> +		while (1) {
> +			if (pe == root)
> +				return NULL;
> +			next = pe->child.next;
> +			if (next != &pe->parent->child_list)
> +				break;
> +			pe = pe->parent;
> +		}
> +	}
> +
> +	return list_entry(next, struct eeh_pe, child);
> +}
> +
> +/**
> + * eeh_pe_traverse - Traverse PEs in the specified PHB
> + * @root: root PE
> + * @fn: callback
> + * @flag: extra parameter to callback
> + *
> + * The function is used to traverse the specified PE and its
> + * child PEs. The traversing is to be terminated once the
> + * callback returns something other than NULL, or no more PEs
> + * to be traversed.
> + */
> +static void *eeh_pe_traverse(struct eeh_pe *root,
> +			eeh_traverse_func fn, void *flag)
> +{
> +	struct eeh_pe *pe;
> +	void *ret;
> +
> +	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> +		ret = fn(pe, flag);
> +		if (ret) return ret;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_dev_traverse - Traverse the devices from the PE
> + * @root: EEH PE
> + * @fn: function callback
> + * @flag: extra parameter to callback
> + *
> + * The function is used to traverse the devices of the specified
> + * PE and its child PEs.
> + */
> +void *eeh_pe_dev_traverse(struct eeh_pe *root,
> +		eeh_traverse_func fn, void *flag)
> +{
> +	struct eeh_pe *pe;
> +	struct eeh_dev *edev;
> +	void *ret;
> +
> +	if (!root) {
> +		pr_warning("%s: Invalid PE %p\n", __func__, root);
> +		return NULL;
> +	}
> +
> +	eeh_lock();
> +
> +	/* Traverse root PE */
> +	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> +		eeh_pe_for_each_dev(pe, edev) {
> +			ret = fn(edev, flag);
> +			if (ret) {
> +				eeh_unlock();
> +				return ret;
> +			}
> +		}
> +	}
> +
> +	eeh_unlock();
> +
> +	return NULL;
> +}
> +
> +/**
> + * __eeh_pe_get - Check the PE address
> + * @data: EEH PE
> + * @flag: EEH device
> + *
> + * For one particular PE, it can be identified by PE address
> + * or tranditional BDF address. BDF address is composed of
> + * Bus/Device/Function number. The extra data referred by flag
> + * indicates which type of address should be used.
> + */
> +static void *__eeh_pe_get(void *data, void *flag)
> +{
> +	struct eeh_pe *pe = (struct eeh_pe *)data;
> +	struct eeh_dev *edev = (struct eeh_dev *)flag;
> +
> +	/* Unexpected PHB PE */
> +	if (pe->type & EEH_PE_PHB)
> +		return NULL;
> +
> +	/* We prefer PE address */
> +	if (edev->pe_config_addr &&
> +	   (edev->pe_config_addr == pe->addr))
> +		return pe;
> +
> +	/* Try BDF address */
> +	if (edev->pe_config_addr &&
> +	   (edev->config_addr == pe->config_addr))
> +		return pe;
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_get - Search PE based on the given address
> + * @edev: EEH device
> + *
> + * Search the corresponding PE based on the specified address which
> + * is included in the eeh device. The function is used to check if
> + * the associated PE has been created against the PE address. It's
> + * notable that the PE address has 2 format: traditional PE address
> + * which is composed of PCI bus/device/function number, or unified
> + * PE address.
> + */
> +static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
> +{
> +	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
> +	struct eeh_pe *pe;
> +
> +	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
> +
> +	return pe;
> +}
> +
> +/**
> + * eeh_pe_get_parent - Retrieve the parent PE
> + * @edev: EEH device
> + *
> + * The whole PEs existing in the system are organized as hierarchy
> + * tree. The function is used to retrieve the parent PE according
> + * to the parent EEH device.
> + */
> +static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> +{
> +	struct device_node *dn;
> +	struct eeh_dev *parent;
> +
> +	/*
> +	 * It might have the case for the indirect parent
> +	 * EEH device already having associated PE, but
> +	 * the direct parent EEH device doesn't have yet.
> +	 */
> +	dn = edev->dn->parent;
> +	while (dn) {
> +		/* We're poking out of PCI territory */
> +		if (!PCI_DN(dn)) return NULL;
> +
> +		parent = of_node_to_eeh_dev(dn);
> +		/* We're poking out of PCI territory */
> +		if (!parent) return NULL;
> +
> +		if (parent->pe)
> +			return parent->pe;
> +
> +		dn = dn->parent;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_add_to_parent_pe - Add EEH device to parent PE
> + * @edev: EEH device
> + *
> + * Add EEH device to the parent PE. If the parent PE already
> + * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
> + * we have to create new PE to hold the EEH device and the new
> + * PE will be linked to its parent PE as well.
> + */
> +int eeh_add_to_parent_pe(struct eeh_dev *edev)
> +{
> +	struct eeh_pe *pe, *parent;
> +
> +	eeh_lock();
> +
> +	/*
> +	 * Search the PE has been existing or not according
> +	 * to the PE address. If that has been existing, the
> +	 * PE should be composed of PCI bus and its subordinate
> +	 * components.
> +	 */
> +	pe = eeh_pe_get(edev);
> +	if (pe && !(pe->type & EEH_PE_INVALID)) {
> +		if (!edev->pe_config_addr) {
> +			eeh_unlock();
> +			pr_err("%s: PE with addr 0x%x already exists\n",
> +				__func__, edev->config_addr);
> +			return -EEXIST;
> +		}
> +
> +		/* Mark the PE as type of PCI bus */
> +		pe->type = EEH_PE_BUS;
> +		edev->pe = pe;
> +
> +		/* Put the edev to PE */
> +		list_add_tail(&edev->list, &pe->edevs);
> +		eeh_unlock();
> +		pr_debug("EEH: Add %s to Bus PE#%x\n",
> +			edev->dn->full_name, pe->addr);
> +
> +		return 0;
> +	} else if (pe && (pe->type & EEH_PE_INVALID)) {
> +		list_add_tail(&edev->list, &pe->edevs);
> +		edev->pe = pe;
> +		/*
> +		 * We're running to here because of PCI hotplug caused by
> +		 * EEH recovery. We need clear EEH_PE_INVALID until the top.
> +		 */
> +		parent = pe;
> +		while (parent) {
> +			if (!(parent->type & EEH_PE_INVALID))
> +				break;
> +			parent->type &= ~EEH_PE_INVALID;
> +			parent = parent->parent;
> +		}
> +		eeh_unlock();
> +		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> +			edev->dn->full_name, pe->addr, pe->parent->addr);
> +
> +		return 0;
> +	}
> +
> +	/* Create a new EEH PE */
> +	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
> +	if (!pe) {
> +		eeh_unlock();
> +		pr_err("%s: out of memory!\n", __func__);
> +		return -ENOMEM;
> +	}
> +	pe->addr	= edev->pe_config_addr;
> +	pe->config_addr	= edev->config_addr;
> +
> +	/*
> +	 * Put the new EEH PE into hierarchy tree. If the parent
> +	 * can't be found, the newly created PE will be attached
> +	 * to PHB directly. Otherwise, we have to associate the
> +	 * PE with its parent.
> +	 */
> +	parent = eeh_pe_get_parent(edev);
> +	if (!parent) {
> +		parent = eeh_phb_pe_get(edev->phb);
> +		if (!parent) {
> +			eeh_unlock();
> +			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
> +				__func__, edev->phb->global_number);
> +			edev->pe = NULL;
> +			kfree(pe);
> +			return -EEXIST;
> +		}
> +	}
> +	pe->parent = parent;
> +
> +	/*
> +	 * Put the newly created PE into the child list and
> +	 * link the EEH device accordingly.
> +	 */
> +	list_add_tail(&pe->child, &parent->child_list);
> +	list_add_tail(&edev->list, &pe->edevs);
> +	edev->pe = pe;
> +	eeh_unlock();
> +	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> +		edev->dn->full_name, pe->addr, pe->parent->addr);
> +
> +	return 0;
> +}
> +
> +/**
> + * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
> + * @edev: EEH device
> + * @purge_pe: remove PE or not
> + *
> + * The PE hierarchy tree might be changed when doing PCI hotplug.
> + * Also, the PCI devices or buses could be removed from the system
> + * during EEH recovery. So we have to call the function remove the
> + * corresponding PE accordingly if necessary.
> + */
> +int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
> +{
> +	struct eeh_pe *pe, *parent, *child;
> +	int cnt;
> +
> +	if (!edev->pe) {
> +		pr_warning("%s: No PE found for EEH device %s\n",
> +			__func__, edev->dn->full_name);
> +		return -EEXIST;
> +	}
> +
> +	eeh_lock();
> +
> +	/* Remove the EEH device */
> +	pe = edev->pe;
> +	edev->pe = NULL;
> +	list_del(&edev->list);
> +
> +	/*
> +	 * Check if the parent PE includes any EEH devices.
> +	 * If not, we should delete that. Also, we should
> +	 * delete the parent PE if it doesn't have associated
> +	 * child PEs and EEH devices.
> +	 */
> +	while (1) {
> +		parent = pe->parent;
> +		if (pe->type & EEH_PE_PHB)
> +			break;
> +
> +		if (purge_pe) {
> +			if (list_empty(&pe->edevs) &&
> +			    list_empty(&pe->child_list)) {
> +				list_del(&pe->child);
> +				kfree(pe);
> +			} else {
> +				break;
> +			}
> +		} else {
> +			if (list_empty(&pe->edevs)) {
> +				cnt = 0;
> +				list_for_each_entry(child, &pe->child_list, child) {
> +					if (!(child->type & EEH_PE_INVALID)) {
> +						cnt++;
> +						break;
> +					}
> +				}
> +
> +				if (!cnt)
> +					pe->type |= EEH_PE_INVALID;
> +				else
> +					break;
> +			}
> +		}
> +
> +		pe = parent;
> +	}
> +
> +	eeh_unlock();
> +
> +	return 0;
> +}
> +
> +/**
> + * __eeh_pe_state_mark - Mark the state for the PE
> + * @data: EEH PE
> + * @flag: state
> + *
> + * The function is used to mark the indicated state for the given
> + * PE. Also, the associated PCI devices will be put into IO frozen
> + * state as well.
> + */
> +static void *__eeh_pe_state_mark(void *data, void *flag)
> +{
> +	struct eeh_pe *pe = (struct eeh_pe *)data;
> +	int state = *((int *)flag);
> +	struct eeh_dev *tmp;
> +	struct pci_dev *pdev;
> +
> +	/*
> +	 * Mark the PE with the indicated state. Also,
> +	 * the associated PCI device will be put into
> +	 * I/O frozen state to avoid I/O accesses from
> +	 * the PCI device driver.
> +	 */
> +	pe->state |= state;
> +	eeh_pe_for_each_dev(pe, tmp) {
> +		pdev = eeh_dev_to_pci_dev(tmp);
> +		if (pdev)
> +			pdev->error_state = pci_channel_io_frozen;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_state_mark - Mark specified state for PE and its associated device
> + * @pe: EEH PE
> + *
> + * EEH error affects the current PE and its child PEs. The function
> + * is used to mark appropriate state for the affected PEs and the
> + * associated devices.
> + */
> +void eeh_pe_state_mark(struct eeh_pe *pe, int state)
> +{
> +	eeh_lock();
> +	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
> +	eeh_unlock();
> +}
> +
> +/**
> + * __eeh_pe_state_clear - Clear state for the PE
> + * @data: EEH PE
> + * @flag: state
> + *
> + * The function is used to clear the indicated state from the
> + * given PE. Besides, we also clear the check count of the PE
> + * as well.
> + */
> +static void *__eeh_pe_state_clear(void *data, void *flag)
> +{
> +	struct eeh_pe *pe = (struct eeh_pe *)data;
> +	int state = *((int *)flag);
> +
> +	pe->state &= ~state;
> +	pe->check_count = 0;
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_state_clear - Clear state for the PE and its children
> + * @pe: PE
> + * @state: state to be cleared
> + *
> + * When the PE and its children has been recovered from error,
> + * we need clear the error state for that. The function is used
> + * for the purpose.
> + */
> +void eeh_pe_state_clear(struct eeh_pe *pe, int state)
> +{
> +	eeh_lock();
> +	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
> +	eeh_unlock();
> +}
> +
> +/**
> + * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
> + * @data: EEH device
> + * @flag: Unused
> + *
> + * Loads the PCI configuration space base address registers,
> + * the expansion ROM base address, the latency timer, and etc.
> + * from the saved values in the device node.
> + */
> +static void *eeh_restore_one_device_bars(void *data, void *flag)
> +{
> +	int i;
> +	u32 cmd;
> +	struct eeh_dev *edev = (struct eeh_dev *)data;
> +	struct device_node *dn = eeh_dev_to_of_node(edev);
> +
> +	for (i = 4; i < 10; i++)
> +		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
> +	/* 12 == Expansion ROM Address */
> +	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
> +
> +#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
> +#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
> +
> +	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
> +		SAVED_BYTE(PCI_CACHE_LINE_SIZE));
> +	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
> +		SAVED_BYTE(PCI_LATENCY_TIMER));
> +
> +	/* max latency, min grant, interrupt pin and line */
> +	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
> +
> +	/*
> +	 * Restore PERR & SERR bits, some devices require it,
> +	 * don't touch the other command bits
> +	 */
> +	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
> +	if (edev->config_space[1] & PCI_COMMAND_PARITY)
> +		cmd |= PCI_COMMAND_PARITY;
> +	else
> +		cmd &= ~PCI_COMMAND_PARITY;
> +	if (edev->config_space[1] & PCI_COMMAND_SERR)
> +		cmd |= PCI_COMMAND_SERR;
> +	else
> +		cmd &= ~PCI_COMMAND_SERR;
> +	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
> +
> +	return NULL;
> +}
> +
> +/**
> + * eeh_pe_restore_bars - Restore the PCI config space info
> + * @pe: EEH PE
> + *
> + * This routine performs a recursive walk to the children
> + * of this device as well.
> + */
> +void eeh_pe_restore_bars(struct eeh_pe *pe)
> +{
> +	/*
> +	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
> +	 * will take that.
> +	 */
> +	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
> +}
> +
> +/**
> + * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
> + * @pe: EEH PE
> + *
> + * Retrieve the PCI bus according to the given PE. Basically,
> + * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
> + * primary PCI bus will be retrieved. The parent bus will be
> + * returned for BUS PE. However, we don't have associated PCI
> + * bus for DEVICE PE.
> + */
> +struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> +{
> +	struct pci_bus *bus = NULL;
> +	struct eeh_dev *edev;
> +	struct pci_dev *pdev;
> +
> +	eeh_lock();
> +
> +	if (pe->type & EEH_PE_PHB) {
> +		bus = pe->phb->bus;
> +	} else if (pe->type & EEH_PE_BUS ||
> +		   pe->type & EEH_PE_DEVICE) {
> +		edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> +		pdev = eeh_dev_to_pci_dev(edev);
> +		if (pdev)
> +			bus = pdev->bus;
> +	}
> +
> +	eeh_unlock();
> +
> +	return bus;
> +}
> diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
> new file mode 100644
> index 0000000..d377083
> --- /dev/null
> +++ b/arch/powerpc/kernel/eeh_sysfs.c
> @@ -0,0 +1,75 @@
> +/*
> + * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
> + * Copyright IBM Corporation 2007
> + * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
> + *
> + * All rights reserved.
> + *
> + * 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, GOOD TITLE or
> + * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> + */
> +#include <linux/pci.h>
> +#include <linux/stat.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/pci-bridge.h>
> +
> +/**
> + * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
> + * @_name: name of file in sysfs directory
> + * @_memb: name of member in struct pci_dn to access
> + * @_format: printf format for display
> + *
> + * All of the attributes look very similar, so just
> + * auto-gen a cut-n-paste routine to display them.
> + */
> +#define EEH_SHOW_ATTR(_name,_memb,_format)               \
> +static ssize_t eeh_show_##_name(struct device *dev,      \
> +		struct device_attribute *attr, char *buf)          \
> +{                                                        \
> +	struct pci_dev *pdev = to_pci_dev(dev);               \
> +	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
> +	                                                      \
> +	if (!edev)                                            \
> +		return 0;                                     \
> +	                                                      \
> +	return sprintf(buf, _format "\n", edev->_memb);       \
> +}                                                        \
> +static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
> +
> +EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
> +EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
> +EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
> +
> +void eeh_sysfs_add_device(struct pci_dev *pdev)
> +{
> +	int rc=0;
> +
> +	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
> +	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
> +	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> +
> +	if (rc)
> +		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
> +}
> +
> +void eeh_sysfs_remove_device(struct pci_dev *pdev)
> +{
> +	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
> +	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
> +	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> +}
> +
> diff --git a/arch/powerpc/kernel/pci_hotplug.c b/arch/powerpc/kernel/pci_hotplug.c
> new file mode 100644
> index 0000000..3f60880
> --- /dev/null
> +++ b/arch/powerpc/kernel/pci_hotplug.c
> @@ -0,0 +1,111 @@
> +/*
> + * Derived from "arch/powerpc/platforms/pseries/pci_dlpar.c"
> + *
> + * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
> + * Copyright (C) 2005 International Business Machines
> + *
> + * Updates, 2005, John Rose <johnrose@austin.ibm.com>
> + * Updates, 2005, Linas Vepstas <linas@austin.ibm.com>
> + * Updates, 2013, Gavin Shan <shangw@linux.vnet.ibm.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/export.h>
> +#include <asm/pci-bridge.h>
> +#include <asm/ppc-pci.h>
> +#include <asm/firmware.h>
> +#include <asm/eeh.h>
> +
> +/**
> + * __pcibios_remove_pci_devices - remove all devices under this bus
> + * @bus: the indicated PCI bus
> + * @purge_pe: destroy the PE on removal of PCI devices
> + *
> + * Remove all of the PCI devices under this bus both from the
> + * linux pci device tree, and from the powerpc EEH address cache.
> + * By default, the corresponding PE will be destroied during the
> + * normal PCI hotplug path. For PCI hotplug during EEH recovery,
> + * the corresponding PE won't be destroied and deallocated.
> + */
> +void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
> +{
> +	struct pci_dev *dev, *tmp;
> +	struct pci_bus *child_bus;
> +
> +	/* First go down child busses */
> +	list_for_each_entry(child_bus, &bus->children, node)
> +		__pcibios_remove_pci_devices(child_bus, purge_pe);
> +
> +	pr_debug("PCI: Removing devices on bus %04x:%02x\n",
> +		 pci_domain_nr(bus),  bus->number);
> +	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
> +		pr_debug("     * Removing %s...\n", pci_name(dev));
> +		eeh_remove_bus_device(dev, purge_pe);
> +		pci_stop_and_remove_bus_device(dev);
> +	}
> +}
> +
> +/**
> + * pcibios_remove_pci_devices - remove all devices under this bus
> + * @bus: the indicated PCI bus
> + *
> + * Remove all of the PCI devices under this bus both from the
> + * linux pci device tree, and from the powerpc EEH address cache.
> + */
> +void pcibios_remove_pci_devices(struct pci_bus *bus)
> +{
> +	__pcibios_remove_pci_devices(bus, 1);
> +}
> +EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
> +
> +/**
> + * pcibios_add_pci_devices - adds new pci devices to bus
> + * @bus: the indicated PCI bus
> + *
> + * This routine will find and fixup new pci devices under
> + * the indicated bus. This routine presumes that there
> + * might already be some devices under this bridge, so
> + * it carefully tries to add only new devices.  (And that
> + * is how this routine differs from other, similar pcibios
> + * routines.)
> + */
> +void pcibios_add_pci_devices(struct pci_bus * bus)
> +{
> +	int slotno, num, mode, pass, max;
> +	struct pci_dev *dev;
> +	struct device_node *dn = pci_bus_to_OF_node(bus);
> +
> +	eeh_add_device_tree_early(dn);
> +
> +	mode = PCI_PROBE_NORMAL;
> +	if (ppc_md.pci_probe_mode)
> +		mode = ppc_md.pci_probe_mode(bus);
> +
> +	if (mode == PCI_PROBE_DEVTREE) {
> +		/* use ofdt-based probe */
> +		of_rescan_bus(dn, bus);
> +	} else if (mode == PCI_PROBE_NORMAL) {
> +		/* use legacy probe */
> +		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
> +		num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
> +		if (!num)
> +			return;
> +		pcibios_setup_bus_devices(bus);
> +		max = bus->busn_res.start;
> +		for (pass = 0; pass < 2; pass++) {
> +			list_for_each_entry(dev, &bus->devices, bus_list) {
> +				if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
> +				    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
> +					max = pci_scan_bridge(bus, dev,
> +							      max, pass);
> +			}
> +		}
> +	}
> +	pcibios_finish_adding_to_bus(bus);
> +}
> +EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
> diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
> index b62aab3..bed8c60 100644
> --- a/arch/powerpc/platforms/Kconfig
> +++ b/arch/powerpc/platforms/Kconfig
> @@ -164,6 +164,11 @@ config IBMEBUS
>  	help
>  	  Bus device driver for GX bus based adapters.
>  
> +config EEH
> +	bool
> +	depends on (PPC_POWERNV || PPC_PSERIES) && PCI
> +	default y
> +
>  config PPC_MPC106
>  	bool
>  	default n
> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index 4459eff..1bd3399 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -33,11 +33,6 @@ config PPC_SPLPAR
>  	  processors, that is, which share physical processors between
>  	  two or more partitions.
>  
> -config EEH
> -	bool
> -	depends on PPC_PSERIES && PCI
> -	default y
> -
>  config PSERIES_MSI
>         bool
>         depends on PCI_MSI && EEH
> diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
> index 53866e5..8ae0103 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -6,9 +6,7 @@ obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
>  			   firmware.o power.o dlpar.o mobility.o
>  obj-$(CONFIG_SMP)	+= smp.o
>  obj-$(CONFIG_SCANLOG)	+= scanlog.o
> -obj-$(CONFIG_EEH)	+= eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
> -			   eeh_driver.o eeh_event.o eeh_sysfs.o \
> -			   eeh_pseries.o
> +obj-$(CONFIG_EEH)	+= eeh_pseries.o
>  obj-$(CONFIG_KEXEC)	+= kexec.o
>  obj-$(CONFIG_PCI)	+= pci.o pci_dlpar.o
>  obj-$(CONFIG_PSERIES_MSI)	+= msi.o
> diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
> deleted file mode 100644
> index 6b73d6c..0000000
> --- a/arch/powerpc/platforms/pseries/eeh.c
> +++ /dev/null
> @@ -1,942 +0,0 @@
> -/*
> - * Copyright IBM Corporation 2001, 2005, 2006
> - * Copyright Dave Engebretsen & Todd Inglett 2001
> - * Copyright Linas Vepstas 2005, 2006
> - * Copyright 2001-2012 IBM Corporation.
> - *
> - * 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
> - *
> - * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -
> -#include <linux/delay.h>
> -#include <linux/sched.h>
> -#include <linux/init.h>
> -#include <linux/list.h>
> -#include <linux/pci.h>
> -#include <linux/proc_fs.h>
> -#include <linux/rbtree.h>
> -#include <linux/seq_file.h>
> -#include <linux/spinlock.h>
> -#include <linux/export.h>
> -#include <linux/of.h>
> -
> -#include <linux/atomic.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>
> -
> -
> -/** Overview:
> - *  EEH, or "Extended Error Handling" is a PCI bridge technology for
> - *  dealing with PCI bus errors that can't be dealt with within the
> - *  usual PCI framework, except by check-stopping the CPU.  Systems
> - *  that are designed for high-availability/reliability cannot afford
> - *  to crash due to a "mere" PCI error, thus the need for EEH.
> - *  An EEH-capable bridge operates by converting a detected error
> - *  into a "slot freeze", taking the PCI adapter off-line, making
> - *  the slot behave, from the OS'es point of view, as if the slot
> - *  were "empty": all reads return 0xff's and all writes are silently
> - *  ignored.  EEH slot isolation events can be triggered by parity
> - *  errors on the address or data busses (e.g. during posted writes),
> - *  which in turn might be caused by low voltage on the bus, dust,
> - *  vibration, humidity, radioactivity or plain-old failed hardware.
> - *
> - *  Note, however, that one of the leading causes of EEH slot
> - *  freeze events are buggy device drivers, buggy device microcode,
> - *  or buggy device hardware.  This is because any attempt by the
> - *  device to bus-master data to a memory address that is not
> - *  assigned to the device will trigger a slot freeze.   (The idea
> - *  is to prevent devices-gone-wild from corrupting system memory).
> - *  Buggy hardware/drivers will have a miserable time co-existing
> - *  with EEH.
> - *
> - *  Ideally, a PCI device driver, when suspecting that an isolation
> - *  event has occurred (e.g. by reading 0xff's), will then ask EEH
> - *  whether this is the case, and then take appropriate steps to
> - *  reset the PCI slot, the PCI device, and then resume operations.
> - *  However, until that day,  the checking is done here, with the
> - *  eeh_check_failure() routine embedded in the MMIO macros.  If
> - *  the slot is found to be isolated, an "EEH Event" is synthesized
> - *  and sent out for processing.
> - */
> -
> -/* If a device driver keeps reading an MMIO register in an interrupt
> - * handler after a slot isolation event, it might be broken.
> - * This sets the threshold for how many read attempts we allow
> - * before printing an error message.
> - */
> -#define EEH_MAX_FAILS	2100000
> -
> -/* Time to wait for a PCI slot to report status, in milliseconds */
> -#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
> -
> -/* Platform dependent EEH operations */
> -struct eeh_ops *eeh_ops = NULL;
> -
> -int eeh_subsystem_enabled;
> -EXPORT_SYMBOL(eeh_subsystem_enabled);
> -
> -/*
> - * EEH probe mode support. The intention is to support multiple
> - * platforms for EEH. Some platforms like pSeries do PCI emunation
> - * based on device tree. However, other platforms like powernv probe
> - * PCI devices from hardware. The flag is used to distinguish that.
> - * In addition, struct eeh_ops::probe would be invoked for particular
> - * OF node or PCI device so that the corresponding PE would be created
> - * there.
> - */
> -int eeh_probe_mode;
> -
> -/* Global EEH mutex */
> -DEFINE_MUTEX(eeh_mutex);
> -
> -/* Lock to avoid races due to multiple reports of an error */
> -static DEFINE_RAW_SPINLOCK(confirm_error_lock);
> -
> -/* Buffer for reporting pci register dumps. Its here in BSS, and
> - * not dynamically alloced, so that it ends up in RMO where RTAS
> - * can access it.
> - */
> -#define EEH_PCI_REGS_LOG_LEN 4096
> -static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
> -
> -/*
> - * The struct is used to maintain the EEH global statistic
> - * information. Besides, the EEH global statistics will be
> - * exported to user space through procfs
> - */
> -struct eeh_stats {
> -	u64 no_device;		/* PCI device not found		*/
> -	u64 no_dn;		/* OF node not found		*/
> -	u64 no_cfg_addr;	/* Config address not found	*/
> -	u64 ignored_check;	/* EEH check skipped		*/
> -	u64 total_mmio_ffs;	/* Total EEH checks		*/
> -	u64 false_positives;	/* Unnecessary EEH checks	*/
> -	u64 slot_resets;	/* PE reset			*/
> -};
> -
> -static struct eeh_stats eeh_stats;
> -
> -#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
> -
> -/**
> - * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
> - * @edev: device to report data for
> - * @buf: point to buffer in which to log
> - * @len: amount of room in buffer
> - *
> - * This routine captures assorted PCI configuration space data,
> - * and puts them into a buffer for RTAS error logging.
> - */
> -static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
> -{
> -	struct device_node *dn = eeh_dev_to_of_node(edev);
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	u32 cfg;
> -	int cap, i;
> -	int n = 0;
> -
> -	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
> -	printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
> -
> -	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
> -	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
> -	printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
> -
> -	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
> -	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
> -	printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
> -
> -	if (!dev) {
> -		printk(KERN_WARNING "EEH: no PCI device for this of node\n");
> -		return n;
> -	}
> -
> -	/* Gather bridge-specific registers */
> -	if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
> -		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
> -		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
> -
> -		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
> -		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
> -	}
> -
> -	/* Dump out the PCI-X command and status regs */
> -	cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
> -	if (cap) {
> -		eeh_ops->read_config(dn, cap, 4, &cfg);
> -		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
> -
> -		eeh_ops->read_config(dn, cap+4, 4, &cfg);
> -		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
> -		printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
> -	}
> -
> -	/* If PCI-E capable, dump PCI-E cap 10, and the AER */
> -	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
> -	if (cap) {
> -		n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
> -		printk(KERN_WARNING
> -		       "EEH: PCI-E capabilities and status follow:\n");
> -
> -		for (i=0; i<=8; i++) {
> -			eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
> -			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
> -			printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
> -		}
> -
> -		cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
> -		if (cap) {
> -			n += scnprintf(buf+n, len-n, "pci-e AER:\n");
> -			printk(KERN_WARNING
> -			       "EEH: PCI-E AER capability register set follows:\n");
> -
> -			for (i=0; i<14; i++) {
> -				eeh_ops->read_config(dn, cap+4*i, 4, &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);
> -			}
> -		}
> -	}
> -
> -	return n;
> -}
> -
> -/**
> - * eeh_slot_error_detail - Generate combined log including driver log and error log
> - * @pe: EEH PE
> - * @severity: temporary or permanent error log
> - *
> - * This routine should be called to generate the combined log, which
> - * is comprised of driver log and error log. The driver log is figured
> - * out from the config space of the corresponding PCI device, while
> - * the error log is fetched through platform dependent function call.
> - */
> -void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
> -{
> -	size_t loglen = 0;
> -	struct eeh_dev *edev;
> -
> -	eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> -	eeh_ops->configure_bridge(pe);
> -	eeh_pe_restore_bars(pe);
> -
> -	pci_regs_buf[0] = 0;
> -	eeh_pe_for_each_dev(pe, edev) {
> -		loglen += eeh_gather_pci_data(edev, pci_regs_buf,
> -				EEH_PCI_REGS_LOG_LEN);
> -        }
> -
> -	eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
> -}
> -
> -/**
> - * eeh_token_to_phys - Convert EEH address token to phys address
> - * @token: I/O token, should be address in the form 0xA....
> - *
> - * This routine should be called to convert virtual I/O address
> - * to physical one.
> - */
> -static inline unsigned long eeh_token_to_phys(unsigned long token)
> -{
> -	pte_t *ptep;
> -	unsigned long pa;
> -
> -	ptep = find_linux_pte(init_mm.pgd, token);
> -	if (!ptep)
> -		return token;
> -	pa = pte_pfn(*ptep) << PAGE_SHIFT;
> -
> -	return pa | (token & (PAGE_SIZE-1));
> -}
> -
> -/**
> - * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
> - * @edev: eeh device
> - *
> - * Check for an EEH failure for the given device node.  Call this
> - * routine if the result of a read was all 0xff's and you want to
> - * find out if this is due to an EEH slot freeze.  This routine
> - * will query firmware for the EEH status.
> - *
> - * Returns 0 if there has not been an EEH error; otherwise returns
> - * a non-zero value and queues up a slot isolation event notification.
> - *
> - * It is safe to call this routine in an interrupt context.
> - */
> -int eeh_dev_check_failure(struct eeh_dev *edev)
> -{
> -	int ret;
> -	unsigned long flags;
> -	struct device_node *dn;
> -	struct pci_dev *dev;
> -	struct eeh_pe *pe;
> -	int rc = 0;
> -	const char *location;
> -
> -	eeh_stats.total_mmio_ffs++;
> -
> -	if (!eeh_subsystem_enabled)
> -		return 0;
> -
> -	if (!edev) {
> -		eeh_stats.no_dn++;
> -		return 0;
> -	}
> -	dn = eeh_dev_to_of_node(edev);
> -	dev = eeh_dev_to_pci_dev(edev);
> -	pe = edev->pe;
> -
> -	/* Access to IO BARs might get this far and still not want checking. */
> -	if (!pe) {
> -		eeh_stats.ignored_check++;
> -		pr_debug("EEH: Ignored check for %s %s\n",
> -			eeh_pci_name(dev), dn->full_name);
> -		return 0;
> -	}
> -
> -	if (!pe->addr && !pe->config_addr) {
> -		eeh_stats.no_cfg_addr++;
> -		return 0;
> -	}
> -
> -	/* If we already have a pending isolation event for this
> -	 * slot, we know it's bad already, we don't need to check.
> -	 * Do this checking under a lock; as multiple PCI devices
> -	 * in one slot might report errors simultaneously, and we
> -	 * only want one error recovery routine running.
> -	 */
> -	raw_spin_lock_irqsave(&confirm_error_lock, flags);
> -	rc = 1;
> -	if (pe->state & EEH_PE_ISOLATED) {
> -		pe->check_count++;
> -		if (pe->check_count % EEH_MAX_FAILS == 0) {
> -			location = of_get_property(dn, "ibm,loc-code", NULL);
> -			printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
> -				"location=%s driver=%s pci addr=%s\n",
> -				pe->check_count, location,
> -				eeh_driver_name(dev), eeh_pci_name(dev));
> -			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
> -				eeh_driver_name(dev));
> -			dump_stack();
> -		}
> -		goto dn_unlock;
> -	}
> -
> -	/*
> -	 * Now test for an EEH failure.  This is VERY expensive.
> -	 * Note that the eeh_config_addr may be a parent device
> -	 * in the case of a device behind a bridge, or it may be
> -	 * function zero of a multi-function device.
> -	 * In any case they must share a common PHB.
> -	 */
> -	ret = eeh_ops->get_state(pe, NULL);
> -
> -	/* Note that config-io to empty slots may fail;
> -	 * they are empty when they don't have children.
> -	 * We will punt with the following conditions: Failure to get
> -	 * PE's state, EEH not support and Permanently unavailable
> -	 * state, PE is in good state.
> -	 */
> -	if ((ret < 0) ||
> -	    (ret == EEH_STATE_NOT_SUPPORT) ||
> -	    (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
> -	    (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
> -		eeh_stats.false_positives++;
> -		pe->false_positives++;
> -		rc = 0;
> -		goto dn_unlock;
> -	}
> -
> -	eeh_stats.slot_resets++;
> - 
> -	/* Avoid repeated reports of this failure, including problems
> -	 * with other functions on this device, and functions under
> -	 * bridges.
> -	 */
> -	eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
> -	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> -
> -	eeh_send_failure_event(pe);
> -
> -	/* Most EEH events are due to device driver bugs.  Having
> -	 * a stack trace will help the device-driver authors figure
> -	 * out what happened.  So print that out.
> -	 */
> -	WARN(1, "EEH: failure detected\n");
> -	return 1;
> -
> -dn_unlock:
> -	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
> -	return rc;
> -}
> -
> -EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
> -
> -/**
> - * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
> - * @token: I/O token, should be address in the form 0xA....
> - * @val: value, should be all 1's (XXX why do we need this arg??)
> - *
> - * Check for an EEH failure at the given token address.  Call this
> - * routine if the result of a read was all 0xff's and you want to
> - * find out if this is due to an EEH slot freeze event.  This routine
> - * will query firmware for the EEH status.
> - *
> - * Note this routine is safe to call in an interrupt context.
> - */
> -unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
> -{
> -	unsigned long addr;
> -	struct eeh_dev *edev;
> -
> -	/* Finding the phys addr + pci device; this is pretty quick. */
> -	addr = eeh_token_to_phys((unsigned long __force) token);
> -	edev = eeh_addr_cache_get_dev(addr);
> -	if (!edev) {
> -		eeh_stats.no_device++;
> -		return val;
> -	}
> -
> -	eeh_dev_check_failure(edev);
> -
> -	pci_dev_put(eeh_dev_to_pci_dev(edev));
> -	return val;
> -}
> -
> -EXPORT_SYMBOL(eeh_check_failure);
> -
> -
> -/**
> - * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
> - * @pe: EEH PE
> - *
> - * 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_pe *pe, int function)
> -{
> -	int rc;
> -
> -	rc = eeh_ops->set_option(pe, function);
> -	if (rc)
> -		pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
> -			__func__, function, pe->phb->global_number, pe->addr, rc);
> -
> -	rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> -	if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
> -	   (function == EEH_OPT_THAW_MMIO))
> -		return 0;
> -
> -	return rc;
> -}
> -
> -/**
> - * pcibios_set_pcie_slot_reset - Set PCI-E reset state
> - * @dev: pci device struct
> - * @state: reset state to enter
> - *
> - * Return value:
> - * 	0 if success
> - */
> -int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
> -{
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -	struct eeh_pe *pe = edev->pe;
> -
> -	if (!pe) {
> -		pr_err("%s: No PE found on PCI device %s\n",
> -			__func__, pci_name(dev));
> -		return -EINVAL;
> -	}
> -
> -	switch (state) {
> -	case pcie_deassert_reset:
> -		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> -		break;
> -	case pcie_hot_reset:
> -		eeh_ops->reset(pe, EEH_RESET_HOT);
> -		break;
> -	case pcie_warm_reset:
> -		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> -		break;
> -	default:
> -		return -EINVAL;
> -	};
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_set_pe_freset - Check the required reset for the indicated device
> - * @data: EEH device
> - * @flag: 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.
> - */
> -static void *eeh_set_dev_freset(void *data, void *flag)
> -{
> -	struct pci_dev *dev;
> -	unsigned int *freset = (unsigned int *)flag;
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -
> -	dev = eeh_dev_to_pci_dev(edev);
> -	if (dev)
> -		*freset |= dev->needs_freset;
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
> - * @pe: EEH PE
> - *
> - * Assert the PCI #RST line for 1/4 second.
> - */
> -static void eeh_reset_pe_once(struct eeh_pe *pe)
> -{
> -	unsigned int freset = 0;
> -
> -	/* Determine type of EEH reset required for
> -	 * Partitionable Endpoint, a hot-reset (1)
> -	 * or a fundamental reset (3).
> -	 * A fundamental reset required by any device under
> -	 * Partitionable Endpoint trumps hot-reset.
> -  	 */
> -	eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
> -
> -	if (freset)
> -		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
> -	else
> -		eeh_ops->reset(pe, EEH_RESET_HOT);
> -
> -	/* The PCI bus requires that the reset be held high for at least
> -	 * a 100 milliseconds. We wait a bit longer 'just in case'.
> -	 */
> -#define PCI_BUS_RST_HOLD_TIME_MSEC 250
> -	msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
> -	
> -	/* We might get hit with another EEH freeze as soon as the 
> -	 * pci slot reset line is dropped. Make sure we don't miss
> -	 * these, and clear the flag now.
> -	 */
> -	eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
> -
> -	eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
> -
> -	/* 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
> -	 * up traffic.
> -	 */
> -#define PCI_BUS_SETTLE_TIME_MSEC 1800
> -	msleep(PCI_BUS_SETTLE_TIME_MSEC);
> -}
> -
> -/**
> - * eeh_reset_pe - Reset the indicated PE
> - * @pe: EEH PE
> - *
> - * 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_pe *pe)
> -{
> -	int i, rc;
> -
> -	/* Take three shots at resetting the bus */
> -	for (i=0; i<3; i++) {
> -		eeh_reset_pe_once(pe);
> -
> -		rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
> -		if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
> -			return 0;
> -
> -		if (rc < 0) {
> -			pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
> -				__func__, pe->phb->global_number, pe->addr);
> -			return -1;
> -		}
> -		pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
> -			i+1, pe->phb->global_number, pe->addr, rc);
> -	}
> -
> -	return -1;
> -}
> -
> -/**
> - * eeh_save_bars - Save device bars
> - * @edev: PCI device associated EEH device
> - *
> - * Save the values of the device bars. Unlike the restore
> - * routine, this routine is *not* recursive. This is because
> - * PCI devices are added individually; but, for the restore,
> - * an entire slot is reset at a time.
> - */
> -void eeh_save_bars(struct eeh_dev *edev)
> -{
> -	int i;
> -	struct device_node *dn;
> -
> -	if (!edev)
> -		return;
> -	dn = eeh_dev_to_of_node(edev);
> -	
> -	for (i = 0; i < 16; i++)
> -		eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
> -}
> -
> -/**
> - * 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.
> - * As a side effect we can determine here if eeh is supported at all.
> - * Note that we leave EEH on so failed config cycles won't cause a machine
> - * check.  If a user turns off EEH for a particular adapter they are really
> - * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't
> - * grant access to a slot if EEH isn't enabled, and so we always enable
> - * EEH for all slots/all devices.
> - *
> - * The eeh-force-off option disables EEH checking globally, for all slots.
> - * Even if force-off is set, the EEH hardware is still enabled, so that
> - * newer systems can boot.
> - */
> -static int __init eeh_init(void)
> -{
> -	struct pci_controller *hose, *tmp;
> -	struct device_node *phb;
> -	int ret;
> -
> -	/* call platform initialization function */
> -	if (!eeh_ops) {
> -		pr_warning("%s: Platform EEH operation not found\n",
> -			__func__);
> -		return -EEXIST;
> -	} else if ((ret = eeh_ops->init())) {
> -		pr_warning("%s: Failed to call platform init function (%d)\n",
> -			__func__, ret);
> -		return ret;
> -	}
> -
> -	raw_spin_lock_init(&confirm_error_lock);
> -
> -	/* Enable EEH for all adapters */
> -	if (eeh_probe_mode_devtree()) {
> -		list_for_each_entry_safe(hose, tmp,
> -			&hose_list, list_node) {
> -			phb = hose->dn;
> -			traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
> -		}
> -	}
> -
> -	if (eeh_subsystem_enabled)
> -		pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
> -	else
> -		pr_warning("EEH: No capable adapters found\n");
> -
> -	return ret;
> -}
> -
> -core_initcall_sync(eeh_init);
> -
> -/**
> - * eeh_add_device_early - Enable EEH for the indicated device_node
> - * @dn: device node for which to set up EEH
> - *
> - * This routine must be used to perform EEH initialization for PCI
> - * devices that were added after system boot (e.g. hotplug, dlpar).
> - * This routine must be called before any i/o is performed to the
> - * adapter (inluding any config-space i/o).
> - * Whether this actually enables EEH or not for this device depends
> - * on the CEC architecture, type of the device, on earlier boot
> - * command-line arguments & etc.
> - */
> -static void eeh_add_device_early(struct device_node *dn)
> -{
> -	struct pci_controller *phb;
> -
> -	if (!of_node_to_eeh_dev(dn))
> -		return;
> -	phb = of_node_to_eeh_dev(dn)->phb;
> -
> -	/* USB Bus children of PCI devices will not have BUID's */
> -	if (NULL == phb || 0 == phb->buid)
> -		return;
> -
> -	/* FIXME: hotplug support on POWERNV */
> -	eeh_ops->of_probe(dn, NULL);
> -}
> -
> -/**
> - * 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)
> -{
> -	struct device_node *sib;
> -
> -	for_each_child_of_node(dn, sib)
> -		eeh_add_device_tree_early(sib);
> -	eeh_add_device_early(dn);
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
> -
> -/**
> - * eeh_add_device_late - Perform EEH initialization for the indicated pci device
> - * @dev: pci device for which to set up EEH
> - *
> - * This routine must be used to complete EEH initialization for PCI
> - * devices that were added after system boot (e.g. hotplug, dlpar).
> - */
> -static void eeh_add_device_late(struct pci_dev *dev)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *edev;
> -
> -	if (!dev || !eeh_subsystem_enabled)
> -		return;
> -
> -	pr_debug("EEH: Adding device %s\n", pci_name(dev));
> -
> -	dn = pci_device_to_OF_node(dev);
> -	edev = of_node_to_eeh_dev(dn);
> -	if (edev->pdev == dev) {
> -		pr_debug("EEH: Already referenced !\n");
> -		return;
> -	}
> -	WARN_ON(edev->pdev);
> -
> -	pci_dev_get(dev);
> -	edev->pdev = dev;
> -	dev->dev.archdata.edev = edev;
> -
> -	eeh_addr_cache_insert_dev(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)
> -{
> -	struct pci_dev *dev;
> -
> -	list_for_each_entry(dev, &bus->devices, bus_list) {
> - 		eeh_add_device_late(dev);
> - 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> - 			struct pci_bus *subbus = dev->subordinate;
> - 			if (subbus)
> - 				eeh_add_device_tree_late(subbus);
> - 		}
> -	}
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
> -
> -/**
> - * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
> - * @bus: PCI bus
> - *
> - * This routine must be used to add EEH sysfs files for PCI
> - * devices which are attached to the indicated PCI bus. The PCI bus
> - * is added after system boot through hotplug or dlpar.
> - */
> -void eeh_add_sysfs_files(struct pci_bus *bus)
> -{
> -	struct pci_dev *dev;
> -
> -	list_for_each_entry(dev, &bus->devices, bus_list) {
> -		eeh_sysfs_add_device(dev);
> -		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> -			struct pci_bus *subbus = dev->subordinate;
> -			if (subbus)
> -				eeh_add_sysfs_files(subbus);
> -		}
> -	}
> -}
> -EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
> -
> -/**
> - * eeh_remove_device - Undo EEH setup for the indicated pci device
> - * @dev: pci device to be removed
> - * @purge_pe: remove the PE or not
> - *
> - * This routine should be called when a device is removed from
> - * a running system (e.g. by hotplug or dlpar).  It unregisters
> - * the PCI device from the EEH subsystem.  I/O errors affecting
> - * this device will no longer be detected after this call; thus,
> - * i/o errors affecting this slot may leave this device unusable.
> - */
> -static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
> -{
> -	struct eeh_dev *edev;
> -
> -	if (!dev || !eeh_subsystem_enabled)
> -		return;
> -	edev = pci_dev_to_eeh_dev(dev);
> -
> -	/* Unregister the device with the EEH/PCI address search system */
> -	pr_debug("EEH: Removing device %s\n", pci_name(dev));
> -
> -	if (!edev || !edev->pdev) {
> -		pr_debug("EEH: Not referenced !\n");
> -		return;
> -	}
> -	edev->pdev = NULL;
> -	dev->dev.archdata.edev = NULL;
> -	pci_dev_put(dev);
> -
> -	eeh_rmv_from_parent_pe(edev, purge_pe);
> -	eeh_addr_cache_rmv_dev(dev);
> -	eeh_sysfs_remove_device(dev);
> -}
> -
> -/**
> - * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
> - * @dev: PCI device
> - * @purge_pe: remove the corresponding PE or not
> - *
> - * 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, int purge_pe)
> -{
> -	struct pci_bus *bus = dev->subordinate;
> -	struct pci_dev *child, *tmp;
> -
> -	eeh_remove_device(dev, purge_pe);
> -
> -	if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
> -		list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
> -			 eeh_remove_bus_device(child, purge_pe);
> -	}
> -}
> -EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
> -
> -static int proc_eeh_show(struct seq_file *m, void *v)
> -{
> -	if (0 == eeh_subsystem_enabled) {
> -		seq_printf(m, "EEH Subsystem is globally disabled\n");
> -		seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
> -	} else {
> -		seq_printf(m, "EEH Subsystem is enabled\n");
> -		seq_printf(m,
> -				"no device=%llu\n"
> -				"no device node=%llu\n"
> -				"no config address=%llu\n"
> -				"check not wanted=%llu\n"
> -				"eeh_total_mmio_ffs=%llu\n"
> -				"eeh_false_positives=%llu\n"
> -				"eeh_slot_resets=%llu\n",
> -				eeh_stats.no_device,
> -				eeh_stats.no_dn,
> -				eeh_stats.no_cfg_addr,
> -				eeh_stats.ignored_check,
> -				eeh_stats.total_mmio_ffs,
> -				eeh_stats.false_positives,
> -				eeh_stats.slot_resets);
> -	}
> -
> -	return 0;
> -}
> -
> -static int proc_eeh_open(struct inode *inode, struct file *file)
> -{
> -	return single_open(file, proc_eeh_show, NULL);
> -}
> -
> -static const struct file_operations proc_eeh_operations = {
> -	.open      = proc_eeh_open,
> -	.read      = seq_read,
> -	.llseek    = seq_lseek,
> -	.release   = single_release,
> -};
> -
> -static int __init eeh_init_proc(void)
> -{
> -	if (machine_is(pseries))
> -		proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
> -	return 0;
> -}
> -__initcall(eeh_init_proc);
> diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c
> deleted file mode 100644
> index 5a4c879..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_cache.c
> +++ /dev/null
> @@ -1,319 +0,0 @@
> -/*
> - * PCI address cache; allows the lookup of PCI devices based on I/O address
> - *
> - * Copyright IBM Corporation 2004
> - * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
> - *
> - * 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/list.h>
> -#include <linux/pci.h>
> -#include <linux/rbtree.h>
> -#include <linux/slab.h>
> -#include <linux/spinlock.h>
> -#include <linux/atomic.h>
> -#include <asm/pci-bridge.h>
> -#include <asm/ppc-pci.h>
> -
> -
> -/**
> - * The pci address cache subsystem.  This subsystem places
> - * PCI device address resources into a red-black tree, sorted
> - * according to the address range, so that given only an i/o
> - * address, the corresponding PCI device can be **quickly**
> - * found. It is safe to perform an address lookup in an interrupt
> - * context; this ability is an important feature.
> - *
> - * Currently, the only customer of this code is the EEH subsystem;
> - * thus, this code has been somewhat tailored to suit EEH better.
> - * In particular, the cache does *not* hold the addresses of devices
> - * for which EEH is not enabled.
> - *
> - * (Implementation Note: The RB tree seems to be better/faster
> - * than any hash algo I could think of for this problem, even
> - * with the penalty of slow pointer chases for d-cache misses).
> - */
> -struct pci_io_addr_range {
> -	struct rb_node rb_node;
> -	unsigned long addr_lo;
> -	unsigned long addr_hi;
> -	struct eeh_dev *edev;
> -	struct pci_dev *pcidev;
> -	unsigned int flags;
> -};
> -
> -static struct pci_io_addr_cache {
> -	struct rb_root rb_root;
> -	spinlock_t piar_lock;
> -} pci_io_addr_cache_root;
> -
> -static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
> -{
> -	struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
> -
> -	while (n) {
> -		struct pci_io_addr_range *piar;
> -		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -
> -		if (addr < piar->addr_lo) {
> -			n = n->rb_left;
> -		} else {
> -			if (addr > piar->addr_hi) {
> -				n = n->rb_right;
> -			} else {
> -				pci_dev_get(piar->pcidev);
> -				return piar->edev;
> -			}
> -		}
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_addr_cache_get_dev - Get device, given only address
> - * @addr: mmio (PIO) phys address or i/o port number
> - *
> - * Given an mmio phys address, or a port number, find a pci device
> - * that implements this address.  Be sure to pci_dev_put the device
> - * when finished.  I/O port numbers are assumed to be offset
> - * from zero (that is, they do *not* have pci_io_addr added in).
> - * It is safe to call this function within an interrupt.
> - */
> -struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
> -{
> -	struct eeh_dev *edev;
> -	unsigned long flags;
> -
> -	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> -	edev = __eeh_addr_cache_get_device(addr);
> -	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -	return edev;
> -}
> -
> -#ifdef DEBUG
> -/*
> - * Handy-dandy debug print routine, does nothing more
> - * than print out the contents of our addr cache.
> - */
> -static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
> -{
> -	struct rb_node *n;
> -	int cnt = 0;
> -
> -	n = rb_first(&cache->rb_root);
> -	while (n) {
> -		struct pci_io_addr_range *piar;
> -		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -		pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
> -		       (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
> -		       piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
> -		cnt++;
> -		n = rb_next(n);
> -	}
> -}
> -#endif
> -
> -/* Insert address range into the rb tree. */
> -static struct pci_io_addr_range *
> -eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
> -		      unsigned long ahi, unsigned int flags)
> -{
> -	struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
> -	struct rb_node *parent = NULL;
> -	struct pci_io_addr_range *piar;
> -
> -	/* Walk tree, find a place to insert into tree */
> -	while (*p) {
> -		parent = *p;
> -		piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
> -		if (ahi < piar->addr_lo) {
> -			p = &parent->rb_left;
> -		} else if (alo > piar->addr_hi) {
> -			p = &parent->rb_right;
> -		} else {
> -			if (dev != piar->pcidev ||
> -			    alo != piar->addr_lo || ahi != piar->addr_hi) {
> -				pr_warning("PIAR: overlapping address range\n");
> -			}
> -			return piar;
> -		}
> -	}
> -	piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
> -	if (!piar)
> -		return NULL;
> -
> -	pci_dev_get(dev);
> -	piar->addr_lo = alo;
> -	piar->addr_hi = ahi;
> -	piar->edev = pci_dev_to_eeh_dev(dev);
> -	piar->pcidev = dev;
> -	piar->flags = flags;
> -
> -#ifdef DEBUG
> -	pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
> -	                  alo, ahi, pci_name(dev));
> -#endif
> -
> -	rb_link_node(&piar->rb_node, parent, p);
> -	rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
> -
> -	return piar;
> -}
> -
> -static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *edev;
> -	int i;
> -
> -	dn = pci_device_to_OF_node(dev);
> -	if (!dn) {
> -		pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
> -		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. */
> -	if (!edev->pe) {
> -#ifdef DEBUG
> -		pr_info("PCI: skip building address cache for=%s - %s\n",
> -			pci_name(dev), dn->full_name);
> -#endif
> -		return;
> -	}
> -
> -	/* Walk resources on this device, poke them into the tree */
> -	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
> -		unsigned long start = pci_resource_start(dev,i);
> -		unsigned long end = pci_resource_end(dev,i);
> -		unsigned int flags = pci_resource_flags(dev,i);
> -
> -		/* We are interested only bus addresses, not dma or other stuff */
> -		if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
> -			continue;
> -		if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
> -			 continue;
> -		eeh_addr_cache_insert(dev, start, end, flags);
> -	}
> -}
> -
> -/**
> - * eeh_addr_cache_insert_dev - Add a device to the address cache
> - * @dev: PCI device whose I/O addresses we are interested in.
> - *
> - * In order to support the fast lookup of devices based on addresses,
> - * we maintain a cache of devices that can be quickly searched.
> - * This routine adds a device to that cache.
> - */
> -void eeh_addr_cache_insert_dev(struct pci_dev *dev)
> -{
> -	unsigned long flags;
> -
> -	/* Ignore PCI bridges */
> -	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
> -		return;
> -
> -	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> -	__eeh_addr_cache_insert_dev(dev);
> -	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -}
> -
> -static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> -{
> -	struct rb_node *n;
> -
> -restart:
> -	n = rb_first(&pci_io_addr_cache_root.rb_root);
> -	while (n) {
> -		struct pci_io_addr_range *piar;
> -		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
> -
> -		if (piar->pcidev == dev) {
> -			rb_erase(n, &pci_io_addr_cache_root.rb_root);
> -			pci_dev_put(piar->pcidev);
> -			kfree(piar);
> -			goto restart;
> -		}
> -		n = rb_next(n);
> -	}
> -}
> -
> -/**
> - * eeh_addr_cache_rmv_dev - remove pci device from addr cache
> - * @dev: device to remove
> - *
> - * Remove a device from the addr-cache tree.
> - * This is potentially expensive, since it will walk
> - * the tree multiple times (once per resource).
> - * But so what; device removal doesn't need to be that fast.
> - */
> -void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
> -{
> -	unsigned long flags;
> -
> -	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
> -	__eeh_addr_cache_rmv_dev(dev);
> -	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
> -}
> -
> -/**
> - * eeh_addr_cache_build - Build a cache of I/O addresses
> - *
> - * Build a cache of pci i/o addresses.  This cache will be used to
> - * find the pci device that corresponds to a given address.
> - * This routine scans all pci busses to build the cache.
> - * Must be run late in boot process, after the pci controllers
> - * have been scanned for devices (after all device resources are known).
> - */
> -void __init eeh_addr_cache_build(void)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *edev;
> -	struct pci_dev *dev = NULL;
> -
> -	spin_lock_init(&pci_io_addr_cache_root.piar_lock);
> -
> -	for_each_pci_dev(dev) {
> -		eeh_addr_cache_insert_dev(dev);
> -
> -		dn = pci_device_to_OF_node(dev);
> -		if (!dn)
> -			continue;
> -
> -		edev = of_node_to_eeh_dev(dn);
> -		if (!edev)
> -			continue;
> -
> -		pci_dev_get(dev);  /* matching put is in eeh_remove_device() */
> -		dev->dev.archdata.edev = edev;
> -		edev->pdev = dev;
> -
> -		eeh_sysfs_add_device(dev);
> -	}
> -
> -#ifdef DEBUG
> -	/* Verify tree built up above, echo back the list of addrs. */
> -	eeh_addr_cache_print(&pci_io_addr_cache_root);
> -#endif
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c
> deleted file mode 100644
> index 1efa28f..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_dev.c
> +++ /dev/null
> @@ -1,112 +0,0 @@
> -/*
> - * 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 *eeh_dev_init(struct device_node *dn, void *data)
> -{
> -	struct pci_controller *phb = data;
> -	struct eeh_dev *edev;
> -
> -	/* Allocate EEH device */
> -	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
> -	if (!edev) {
> -		pr_warning("%s: out of memory\n", __func__);
> -		return NULL;
> -	}
> -
> -	/* Associate EEH device with OF node */
> -	PCI_DN(dn)->edev = edev;
> -	edev->dn  = dn;
> -	edev->phb = phb;
> -	INIT_LIST_HEAD(&edev->list);
> -
> -	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 eeh_dev_phb_init_dynamic(struct pci_controller *phb)
> -{
> -	struct device_node *dn = phb->dn;
> -
> -	/* EEH PE for PHB */
> -	eeh_phb_pe_create(phb);
> -
> -	/* 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
> - */
> -static int __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);
> -
> -	pr_info("EEH: devices created\n");
> -
> -	return 0;
> -}
> -
> -core_initcall(eeh_dev_phb_init);
> diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
> deleted file mode 100644
> index a3fefb6..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_driver.c
> +++ /dev/null
> @@ -1,552 +0,0 @@
> -/*
> - * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
> - * Copyright IBM Corp. 2004 2005
> - * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
> - *
> - * All rights reserved.
> - *
> - * 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, GOOD TITLE or
> - * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> - *
> - * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -#include <linux/delay.h>
> -#include <linux/interrupt.h>
> -#include <linux/irq.h>
> -#include <linux/module.h>
> -#include <linux/pci.h>
> -#include <asm/eeh.h>
> -#include <asm/eeh_event.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/pci-bridge.h>
> -#include <asm/prom.h>
> -#include <asm/rtas.h>
> -
> -/**
> - * 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)
> -		return pdev->dev.driver->name;
> -	return "";
> -}
> -
> -/**
> - * eeh_pcid_get - Get the PCI device driver
> - * @pdev: PCI device
> - *
> - * The function is used to retrieve the PCI device driver for
> - * the indicated PCI device. Besides, we will increase the reference
> - * of the PCI device driver to prevent that being unloaded on
> - * the fly. Otherwise, kernel crash would be seen.
> - */
> -static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
> -{
> -	if (!pdev || !pdev->driver)
> -		return NULL;
> -
> -	if (!try_module_get(pdev->driver->driver.owner))
> -		return NULL;
> -
> -	return pdev->driver;
> -}
> -
> -/**
> - * eeh_pcid_put - Dereference on the PCI device driver
> - * @pdev: PCI device
> - *
> - * The function is called to do dereference on the PCI device
> - * driver of the indicated PCI device.
> - */
> -static inline void eeh_pcid_put(struct pci_dev *pdev)
> -{
> -	if (!pdev || !pdev->driver)
> -		return;
> -
> -	module_put(pdev->driver->driver.owner);
> -}
> -
> -#if 0
> -static void print_device_node_tree(struct pci_dn *pdn, int dent)
> -{
> -	int i;
> -	struct device_node *pc;
> -
> -	if (!pdn)
> -		return;
> -	for (i = 0; i < dent; i++)
> -		printk(" ");
> -	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
> -		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
> -		pdn->eeh_pe_config_addr, pdn->node->full_name);
> -	dent += 3;
> -	pc = pdn->node->child;
> -	while (pc) {
> -		print_device_node_tree(PCI_DN(pc), dent);
> -		pc = pc->sibling;
> -	}
> -}
> -#endif
> -
> -/**
> - * 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)
> -{
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -
> -	/* Don't disable MSI and MSI-X interrupts. They are
> -	 * effectively disabled by the DMA Stopped state
> -	 * when an EEH error occurs.
> -	 */
> -	if (dev->msi_enabled || dev->msix_enabled)
> -		return;
> -
> -	if (!irq_has_action(dev->irq))
> -		return;
> -
> -	edev->mode |= EEH_DEV_IRQ_DISABLED;
> -	disable_irq_nosync(dev->irq);
> -}
> -
> -/**
> - * 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)
> -{
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
> -
> -	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
> -		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
> -		enable_irq(dev->irq);
> -	}
> -}
> -
> -/**
> - * eeh_report_error - Report pci error to each device driver
> - * @data: eeh device
> - * @userdata: return value
> - * 
> - * Report an EEH error to each device driver, collect up and 
> - * merge the device driver responses. Cumulative response 
> - * passed back in "userdata".
> - */
> -static void *eeh_report_error(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	enum pci_ers_result rc, *res = userdata;
> -	struct pci_driver *driver;
> -
> -	/* We might not have the associated PCI device,
> -	 * then we should continue for next one.
> -	 */
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_frozen;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_disable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->error_detected) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
> -
> -	/* A driver that needs a reset trumps all others */
> -	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * Tells each device driver that IO ports, MMIO and config space I/O
> - * are now enabled. Collects up and merges the device driver responses.
> - * Cumulative response passed back in "userdata".
> - */
> -static void *eeh_report_mmio_enabled(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	enum pci_ers_result rc, *res = userdata;
> -	struct pci_driver *driver;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->mmio_enabled) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	rc = driver->err_handler->mmio_enabled(dev);
> -
> -	/* A driver that needs a reset trumps all others */
> -	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_reset - Tell device that slot has been reset
> - * @data: eeh 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 void *eeh_report_reset(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	enum pci_ers_result rc, *res = userdata;
> -	struct pci_driver *driver;
> -
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_normal;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_enable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->slot_reset) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	rc = driver->err_handler->slot_reset(dev);
> -	if ((*res == PCI_ERS_RESULT_NONE) ||
> -	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
> -	if (*res == PCI_ERS_RESULT_DISCONNECT &&
> -	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_resume - Tell device to resume normal operations
> - * @data: eeh 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 void *eeh_report_resume(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	struct pci_driver *driver;
> -
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_normal;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_enable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->resume) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	driver->err_handler->resume(dev);
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_report_failure - Tell device driver that device is dead.
> - * @data: eeh device
> - * @userdata: return value
> - *
> - * This informs the device driver that the device is permanently
> - * dead, and that no further recovery attempts will be made on it.
> - */
> -static void *eeh_report_failure(void *data, void *userdata)
> -{
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
> -	struct pci_driver *driver;
> -
> -	if (!dev) return NULL;
> -	dev->error_state = pci_channel_io_perm_failure;
> -
> -	driver = eeh_pcid_get(dev);
> -	if (!driver) return NULL;
> -
> -	eeh_disable_irq(dev);
> -
> -	if (!driver->err_handler ||
> -	    !driver->err_handler->error_detected) {
> -		eeh_pcid_put(dev);
> -		return NULL;
> -	}
> -
> -	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
> -
> -	eeh_pcid_put(dev);
> -	return NULL;
> -}
> -
> -/**
> - * eeh_reset_device - Perform actual reset of a pci slot
> - * @pe: EEH PE
> - * @bus: PCI bus corresponding to the isolcated slot
> - *
> - * This routine must be called to do reset on the indicated PE.
> - * During the reset, udev might be invoked because those affected
> - * PCI devices will be removed and then added.
> - */
> -static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
> -{
> -	int cnt, rc;
> -
> -	/* pcibios will clear the counter; save the value */
> -	cnt = pe->freeze_count;
> -
> -	/*
> -	 * We don't remove the corresponding PE instances because
> -	 * we need the information afterwords. The attached EEH
> -	 * devices are expected to be attached soon when calling
> -	 * into pcibios_add_pci_devices().
> -	 */
> -	if (bus)
> -		__pcibios_remove_pci_devices(bus, 0);
> -
> -	/* Reset the pci controller. (Asserts RST#; resets config space).
> -	 * Reconfigure bridges and devices. Don't try to bring the system
> -	 * up if the reset failed for some reason.
> -	 */
> -	rc = eeh_reset_pe(pe);
> -	if (rc)
> -		return rc;
> -
> -	/* Restore PE */
> -	eeh_ops->configure_bridge(pe);
> -	eeh_pe_restore_bars(pe);
> -
> -	/* Give the system 5 seconds to finish running the user-space
> -	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes, 
> -	 * this is a hack, but if we don't do this, and try to bring 
> -	 * the device up before the scripts have taken it down, 
> -	 * potentially weird things happen.
> -	 */
> -	if (bus) {
> -		ssleep(5);
> -		pcibios_add_pci_devices(bus);
> -	}
> -	pe->freeze_count = cnt;
> -
> -	return 0;
> -}
> -
> -/* The longest amount of time to wait for a pci device
> - * to come back on line, in seconds.
> - */
> -#define MAX_WAIT_FOR_RECOVERY 150
> -
> -/**
> - * eeh_handle_event - Reset a PCI device after hard lockup.
> - * @pe: EEH PE
> - *
> - * 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).
> - */
> -void eeh_handle_event(struct eeh_pe *pe)
> -{
> -	struct pci_bus *frozen_bus;
> -	int rc = 0;
> -	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
> -
> -	frozen_bus = eeh_pe_bus_get(pe);
> -	if (!frozen_bus) {
> -		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
> -			__func__, pe->phb->global_number, pe->addr);
> -		return;
> -	}
> -
> -	pe->freeze_count++;
> -	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
> -		goto excess_failures;
> -	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
> -		pe->freeze_count);
> -
> -	/* Walk the various device drivers attached to this slot through
> -	 * a reset sequence, giving each an opportunity to do what it needs
> -	 * to accomplish the reset.  Each child gets a report of the
> -	 * status ... if any child can't handle the reset, then the entire
> -	 * slot is dlpar removed and added.
> -	 */
> -	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
> -
> -	/* Get the current PCI slot state. This can take a long time,
> -	 * sometimes over 3 seconds for certain systems.
> -	 */
> -	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
> -	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
> -		printk(KERN_WARNING "EEH: Permanent failure\n");
> -		goto hard_fail;
> -	}
> -
> -	/* Since rtas may enable MMIO when posting the error log,
> -	 * don't post the error log until after all dev drivers
> -	 * have been informed.
> -	 */
> -	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
> -
> -	/* If all device drivers were EEH-unaware, then shut
> -	 * down all of the device drivers, and hope they
> -	 * go down willingly, without panicing the system.
> -	 */
> -	if (result == PCI_ERS_RESULT_NONE) {
> -		rc = eeh_reset_device(pe, frozen_bus);
> -		if (rc) {
> -			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
> -			goto hard_fail;
> -		}
> -	}
> -
> -	/* If all devices reported they can proceed, then re-enable MMIO */
> -	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> -		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
> -
> -		if (rc < 0)
> -			goto hard_fail;
> -		if (rc) {
> -			result = PCI_ERS_RESULT_NEED_RESET;
> -		} else {
> -			result = PCI_ERS_RESULT_NONE;
> -			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
> -		}
> -	}
> -
> -	/* If all devices reported they can proceed, then re-enable DMA */
> -	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
> -		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
> -
> -		if (rc < 0)
> -			goto hard_fail;
> -		if (rc)
> -			result = PCI_ERS_RESULT_NEED_RESET;
> -		else
> -			result = PCI_ERS_RESULT_RECOVERED;
> -	}
> -
> -	/* If any device has a hard failure, then shut off everything. */
> -	if (result == PCI_ERS_RESULT_DISCONNECT) {
> -		printk(KERN_WARNING "EEH: Device driver gave up\n");
> -		goto hard_fail;
> -	}
> -
> -	/* If any device called out for a reset, then reset the slot */
> -	if (result == PCI_ERS_RESULT_NEED_RESET) {
> -		rc = eeh_reset_device(pe, NULL);
> -		if (rc) {
> -			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
> -			goto hard_fail;
> -		}
> -		result = PCI_ERS_RESULT_NONE;
> -		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
> -	}
> -
> -	/* All devices should claim they have recovered by now. */
> -	if ((result != PCI_ERS_RESULT_RECOVERED) &&
> -	    (result != PCI_ERS_RESULT_NONE)) {
> -		printk(KERN_WARNING "EEH: Not recovered\n");
> -		goto hard_fail;
> -	}
> -
> -	/* Tell all device drivers that they can resume operations */
> -	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
> -
> -	return;
> -	
> -excess_failures:
> -	/*
> -	 * About 90% of all real-life EEH failures in the field
> -	 * are due to poorly seated PCI cards. Only 10% or so are
> -	 * due to actual, failed cards.
> -	 */
> -	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
> -	       "last hour and has been permanently disabled.\n"
> -	       "Please try reseating or replacing it.\n",
> -		pe->phb->global_number, pe->addr,
> -		pe->freeze_count);
> -	goto perm_error;
> -
> -hard_fail:
> -	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
> -	       "Please try reseating or replacing it\n",
> -		pe->phb->global_number, pe->addr);
> -
> -perm_error:
> -	eeh_slot_error_detail(pe, EEH_LOG_PERM);
> -
> -	/* Notify all devices that they're about to go down. */
> -	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
> -
> -	/* Shut down the device drivers for good. */
> -	if (frozen_bus)
> -		pcibios_remove_pci_devices(frozen_bus);
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c
> deleted file mode 100644
> index 185bedd..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_event.c
> +++ /dev/null
> @@ -1,142 +0,0 @@
> -/*
> - * 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
> - *
> - * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
> - */
> -
> -#include <linux/delay.h>
> -#include <linux/list.h>
> -#include <linux/mutex.h>
> -#include <linux/sched.h>
> -#include <linux/pci.h>
> -#include <linux/slab.h>
> -#include <linux/workqueue.h>
> -#include <linux/kthread.h>
> -#include <asm/eeh_event.h>
> -#include <asm/ppc-pci.h>
> -
> -/** Overview:
> - *  EEH error states may be detected within exception handlers;
> - *  however, the recovery processing needs to occur asynchronously
> - *  in a normal kernel context and not an interrupt context.
> - *  This pair of routines creates an event and queues it onto a
> - *  work-queue, where a worker thread can drive recovery.
> - */
> -
> -/* EEH event workqueue setup. */
> -static DEFINE_SPINLOCK(eeh_eventlist_lock);
> -LIST_HEAD(eeh_eventlist);
> -static void eeh_thread_launcher(struct work_struct *);
> -DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
> -
> -/* Serialize reset sequences for a given pci device */
> -DEFINE_MUTEX(eeh_event_mutex);
> -
> -/**
> - * eeh_event_handler - Dispatch EEH events.
> - * @dummy - unused
> - *
> - * The detection of a frozen slot can occur inside an interrupt,
> - * where it can be hard to do anything about it.  The goal of this
> - * routine is to pull these detection events out of the context
> - * of the interrupt handler, and re-dispatch them for processing
> - * at a later time in a normal context.
> - */
> -static int eeh_event_handler(void * dummy)
> -{
> -	unsigned long flags;
> -	struct eeh_event *event;
> -	struct eeh_pe *pe;
> -
> -	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> -	event = NULL;
> -
> -	/* Unqueue the event, get ready to process. */
> -	if (!list_empty(&eeh_eventlist)) {
> -		event = list_entry(eeh_eventlist.next, struct eeh_event, list);
> -		list_del(&event->list);
> -	}
> -	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> -
> -	if (event == NULL)
> -		return 0;
> -
> -	/* Serialize processing of EEH events */
> -	mutex_lock(&eeh_event_mutex);
> -	pe = event->pe;
> -	eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
> -	pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
> -		pe->phb->global_number, pe->addr);
> -
> -	set_current_state(TASK_INTERRUPTIBLE);	/* Don't add to load average */
> -	eeh_handle_event(pe);
> -	eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> -
> -	kfree(event);
> -	mutex_unlock(&eeh_event_mutex);
> -
> -	/* If there are no new errors after an hour, clear the counter. */
> -	if (pe && pe->freeze_count > 0) {
> -		msleep_interruptible(3600*1000);
> -		if (pe->freeze_count > 0)
> -			pe->freeze_count--;
> -
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_thread_launcher - Start kernel thread to handle EEH events
> - * @dummy - unused
> - *
> - * This routine is called to start the kernel thread for processing
> - * EEH event.
> - */
> -static void eeh_thread_launcher(struct work_struct *dummy)
> -{
> -	if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
> -		printk(KERN_ERR "Failed to start EEH daemon\n");
> -}
> -
> -/**
> - * eeh_send_failure_event - Generate a PCI error event
> - * @pe: EEH PE
> - *
> - * 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 eeh_pe *pe)
> -{
> -	unsigned long flags;
> -	struct eeh_event *event;
> -
> -	event = kzalloc(sizeof(*event), GFP_ATOMIC);
> -	if (!event) {
> -		pr_err("EEH: out of memory, event not handled\n");
> -		return -ENOMEM;
> -	}
> -	event->pe = pe;
> -
> -	/* We may or may not be called in an interrupt context */
> -	spin_lock_irqsave(&eeh_eventlist_lock, flags);
> -	list_add(&event->list, &eeh_eventlist);
> -	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
> -
> -	schedule_work(&eeh_event_wq);
> -
> -	return 0;
> -}
> diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
> deleted file mode 100644
> index 9d4a9e8..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_pe.c
> +++ /dev/null
> @@ -1,653 +0,0 @@
> -/*
> - * The file intends to implement PE based on the information from
> - * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
> - * All the PEs should be organized as hierarchy tree. The first level
> - * of the tree will be associated to existing PHBs since the particular
> - * PE is only meaningful in one PHB domain.
> - *
> - * 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>
> -
> -static LIST_HEAD(eeh_phb_pe);
> -
> -/**
> - * eeh_pe_alloc - Allocate PE
> - * @phb: PCI controller
> - * @type: PE type
> - *
> - * Allocate PE instance dynamically.
> - */
> -static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
> -{
> -	struct eeh_pe *pe;
> -
> -	/* Allocate PHB PE */
> -	pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
> -	if (!pe) return NULL;
> -
> -	/* Initialize PHB PE */
> -	pe->type = type;
> -	pe->phb = phb;
> -	INIT_LIST_HEAD(&pe->child_list);
> -	INIT_LIST_HEAD(&pe->child);
> -	INIT_LIST_HEAD(&pe->edevs);
> -
> -	return pe;
> -}
> -
> -/**
> - * eeh_phb_pe_create - Create PHB PE
> - * @phb: PCI controller
> - *
> - * The function should be called while the PHB is detected during
> - * system boot or PCI hotplug in order to create PHB PE.
> - */
> -int eeh_phb_pe_create(struct pci_controller *phb)
> -{
> -	struct eeh_pe *pe;
> -
> -	/* Allocate PHB PE */
> -	pe = eeh_pe_alloc(phb, EEH_PE_PHB);
> -	if (!pe) {
> -		pr_err("%s: out of memory!\n", __func__);
> -		return -ENOMEM;
> -	}
> -
> -	/* Put it into the list */
> -	eeh_lock();
> -	list_add_tail(&pe->child, &eeh_phb_pe);
> -	eeh_unlock();
> -
> -	pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
> - * @phb: PCI controller
> - *
> - * The overall PEs form hierarchy tree. The first layer of the
> - * hierarchy tree is composed of PHB PEs. The function is used
> - * to retrieve the corresponding PHB PE according to the given PHB.
> - */
> -static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
> -{
> -	struct eeh_pe *pe;
> -
> -	list_for_each_entry(pe, &eeh_phb_pe, child) {
> -		/*
> -		 * Actually, we needn't check the type since
> -		 * the PE for PHB has been determined when that
> -		 * was created.
> -		 */
> -		if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
> -			return pe;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_next - Retrieve the next PE in the tree
> - * @pe: current PE
> - * @root: root PE
> - *
> - * The function is used to retrieve the next PE in the
> - * hierarchy PE tree.
> - */
> -static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
> -				  struct eeh_pe *root)
> -{
> -	struct list_head *next = pe->child_list.next;
> -
> -	if (next == &pe->child_list) {
> -		while (1) {
> -			if (pe == root)
> -				return NULL;
> -			next = pe->child.next;
> -			if (next != &pe->parent->child_list)
> -				break;
> -			pe = pe->parent;
> -		}
> -	}
> -
> -	return list_entry(next, struct eeh_pe, child);
> -}
> -
> -/**
> - * eeh_pe_traverse - Traverse PEs in the specified PHB
> - * @root: root PE
> - * @fn: callback
> - * @flag: extra parameter to callback
> - *
> - * The function is used to traverse the specified PE and its
> - * child PEs. The traversing is to be terminated once the
> - * callback returns something other than NULL, or no more PEs
> - * to be traversed.
> - */
> -static void *eeh_pe_traverse(struct eeh_pe *root,
> -			eeh_traverse_func fn, void *flag)
> -{
> -	struct eeh_pe *pe;
> -	void *ret;
> -
> -	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> -		ret = fn(pe, flag);
> -		if (ret) return ret;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_dev_traverse - Traverse the devices from the PE
> - * @root: EEH PE
> - * @fn: function callback
> - * @flag: extra parameter to callback
> - *
> - * The function is used to traverse the devices of the specified
> - * PE and its child PEs.
> - */
> -void *eeh_pe_dev_traverse(struct eeh_pe *root,
> -		eeh_traverse_func fn, void *flag)
> -{
> -	struct eeh_pe *pe;
> -	struct eeh_dev *edev;
> -	void *ret;
> -
> -	if (!root) {
> -		pr_warning("%s: Invalid PE %p\n", __func__, root);
> -		return NULL;
> -	}
> -
> -	eeh_lock();
> -
> -	/* Traverse root PE */
> -	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
> -		eeh_pe_for_each_dev(pe, edev) {
> -			ret = fn(edev, flag);
> -			if (ret) {
> -				eeh_unlock();
> -				return ret;
> -			}
> -		}
> -	}
> -
> -	eeh_unlock();
> -
> -	return NULL;
> -}
> -
> -/**
> - * __eeh_pe_get - Check the PE address
> - * @data: EEH PE
> - * @flag: EEH device
> - *
> - * For one particular PE, it can be identified by PE address
> - * or tranditional BDF address. BDF address is composed of
> - * Bus/Device/Function number. The extra data referred by flag
> - * indicates which type of address should be used.
> - */
> -static void *__eeh_pe_get(void *data, void *flag)
> -{
> -	struct eeh_pe *pe = (struct eeh_pe *)data;
> -	struct eeh_dev *edev = (struct eeh_dev *)flag;
> -
> -	/* Unexpected PHB PE */
> -	if (pe->type & EEH_PE_PHB)
> -		return NULL;
> -
> -	/* We prefer PE address */
> -	if (edev->pe_config_addr &&
> -	   (edev->pe_config_addr == pe->addr))
> -		return pe;
> -
> -	/* Try BDF address */
> -	if (edev->pe_config_addr &&
> -	   (edev->config_addr == pe->config_addr))
> -		return pe;
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_get - Search PE based on the given address
> - * @edev: EEH device
> - *
> - * Search the corresponding PE based on the specified address which
> - * is included in the eeh device. The function is used to check if
> - * the associated PE has been created against the PE address. It's
> - * notable that the PE address has 2 format: traditional PE address
> - * which is composed of PCI bus/device/function number, or unified
> - * PE address.
> - */
> -static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
> -{
> -	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
> -	struct eeh_pe *pe;
> -
> -	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
> -
> -	return pe;
> -}
> -
> -/**
> - * eeh_pe_get_parent - Retrieve the parent PE
> - * @edev: EEH device
> - *
> - * The whole PEs existing in the system are organized as hierarchy
> - * tree. The function is used to retrieve the parent PE according
> - * to the parent EEH device.
> - */
> -static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
> -{
> -	struct device_node *dn;
> -	struct eeh_dev *parent;
> -
> -	/*
> -	 * It might have the case for the indirect parent
> -	 * EEH device already having associated PE, but
> -	 * the direct parent EEH device doesn't have yet.
> -	 */
> -	dn = edev->dn->parent;
> -	while (dn) {
> -		/* We're poking out of PCI territory */
> -		if (!PCI_DN(dn)) return NULL;
> -
> -		parent = of_node_to_eeh_dev(dn);
> -		/* We're poking out of PCI territory */
> -		if (!parent) return NULL;
> -
> -		if (parent->pe)
> -			return parent->pe;
> -
> -		dn = dn->parent;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_add_to_parent_pe - Add EEH device to parent PE
> - * @edev: EEH device
> - *
> - * Add EEH device to the parent PE. If the parent PE already
> - * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
> - * we have to create new PE to hold the EEH device and the new
> - * PE will be linked to its parent PE as well.
> - */
> -int eeh_add_to_parent_pe(struct eeh_dev *edev)
> -{
> -	struct eeh_pe *pe, *parent;
> -
> -	eeh_lock();
> -
> -	/*
> -	 * Search the PE has been existing or not according
> -	 * to the PE address. If that has been existing, the
> -	 * PE should be composed of PCI bus and its subordinate
> -	 * components.
> -	 */
> -	pe = eeh_pe_get(edev);
> -	if (pe && !(pe->type & EEH_PE_INVALID)) {
> -		if (!edev->pe_config_addr) {
> -			eeh_unlock();
> -			pr_err("%s: PE with addr 0x%x already exists\n",
> -				__func__, edev->config_addr);
> -			return -EEXIST;
> -		}
> -
> -		/* Mark the PE as type of PCI bus */
> -		pe->type = EEH_PE_BUS;
> -		edev->pe = pe;
> -
> -		/* Put the edev to PE */
> -		list_add_tail(&edev->list, &pe->edevs);
> -		eeh_unlock();
> -		pr_debug("EEH: Add %s to Bus PE#%x\n",
> -			edev->dn->full_name, pe->addr);
> -
> -		return 0;
> -	} else if (pe && (pe->type & EEH_PE_INVALID)) {
> -		list_add_tail(&edev->list, &pe->edevs);
> -		edev->pe = pe;
> -		/*
> -		 * We're running to here because of PCI hotplug caused by
> -		 * EEH recovery. We need clear EEH_PE_INVALID until the top.
> -		 */
> -		parent = pe;
> -		while (parent) {
> -			if (!(parent->type & EEH_PE_INVALID))
> -				break;
> -			parent->type &= ~EEH_PE_INVALID;
> -			parent = parent->parent;
> -		}
> -		eeh_unlock();
> -		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> -			edev->dn->full_name, pe->addr, pe->parent->addr);
> -
> -		return 0;
> -	}
> -
> -	/* Create a new EEH PE */
> -	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
> -	if (!pe) {
> -		eeh_unlock();
> -		pr_err("%s: out of memory!\n", __func__);
> -		return -ENOMEM;
> -	}
> -	pe->addr	= edev->pe_config_addr;
> -	pe->config_addr	= edev->config_addr;
> -
> -	/*
> -	 * Put the new EEH PE into hierarchy tree. If the parent
> -	 * can't be found, the newly created PE will be attached
> -	 * to PHB directly. Otherwise, we have to associate the
> -	 * PE with its parent.
> -	 */
> -	parent = eeh_pe_get_parent(edev);
> -	if (!parent) {
> -		parent = eeh_phb_pe_get(edev->phb);
> -		if (!parent) {
> -			eeh_unlock();
> -			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
> -				__func__, edev->phb->global_number);
> -			edev->pe = NULL;
> -			kfree(pe);
> -			return -EEXIST;
> -		}
> -	}
> -	pe->parent = parent;
> -
> -	/*
> -	 * Put the newly created PE into the child list and
> -	 * link the EEH device accordingly.
> -	 */
> -	list_add_tail(&pe->child, &parent->child_list);
> -	list_add_tail(&edev->list, &pe->edevs);
> -	edev->pe = pe;
> -	eeh_unlock();
> -	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
> -		edev->dn->full_name, pe->addr, pe->parent->addr);
> -
> -	return 0;
> -}
> -
> -/**
> - * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
> - * @edev: EEH device
> - * @purge_pe: remove PE or not
> - *
> - * The PE hierarchy tree might be changed when doing PCI hotplug.
> - * Also, the PCI devices or buses could be removed from the system
> - * during EEH recovery. So we have to call the function remove the
> - * corresponding PE accordingly if necessary.
> - */
> -int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
> -{
> -	struct eeh_pe *pe, *parent, *child;
> -	int cnt;
> -
> -	if (!edev->pe) {
> -		pr_warning("%s: No PE found for EEH device %s\n",
> -			__func__, edev->dn->full_name);
> -		return -EEXIST;
> -	}
> -
> -	eeh_lock();
> -
> -	/* Remove the EEH device */
> -	pe = edev->pe;
> -	edev->pe = NULL;
> -	list_del(&edev->list);
> -
> -	/*
> -	 * Check if the parent PE includes any EEH devices.
> -	 * If not, we should delete that. Also, we should
> -	 * delete the parent PE if it doesn't have associated
> -	 * child PEs and EEH devices.
> -	 */
> -	while (1) {
> -		parent = pe->parent;
> -		if (pe->type & EEH_PE_PHB)
> -			break;
> -
> -		if (purge_pe) {
> -			if (list_empty(&pe->edevs) &&
> -			    list_empty(&pe->child_list)) {
> -				list_del(&pe->child);
> -				kfree(pe);
> -			} else {
> -				break;
> -			}
> -		} else {
> -			if (list_empty(&pe->edevs)) {
> -				cnt = 0;
> -				list_for_each_entry(child, &pe->child_list, child) {
> -					if (!(child->type & EEH_PE_INVALID)) {
> -						cnt++;
> -						break;
> -					}
> -				}
> -
> -				if (!cnt)
> -					pe->type |= EEH_PE_INVALID;
> -				else
> -					break;
> -			}
> -		}
> -
> -		pe = parent;
> -	}
> -
> -	eeh_unlock();
> -
> -	return 0;
> -}
> -
> -/**
> - * __eeh_pe_state_mark - Mark the state for the PE
> - * @data: EEH PE
> - * @flag: state
> - *
> - * The function is used to mark the indicated state for the given
> - * PE. Also, the associated PCI devices will be put into IO frozen
> - * state as well.
> - */
> -static void *__eeh_pe_state_mark(void *data, void *flag)
> -{
> -	struct eeh_pe *pe = (struct eeh_pe *)data;
> -	int state = *((int *)flag);
> -	struct eeh_dev *tmp;
> -	struct pci_dev *pdev;
> -
> -	/*
> -	 * Mark the PE with the indicated state. Also,
> -	 * the associated PCI device will be put into
> -	 * I/O frozen state to avoid I/O accesses from
> -	 * the PCI device driver.
> -	 */
> -	pe->state |= state;
> -	eeh_pe_for_each_dev(pe, tmp) {
> -		pdev = eeh_dev_to_pci_dev(tmp);
> -		if (pdev)
> -			pdev->error_state = pci_channel_io_frozen;
> -	}
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_state_mark - Mark specified state for PE and its associated device
> - * @pe: EEH PE
> - *
> - * EEH error affects the current PE and its child PEs. The function
> - * is used to mark appropriate state for the affected PEs and the
> - * associated devices.
> - */
> -void eeh_pe_state_mark(struct eeh_pe *pe, int state)
> -{
> -	eeh_lock();
> -	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
> -	eeh_unlock();
> -}
> -
> -/**
> - * __eeh_pe_state_clear - Clear state for the PE
> - * @data: EEH PE
> - * @flag: state
> - *
> - * The function is used to clear the indicated state from the
> - * given PE. Besides, we also clear the check count of the PE
> - * as well.
> - */
> -static void *__eeh_pe_state_clear(void *data, void *flag)
> -{
> -	struct eeh_pe *pe = (struct eeh_pe *)data;
> -	int state = *((int *)flag);
> -
> -	pe->state &= ~state;
> -	pe->check_count = 0;
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_state_clear - Clear state for the PE and its children
> - * @pe: PE
> - * @state: state to be cleared
> - *
> - * When the PE and its children has been recovered from error,
> - * we need clear the error state for that. The function is used
> - * for the purpose.
> - */
> -void eeh_pe_state_clear(struct eeh_pe *pe, int state)
> -{
> -	eeh_lock();
> -	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
> -	eeh_unlock();
> -}
> -
> -/**
> - * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
> - * @data: EEH device
> - * @flag: Unused
> - *
> - * Loads the PCI configuration space base address registers,
> - * the expansion ROM base address, the latency timer, and etc.
> - * from the saved values in the device node.
> - */
> -static void *eeh_restore_one_device_bars(void *data, void *flag)
> -{
> -	int i;
> -	u32 cmd;
> -	struct eeh_dev *edev = (struct eeh_dev *)data;
> -	struct device_node *dn = eeh_dev_to_of_node(edev);
> -
> -	for (i = 4; i < 10; i++)
> -		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
> -	/* 12 == Expansion ROM Address */
> -	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
> -
> -#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
> -#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
> -
> -	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
> -		SAVED_BYTE(PCI_CACHE_LINE_SIZE));
> -	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
> -		SAVED_BYTE(PCI_LATENCY_TIMER));
> -
> -	/* max latency, min grant, interrupt pin and line */
> -	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
> -
> -	/*
> -	 * Restore PERR & SERR bits, some devices require it,
> -	 * don't touch the other command bits
> -	 */
> -	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
> -	if (edev->config_space[1] & PCI_COMMAND_PARITY)
> -		cmd |= PCI_COMMAND_PARITY;
> -	else
> -		cmd &= ~PCI_COMMAND_PARITY;
> -	if (edev->config_space[1] & PCI_COMMAND_SERR)
> -		cmd |= PCI_COMMAND_SERR;
> -	else
> -		cmd &= ~PCI_COMMAND_SERR;
> -	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
> -
> -	return NULL;
> -}
> -
> -/**
> - * eeh_pe_restore_bars - Restore the PCI config space info
> - * @pe: EEH PE
> - *
> - * This routine performs a recursive walk to the children
> - * of this device as well.
> - */
> -void eeh_pe_restore_bars(struct eeh_pe *pe)
> -{
> -	/*
> -	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
> -	 * will take that.
> -	 */
> -	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
> -}
> -
> -/**
> - * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
> - * @pe: EEH PE
> - *
> - * Retrieve the PCI bus according to the given PE. Basically,
> - * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
> - * primary PCI bus will be retrieved. The parent bus will be
> - * returned for BUS PE. However, we don't have associated PCI
> - * bus for DEVICE PE.
> - */
> -struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
> -{
> -	struct pci_bus *bus = NULL;
> -	struct eeh_dev *edev;
> -	struct pci_dev *pdev;
> -
> -	eeh_lock();
> -
> -	if (pe->type & EEH_PE_PHB) {
> -		bus = pe->phb->bus;
> -	} else if (pe->type & EEH_PE_BUS ||
> -		   pe->type & EEH_PE_DEVICE) {
> -		edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
> -		pdev = eeh_dev_to_pci_dev(edev);
> -		if (pdev)
> -			bus = pdev->bus;
> -	}
> -
> -	eeh_unlock();
> -
> -	return bus;
> -}
> diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c
> deleted file mode 100644
> index d377083..0000000
> --- a/arch/powerpc/platforms/pseries/eeh_sysfs.c
> +++ /dev/null
> @@ -1,75 +0,0 @@
> -/*
> - * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
> - * Copyright IBM Corporation 2007
> - * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
> - *
> - * All rights reserved.
> - *
> - * 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, GOOD TITLE or
> - * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
> - *
> - * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
> - */
> -#include <linux/pci.h>
> -#include <linux/stat.h>
> -#include <asm/ppc-pci.h>
> -#include <asm/pci-bridge.h>
> -
> -/**
> - * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
> - * @_name: name of file in sysfs directory
> - * @_memb: name of member in struct pci_dn to access
> - * @_format: printf format for display
> - *
> - * All of the attributes look very similar, so just
> - * auto-gen a cut-n-paste routine to display them.
> - */
> -#define EEH_SHOW_ATTR(_name,_memb,_format)               \
> -static ssize_t eeh_show_##_name(struct device *dev,      \
> -		struct device_attribute *attr, char *buf)          \
> -{                                                        \
> -	struct pci_dev *pdev = to_pci_dev(dev);               \
> -	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
> -	                                                      \
> -	if (!edev)                                            \
> -		return 0;                                     \
> -	                                                      \
> -	return sprintf(buf, _format "\n", edev->_memb);       \
> -}                                                        \
> -static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
> -
> -EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
> -EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
> -EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
> -
> -void eeh_sysfs_add_device(struct pci_dev *pdev)
> -{
> -	int rc=0;
> -
> -	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
> -	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
> -	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> -
> -	if (rc)
> -		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
> -}
> -
> -void eeh_sysfs_remove_device(struct pci_dev *pdev)
> -{
> -	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
> -	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
> -	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
> -}
> -
> diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
> index c91b22b..efe6137 100644
> --- a/arch/powerpc/platforms/pseries/pci_dlpar.c
> +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
> @@ -64,91 +64,6 @@ pcibios_find_pci_bus(struct device_node *dn)
>  }
>  EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
>  
> -/**
> - * __pcibios_remove_pci_devices - remove all devices under this bus
> - * @bus: the indicated PCI bus
> - * @purge_pe: destroy the PE on removal of PCI devices
> - *
> - * Remove all of the PCI devices under this bus both from the
> - * linux pci device tree, and from the powerpc EEH address cache.
> - * By default, the corresponding PE will be destroied during the
> - * normal PCI hotplug path. For PCI hotplug during EEH recovery,
> - * the corresponding PE won't be destroied and deallocated.
> - */
> -void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
> -{
> -	struct pci_dev *dev, *tmp;
> -	struct pci_bus *child_bus;
> -
> -	/* First go down child busses */
> -	list_for_each_entry(child_bus, &bus->children, node)
> -		__pcibios_remove_pci_devices(child_bus, purge_pe);
> -
> -	pr_debug("PCI: Removing devices on bus %04x:%02x\n",
> -		pci_domain_nr(bus),  bus->number);
> -	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
> -		pr_debug("     * Removing %s...\n", pci_name(dev));
> -		eeh_remove_bus_device(dev, purge_pe);
> -		pci_stop_and_remove_bus_device(dev);
> -	}
> -}
> -
> -/**
> - * pcibios_remove_pci_devices - remove all devices under this bus
> - *
> - * Remove all of the PCI devices under this bus both from the
> - * linux pci device tree, and from the powerpc EEH address cache.
> - */
> -void pcibios_remove_pci_devices(struct pci_bus *bus)
> -{
> -	__pcibios_remove_pci_devices(bus, 1);
> -}
> -EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
> -
> -/**
> - * pcibios_add_pci_devices - adds new pci devices to bus
> - *
> - * This routine will find and fixup new pci devices under
> - * the indicated bus. This routine presumes that there
> - * might already be some devices under this bridge, so
> - * it carefully tries to add only new devices.  (And that
> - * is how this routine differs from other, similar pcibios
> - * routines.)
> - */
> -void pcibios_add_pci_devices(struct pci_bus * bus)
> -{
> -	int slotno, num, mode, pass, max;
> -	struct pci_dev *dev;
> -	struct device_node *dn = pci_bus_to_OF_node(bus);
> -
> -	eeh_add_device_tree_early(dn);
> -
> -	mode = PCI_PROBE_NORMAL;
> -	if (ppc_md.pci_probe_mode)
> -		mode = ppc_md.pci_probe_mode(bus);
> -
> -	if (mode == PCI_PROBE_DEVTREE) {
> -		/* use ofdt-based probe */
> -		of_rescan_bus(dn, bus);
> -	} else if (mode == PCI_PROBE_NORMAL) {
> -		/* use legacy probe */
> -		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
> -		num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
> -		if (!num)
> -			return;
> -		pcibios_setup_bus_devices(bus);
> -		max = bus->busn_res.start;
> -		for (pass=0; pass < 2; pass++)
> -			list_for_each_entry(dev, &bus->devices, bus_list) {
> -			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
> -			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
> -				max = pci_scan_bridge(bus, dev, max, pass);
> -		}
> -	}
> -	pcibios_finish_adding_to_bus(bus);
> -}
> -EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
> -
>  struct pci_controller *init_phb_dynamic(struct device_node *dn)
>  {
>  	struct pci_controller *phb;
> -- 
> 1.7.5.4
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
diff mbox

Patch

diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index f960a79..5826906 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -58,6 +58,8 @@  obj-$(CONFIG_RTAS_PROC)		+= rtas-proc.o
 obj-$(CONFIG_LPARCFG)		+= lparcfg.o
 obj-$(CONFIG_IBMVIO)		+= vio.o
 obj-$(CONFIG_IBMEBUS)           += ibmebus.o
+obj-$(CONFIG_EEH)		+= eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
+				   eeh_driver.o eeh_event.o eeh_sysfs.o
 obj-$(CONFIG_GENERIC_TBSYNC)	+= smp-tbsync.o
 obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 obj-$(CONFIG_FA_DUMP)		+= fadump.o
@@ -100,7 +102,7 @@  obj-$(CONFIG_PPC_UDBG_16550)	+= legacy_serial.o udbg_16550.o
 obj-$(CONFIG_STACKTRACE)	+= stacktrace.o
 obj-$(CONFIG_SWIOTLB)		+= dma-swiotlb.o
 
-pci64-$(CONFIG_PPC64)		+= pci_dn.o isa-bridge.o
+pci64-$(CONFIG_PPC64)		+= pci_hotplug.o pci_dn.o isa-bridge.o
 obj-$(CONFIG_PCI)		+= pci_$(CONFIG_WORD_SIZE).o $(pci64-y) \
 				   pci-common.o pci_of_scan.o
 obj-$(CONFIG_PCI_MSI)		+= msi.o
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
new file mode 100644
index 0000000..6b73d6c
--- /dev/null
+++ b/arch/powerpc/kernel/eeh.c
@@ -0,0 +1,942 @@ 
+/*
+ * Copyright IBM Corporation 2001, 2005, 2006
+ * Copyright Dave Engebretsen & Todd Inglett 2001
+ * Copyright Linas Vepstas 2005, 2006
+ * Copyright 2001-2012 IBM Corporation.
+ *
+ * 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
+ *
+ * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/rbtree.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/export.h>
+#include <linux/of.h>
+
+#include <linux/atomic.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>
+
+
+/** Overview:
+ *  EEH, or "Extended Error Handling" is a PCI bridge technology for
+ *  dealing with PCI bus errors that can't be dealt with within the
+ *  usual PCI framework, except by check-stopping the CPU.  Systems
+ *  that are designed for high-availability/reliability cannot afford
+ *  to crash due to a "mere" PCI error, thus the need for EEH.
+ *  An EEH-capable bridge operates by converting a detected error
+ *  into a "slot freeze", taking the PCI adapter off-line, making
+ *  the slot behave, from the OS'es point of view, as if the slot
+ *  were "empty": all reads return 0xff's and all writes are silently
+ *  ignored.  EEH slot isolation events can be triggered by parity
+ *  errors on the address or data busses (e.g. during posted writes),
+ *  which in turn might be caused by low voltage on the bus, dust,
+ *  vibration, humidity, radioactivity or plain-old failed hardware.
+ *
+ *  Note, however, that one of the leading causes of EEH slot
+ *  freeze events are buggy device drivers, buggy device microcode,
+ *  or buggy device hardware.  This is because any attempt by the
+ *  device to bus-master data to a memory address that is not
+ *  assigned to the device will trigger a slot freeze.   (The idea
+ *  is to prevent devices-gone-wild from corrupting system memory).
+ *  Buggy hardware/drivers will have a miserable time co-existing
+ *  with EEH.
+ *
+ *  Ideally, a PCI device driver, when suspecting that an isolation
+ *  event has occurred (e.g. by reading 0xff's), will then ask EEH
+ *  whether this is the case, and then take appropriate steps to
+ *  reset the PCI slot, the PCI device, and then resume operations.
+ *  However, until that day,  the checking is done here, with the
+ *  eeh_check_failure() routine embedded in the MMIO macros.  If
+ *  the slot is found to be isolated, an "EEH Event" is synthesized
+ *  and sent out for processing.
+ */
+
+/* If a device driver keeps reading an MMIO register in an interrupt
+ * handler after a slot isolation event, it might be broken.
+ * This sets the threshold for how many read attempts we allow
+ * before printing an error message.
+ */
+#define EEH_MAX_FAILS	2100000
+
+/* Time to wait for a PCI slot to report status, in milliseconds */
+#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
+
+/* Platform dependent EEH operations */
+struct eeh_ops *eeh_ops = NULL;
+
+int eeh_subsystem_enabled;
+EXPORT_SYMBOL(eeh_subsystem_enabled);
+
+/*
+ * EEH probe mode support. The intention is to support multiple
+ * platforms for EEH. Some platforms like pSeries do PCI emunation
+ * based on device tree. However, other platforms like powernv probe
+ * PCI devices from hardware. The flag is used to distinguish that.
+ * In addition, struct eeh_ops::probe would be invoked for particular
+ * OF node or PCI device so that the corresponding PE would be created
+ * there.
+ */
+int eeh_probe_mode;
+
+/* Global EEH mutex */
+DEFINE_MUTEX(eeh_mutex);
+
+/* Lock to avoid races due to multiple reports of an error */
+static DEFINE_RAW_SPINLOCK(confirm_error_lock);
+
+/* Buffer for reporting pci register dumps. Its here in BSS, and
+ * not dynamically alloced, so that it ends up in RMO where RTAS
+ * can access it.
+ */
+#define EEH_PCI_REGS_LOG_LEN 4096
+static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
+
+/*
+ * The struct is used to maintain the EEH global statistic
+ * information. Besides, the EEH global statistics will be
+ * exported to user space through procfs
+ */
+struct eeh_stats {
+	u64 no_device;		/* PCI device not found		*/
+	u64 no_dn;		/* OF node not found		*/
+	u64 no_cfg_addr;	/* Config address not found	*/
+	u64 ignored_check;	/* EEH check skipped		*/
+	u64 total_mmio_ffs;	/* Total EEH checks		*/
+	u64 false_positives;	/* Unnecessary EEH checks	*/
+	u64 slot_resets;	/* PE reset			*/
+};
+
+static struct eeh_stats eeh_stats;
+
+#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
+
+/**
+ * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
+ * @edev: device to report data for
+ * @buf: point to buffer in which to log
+ * @len: amount of room in buffer
+ *
+ * This routine captures assorted PCI configuration space data,
+ * and puts them into a buffer for RTAS error logging.
+ */
+static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
+{
+	struct device_node *dn = eeh_dev_to_of_node(edev);
+	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+	u32 cfg;
+	int cap, i;
+	int n = 0;
+
+	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
+	printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
+
+	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
+	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
+	printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
+
+	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
+	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
+	printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
+
+	if (!dev) {
+		printk(KERN_WARNING "EEH: no PCI device for this of node\n");
+		return n;
+	}
+
+	/* Gather bridge-specific registers */
+	if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
+		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
+		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
+		printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
+
+		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
+		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
+		printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
+	}
+
+	/* Dump out the PCI-X command and status regs */
+	cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+	if (cap) {
+		eeh_ops->read_config(dn, cap, 4, &cfg);
+		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
+		printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
+
+		eeh_ops->read_config(dn, cap+4, 4, &cfg);
+		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
+		printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
+	}
+
+	/* If PCI-E capable, dump PCI-E cap 10, and the AER */
+	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	if (cap) {
+		n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
+		printk(KERN_WARNING
+		       "EEH: PCI-E capabilities and status follow:\n");
+
+		for (i=0; i<=8; i++) {
+			eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
+			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
+			printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
+		}
+
+		cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+		if (cap) {
+			n += scnprintf(buf+n, len-n, "pci-e AER:\n");
+			printk(KERN_WARNING
+			       "EEH: PCI-E AER capability register set follows:\n");
+
+			for (i=0; i<14; i++) {
+				eeh_ops->read_config(dn, cap+4*i, 4, &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);
+			}
+		}
+	}
+
+	return n;
+}
+
+/**
+ * eeh_slot_error_detail - Generate combined log including driver log and error log
+ * @pe: EEH PE
+ * @severity: temporary or permanent error log
+ *
+ * This routine should be called to generate the combined log, which
+ * is comprised of driver log and error log. The driver log is figured
+ * out from the config space of the corresponding PCI device, while
+ * the error log is fetched through platform dependent function call.
+ */
+void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
+{
+	size_t loglen = 0;
+	struct eeh_dev *edev;
+
+	eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
+	eeh_ops->configure_bridge(pe);
+	eeh_pe_restore_bars(pe);
+
+	pci_regs_buf[0] = 0;
+	eeh_pe_for_each_dev(pe, edev) {
+		loglen += eeh_gather_pci_data(edev, pci_regs_buf,
+				EEH_PCI_REGS_LOG_LEN);
+        }
+
+	eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
+}
+
+/**
+ * eeh_token_to_phys - Convert EEH address token to phys address
+ * @token: I/O token, should be address in the form 0xA....
+ *
+ * This routine should be called to convert virtual I/O address
+ * to physical one.
+ */
+static inline unsigned long eeh_token_to_phys(unsigned long token)
+{
+	pte_t *ptep;
+	unsigned long pa;
+
+	ptep = find_linux_pte(init_mm.pgd, token);
+	if (!ptep)
+		return token;
+	pa = pte_pfn(*ptep) << PAGE_SHIFT;
+
+	return pa | (token & (PAGE_SIZE-1));
+}
+
+/**
+ * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
+ * @edev: eeh device
+ *
+ * Check for an EEH failure for the given device node.  Call this
+ * routine if the result of a read was all 0xff's and you want to
+ * find out if this is due to an EEH slot freeze.  This routine
+ * will query firmware for the EEH status.
+ *
+ * Returns 0 if there has not been an EEH error; otherwise returns
+ * a non-zero value and queues up a slot isolation event notification.
+ *
+ * It is safe to call this routine in an interrupt context.
+ */
+int eeh_dev_check_failure(struct eeh_dev *edev)
+{
+	int ret;
+	unsigned long flags;
+	struct device_node *dn;
+	struct pci_dev *dev;
+	struct eeh_pe *pe;
+	int rc = 0;
+	const char *location;
+
+	eeh_stats.total_mmio_ffs++;
+
+	if (!eeh_subsystem_enabled)
+		return 0;
+
+	if (!edev) {
+		eeh_stats.no_dn++;
+		return 0;
+	}
+	dn = eeh_dev_to_of_node(edev);
+	dev = eeh_dev_to_pci_dev(edev);
+	pe = edev->pe;
+
+	/* Access to IO BARs might get this far and still not want checking. */
+	if (!pe) {
+		eeh_stats.ignored_check++;
+		pr_debug("EEH: Ignored check for %s %s\n",
+			eeh_pci_name(dev), dn->full_name);
+		return 0;
+	}
+
+	if (!pe->addr && !pe->config_addr) {
+		eeh_stats.no_cfg_addr++;
+		return 0;
+	}
+
+	/* If we already have a pending isolation event for this
+	 * slot, we know it's bad already, we don't need to check.
+	 * Do this checking under a lock; as multiple PCI devices
+	 * in one slot might report errors simultaneously, and we
+	 * only want one error recovery routine running.
+	 */
+	raw_spin_lock_irqsave(&confirm_error_lock, flags);
+	rc = 1;
+	if (pe->state & EEH_PE_ISOLATED) {
+		pe->check_count++;
+		if (pe->check_count % EEH_MAX_FAILS == 0) {
+			location = of_get_property(dn, "ibm,loc-code", NULL);
+			printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
+				"location=%s driver=%s pci addr=%s\n",
+				pe->check_count, location,
+				eeh_driver_name(dev), eeh_pci_name(dev));
+			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
+				eeh_driver_name(dev));
+			dump_stack();
+		}
+		goto dn_unlock;
+	}
+
+	/*
+	 * Now test for an EEH failure.  This is VERY expensive.
+	 * Note that the eeh_config_addr may be a parent device
+	 * in the case of a device behind a bridge, or it may be
+	 * function zero of a multi-function device.
+	 * In any case they must share a common PHB.
+	 */
+	ret = eeh_ops->get_state(pe, NULL);
+
+	/* Note that config-io to empty slots may fail;
+	 * they are empty when they don't have children.
+	 * We will punt with the following conditions: Failure to get
+	 * PE's state, EEH not support and Permanently unavailable
+	 * state, PE is in good state.
+	 */
+	if ((ret < 0) ||
+	    (ret == EEH_STATE_NOT_SUPPORT) ||
+	    (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
+	    (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
+		eeh_stats.false_positives++;
+		pe->false_positives++;
+		rc = 0;
+		goto dn_unlock;
+	}
+
+	eeh_stats.slot_resets++;
+ 
+	/* Avoid repeated reports of this failure, including problems
+	 * with other functions on this device, and functions under
+	 * bridges.
+	 */
+	eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
+	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
+
+	eeh_send_failure_event(pe);
+
+	/* Most EEH events are due to device driver bugs.  Having
+	 * a stack trace will help the device-driver authors figure
+	 * out what happened.  So print that out.
+	 */
+	WARN(1, "EEH: failure detected\n");
+	return 1;
+
+dn_unlock:
+	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
+	return rc;
+}
+
+EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
+
+/**
+ * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
+ * @token: I/O token, should be address in the form 0xA....
+ * @val: value, should be all 1's (XXX why do we need this arg??)
+ *
+ * Check for an EEH failure at the given token address.  Call this
+ * routine if the result of a read was all 0xff's and you want to
+ * find out if this is due to an EEH slot freeze event.  This routine
+ * will query firmware for the EEH status.
+ *
+ * Note this routine is safe to call in an interrupt context.
+ */
+unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
+{
+	unsigned long addr;
+	struct eeh_dev *edev;
+
+	/* Finding the phys addr + pci device; this is pretty quick. */
+	addr = eeh_token_to_phys((unsigned long __force) token);
+	edev = eeh_addr_cache_get_dev(addr);
+	if (!edev) {
+		eeh_stats.no_device++;
+		return val;
+	}
+
+	eeh_dev_check_failure(edev);
+
+	pci_dev_put(eeh_dev_to_pci_dev(edev));
+	return val;
+}
+
+EXPORT_SYMBOL(eeh_check_failure);
+
+
+/**
+ * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
+ * @pe: EEH PE
+ *
+ * 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_pe *pe, int function)
+{
+	int rc;
+
+	rc = eeh_ops->set_option(pe, function);
+	if (rc)
+		pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
+			__func__, function, pe->phb->global_number, pe->addr, rc);
+
+	rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
+	if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
+	   (function == EEH_OPT_THAW_MMIO))
+		return 0;
+
+	return rc;
+}
+
+/**
+ * pcibios_set_pcie_slot_reset - Set PCI-E reset state
+ * @dev: pci device struct
+ * @state: reset state to enter
+ *
+ * Return value:
+ * 	0 if success
+ */
+int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
+{
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
+	struct eeh_pe *pe = edev->pe;
+
+	if (!pe) {
+		pr_err("%s: No PE found on PCI device %s\n",
+			__func__, pci_name(dev));
+		return -EINVAL;
+	}
+
+	switch (state) {
+	case pcie_deassert_reset:
+		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
+		break;
+	case pcie_hot_reset:
+		eeh_ops->reset(pe, EEH_RESET_HOT);
+		break;
+	case pcie_warm_reset:
+		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
+/**
+ * eeh_set_pe_freset - Check the required reset for the indicated device
+ * @data: EEH device
+ * @flag: 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.
+ */
+static void *eeh_set_dev_freset(void *data, void *flag)
+{
+	struct pci_dev *dev;
+	unsigned int *freset = (unsigned int *)flag;
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+
+	dev = eeh_dev_to_pci_dev(edev);
+	if (dev)
+		*freset |= dev->needs_freset;
+
+	return NULL;
+}
+
+/**
+ * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
+ * @pe: EEH PE
+ *
+ * Assert the PCI #RST line for 1/4 second.
+ */
+static void eeh_reset_pe_once(struct eeh_pe *pe)
+{
+	unsigned int freset = 0;
+
+	/* Determine type of EEH reset required for
+	 * Partitionable Endpoint, a hot-reset (1)
+	 * or a fundamental reset (3).
+	 * A fundamental reset required by any device under
+	 * Partitionable Endpoint trumps hot-reset.
+  	 */
+	eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
+
+	if (freset)
+		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
+	else
+		eeh_ops->reset(pe, EEH_RESET_HOT);
+
+	/* The PCI bus requires that the reset be held high for at least
+	 * a 100 milliseconds. We wait a bit longer 'just in case'.
+	 */
+#define PCI_BUS_RST_HOLD_TIME_MSEC 250
+	msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
+	
+	/* We might get hit with another EEH freeze as soon as the 
+	 * pci slot reset line is dropped. Make sure we don't miss
+	 * these, and clear the flag now.
+	 */
+	eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
+
+	eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
+
+	/* 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
+	 * up traffic.
+	 */
+#define PCI_BUS_SETTLE_TIME_MSEC 1800
+	msleep(PCI_BUS_SETTLE_TIME_MSEC);
+}
+
+/**
+ * eeh_reset_pe - Reset the indicated PE
+ * @pe: EEH PE
+ *
+ * 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_pe *pe)
+{
+	int i, rc;
+
+	/* Take three shots at resetting the bus */
+	for (i=0; i<3; i++) {
+		eeh_reset_pe_once(pe);
+
+		rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
+		if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
+			return 0;
+
+		if (rc < 0) {
+			pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
+				__func__, pe->phb->global_number, pe->addr);
+			return -1;
+		}
+		pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
+			i+1, pe->phb->global_number, pe->addr, rc);
+	}
+
+	return -1;
+}
+
+/**
+ * eeh_save_bars - Save device bars
+ * @edev: PCI device associated EEH device
+ *
+ * Save the values of the device bars. Unlike the restore
+ * routine, this routine is *not* recursive. This is because
+ * PCI devices are added individually; but, for the restore,
+ * an entire slot is reset at a time.
+ */
+void eeh_save_bars(struct eeh_dev *edev)
+{
+	int i;
+	struct device_node *dn;
+
+	if (!edev)
+		return;
+	dn = eeh_dev_to_of_node(edev);
+	
+	for (i = 0; i < 16; i++)
+		eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
+}
+
+/**
+ * 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.
+ * As a side effect we can determine here if eeh is supported at all.
+ * Note that we leave EEH on so failed config cycles won't cause a machine
+ * check.  If a user turns off EEH for a particular adapter they are really
+ * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't
+ * grant access to a slot if EEH isn't enabled, and so we always enable
+ * EEH for all slots/all devices.
+ *
+ * The eeh-force-off option disables EEH checking globally, for all slots.
+ * Even if force-off is set, the EEH hardware is still enabled, so that
+ * newer systems can boot.
+ */
+static int __init eeh_init(void)
+{
+	struct pci_controller *hose, *tmp;
+	struct device_node *phb;
+	int ret;
+
+	/* call platform initialization function */
+	if (!eeh_ops) {
+		pr_warning("%s: Platform EEH operation not found\n",
+			__func__);
+		return -EEXIST;
+	} else if ((ret = eeh_ops->init())) {
+		pr_warning("%s: Failed to call platform init function (%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
+	raw_spin_lock_init(&confirm_error_lock);
+
+	/* Enable EEH for all adapters */
+	if (eeh_probe_mode_devtree()) {
+		list_for_each_entry_safe(hose, tmp,
+			&hose_list, list_node) {
+			phb = hose->dn;
+			traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
+		}
+	}
+
+	if (eeh_subsystem_enabled)
+		pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
+	else
+		pr_warning("EEH: No capable adapters found\n");
+
+	return ret;
+}
+
+core_initcall_sync(eeh_init);
+
+/**
+ * eeh_add_device_early - Enable EEH for the indicated device_node
+ * @dn: device node for which to set up EEH
+ *
+ * This routine must be used to perform EEH initialization for PCI
+ * devices that were added after system boot (e.g. hotplug, dlpar).
+ * This routine must be called before any i/o is performed to the
+ * adapter (inluding any config-space i/o).
+ * Whether this actually enables EEH or not for this device depends
+ * on the CEC architecture, type of the device, on earlier boot
+ * command-line arguments & etc.
+ */
+static void eeh_add_device_early(struct device_node *dn)
+{
+	struct pci_controller *phb;
+
+	if (!of_node_to_eeh_dev(dn))
+		return;
+	phb = of_node_to_eeh_dev(dn)->phb;
+
+	/* USB Bus children of PCI devices will not have BUID's */
+	if (NULL == phb || 0 == phb->buid)
+		return;
+
+	/* FIXME: hotplug support on POWERNV */
+	eeh_ops->of_probe(dn, NULL);
+}
+
+/**
+ * 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)
+{
+	struct device_node *sib;
+
+	for_each_child_of_node(dn, sib)
+		eeh_add_device_tree_early(sib);
+	eeh_add_device_early(dn);
+}
+EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
+
+/**
+ * eeh_add_device_late - Perform EEH initialization for the indicated pci device
+ * @dev: pci device for which to set up EEH
+ *
+ * This routine must be used to complete EEH initialization for PCI
+ * devices that were added after system boot (e.g. hotplug, dlpar).
+ */
+static void eeh_add_device_late(struct pci_dev *dev)
+{
+	struct device_node *dn;
+	struct eeh_dev *edev;
+
+	if (!dev || !eeh_subsystem_enabled)
+		return;
+
+	pr_debug("EEH: Adding device %s\n", pci_name(dev));
+
+	dn = pci_device_to_OF_node(dev);
+	edev = of_node_to_eeh_dev(dn);
+	if (edev->pdev == dev) {
+		pr_debug("EEH: Already referenced !\n");
+		return;
+	}
+	WARN_ON(edev->pdev);
+
+	pci_dev_get(dev);
+	edev->pdev = dev;
+	dev->dev.archdata.edev = edev;
+
+	eeh_addr_cache_insert_dev(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)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+ 		eeh_add_device_late(dev);
+ 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ 			struct pci_bus *subbus = dev->subordinate;
+ 			if (subbus)
+ 				eeh_add_device_tree_late(subbus);
+ 		}
+	}
+}
+EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
+
+/**
+ * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
+ * @bus: PCI bus
+ *
+ * This routine must be used to add EEH sysfs files for PCI
+ * devices which are attached to the indicated PCI bus. The PCI bus
+ * is added after system boot through hotplug or dlpar.
+ */
+void eeh_add_sysfs_files(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		eeh_sysfs_add_device(dev);
+		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+			struct pci_bus *subbus = dev->subordinate;
+			if (subbus)
+				eeh_add_sysfs_files(subbus);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
+
+/**
+ * eeh_remove_device - Undo EEH setup for the indicated pci device
+ * @dev: pci device to be removed
+ * @purge_pe: remove the PE or not
+ *
+ * This routine should be called when a device is removed from
+ * a running system (e.g. by hotplug or dlpar).  It unregisters
+ * the PCI device from the EEH subsystem.  I/O errors affecting
+ * this device will no longer be detected after this call; thus,
+ * i/o errors affecting this slot may leave this device unusable.
+ */
+static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
+{
+	struct eeh_dev *edev;
+
+	if (!dev || !eeh_subsystem_enabled)
+		return;
+	edev = pci_dev_to_eeh_dev(dev);
+
+	/* Unregister the device with the EEH/PCI address search system */
+	pr_debug("EEH: Removing device %s\n", pci_name(dev));
+
+	if (!edev || !edev->pdev) {
+		pr_debug("EEH: Not referenced !\n");
+		return;
+	}
+	edev->pdev = NULL;
+	dev->dev.archdata.edev = NULL;
+	pci_dev_put(dev);
+
+	eeh_rmv_from_parent_pe(edev, purge_pe);
+	eeh_addr_cache_rmv_dev(dev);
+	eeh_sysfs_remove_device(dev);
+}
+
+/**
+ * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
+ * @dev: PCI device
+ * @purge_pe: remove the corresponding PE or not
+ *
+ * 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, int purge_pe)
+{
+	struct pci_bus *bus = dev->subordinate;
+	struct pci_dev *child, *tmp;
+
+	eeh_remove_device(dev, purge_pe);
+
+	if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+		list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
+			 eeh_remove_bus_device(child, purge_pe);
+	}
+}
+EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
+
+static int proc_eeh_show(struct seq_file *m, void *v)
+{
+	if (0 == eeh_subsystem_enabled) {
+		seq_printf(m, "EEH Subsystem is globally disabled\n");
+		seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
+	} else {
+		seq_printf(m, "EEH Subsystem is enabled\n");
+		seq_printf(m,
+				"no device=%llu\n"
+				"no device node=%llu\n"
+				"no config address=%llu\n"
+				"check not wanted=%llu\n"
+				"eeh_total_mmio_ffs=%llu\n"
+				"eeh_false_positives=%llu\n"
+				"eeh_slot_resets=%llu\n",
+				eeh_stats.no_device,
+				eeh_stats.no_dn,
+				eeh_stats.no_cfg_addr,
+				eeh_stats.ignored_check,
+				eeh_stats.total_mmio_ffs,
+				eeh_stats.false_positives,
+				eeh_stats.slot_resets);
+	}
+
+	return 0;
+}
+
+static int proc_eeh_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, proc_eeh_show, NULL);
+}
+
+static const struct file_operations proc_eeh_operations = {
+	.open      = proc_eeh_open,
+	.read      = seq_read,
+	.llseek    = seq_lseek,
+	.release   = single_release,
+};
+
+static int __init eeh_init_proc(void)
+{
+	if (machine_is(pseries))
+		proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
+	return 0;
+}
+__initcall(eeh_init_proc);
diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c
new file mode 100644
index 0000000..5a4c879
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_cache.c
@@ -0,0 +1,319 @@ 
+/*
+ * PCI address cache; allows the lookup of PCI devices based on I/O address
+ *
+ * Copyright IBM Corporation 2004
+ * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
+ *
+ * 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/list.h>
+#include <linux/pci.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
+
+
+/**
+ * The pci address cache subsystem.  This subsystem places
+ * PCI device address resources into a red-black tree, sorted
+ * according to the address range, so that given only an i/o
+ * address, the corresponding PCI device can be **quickly**
+ * found. It is safe to perform an address lookup in an interrupt
+ * context; this ability is an important feature.
+ *
+ * Currently, the only customer of this code is the EEH subsystem;
+ * thus, this code has been somewhat tailored to suit EEH better.
+ * In particular, the cache does *not* hold the addresses of devices
+ * for which EEH is not enabled.
+ *
+ * (Implementation Note: The RB tree seems to be better/faster
+ * than any hash algo I could think of for this problem, even
+ * with the penalty of slow pointer chases for d-cache misses).
+ */
+struct pci_io_addr_range {
+	struct rb_node rb_node;
+	unsigned long addr_lo;
+	unsigned long addr_hi;
+	struct eeh_dev *edev;
+	struct pci_dev *pcidev;
+	unsigned int flags;
+};
+
+static struct pci_io_addr_cache {
+	struct rb_root rb_root;
+	spinlock_t piar_lock;
+} pci_io_addr_cache_root;
+
+static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
+{
+	struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
+
+	while (n) {
+		struct pci_io_addr_range *piar;
+		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
+
+		if (addr < piar->addr_lo) {
+			n = n->rb_left;
+		} else {
+			if (addr > piar->addr_hi) {
+				n = n->rb_right;
+			} else {
+				pci_dev_get(piar->pcidev);
+				return piar->edev;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * eeh_addr_cache_get_dev - Get device, given only address
+ * @addr: mmio (PIO) phys address or i/o port number
+ *
+ * Given an mmio phys address, or a port number, find a pci device
+ * that implements this address.  Be sure to pci_dev_put the device
+ * when finished.  I/O port numbers are assumed to be offset
+ * from zero (that is, they do *not* have pci_io_addr added in).
+ * It is safe to call this function within an interrupt.
+ */
+struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
+{
+	struct eeh_dev *edev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
+	edev = __eeh_addr_cache_get_device(addr);
+	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
+	return edev;
+}
+
+#ifdef DEBUG
+/*
+ * Handy-dandy debug print routine, does nothing more
+ * than print out the contents of our addr cache.
+ */
+static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
+{
+	struct rb_node *n;
+	int cnt = 0;
+
+	n = rb_first(&cache->rb_root);
+	while (n) {
+		struct pci_io_addr_range *piar;
+		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
+		pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
+		       (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
+		       piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
+		cnt++;
+		n = rb_next(n);
+	}
+}
+#endif
+
+/* Insert address range into the rb tree. */
+static struct pci_io_addr_range *
+eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
+		      unsigned long ahi, unsigned int flags)
+{
+	struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
+	struct rb_node *parent = NULL;
+	struct pci_io_addr_range *piar;
+
+	/* Walk tree, find a place to insert into tree */
+	while (*p) {
+		parent = *p;
+		piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
+		if (ahi < piar->addr_lo) {
+			p = &parent->rb_left;
+		} else if (alo > piar->addr_hi) {
+			p = &parent->rb_right;
+		} else {
+			if (dev != piar->pcidev ||
+			    alo != piar->addr_lo || ahi != piar->addr_hi) {
+				pr_warning("PIAR: overlapping address range\n");
+			}
+			return piar;
+		}
+	}
+	piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
+	if (!piar)
+		return NULL;
+
+	pci_dev_get(dev);
+	piar->addr_lo = alo;
+	piar->addr_hi = ahi;
+	piar->edev = pci_dev_to_eeh_dev(dev);
+	piar->pcidev = dev;
+	piar->flags = flags;
+
+#ifdef DEBUG
+	pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
+	                  alo, ahi, pci_name(dev));
+#endif
+
+	rb_link_node(&piar->rb_node, parent, p);
+	rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
+
+	return piar;
+}
+
+static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
+{
+	struct device_node *dn;
+	struct eeh_dev *edev;
+	int i;
+
+	dn = pci_device_to_OF_node(dev);
+	if (!dn) {
+		pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
+		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. */
+	if (!edev->pe) {
+#ifdef DEBUG
+		pr_info("PCI: skip building address cache for=%s - %s\n",
+			pci_name(dev), dn->full_name);
+#endif
+		return;
+	}
+
+	/* Walk resources on this device, poke them into the tree */
+	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+		unsigned long start = pci_resource_start(dev,i);
+		unsigned long end = pci_resource_end(dev,i);
+		unsigned int flags = pci_resource_flags(dev,i);
+
+		/* We are interested only bus addresses, not dma or other stuff */
+		if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
+			continue;
+		if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
+			 continue;
+		eeh_addr_cache_insert(dev, start, end, flags);
+	}
+}
+
+/**
+ * eeh_addr_cache_insert_dev - Add a device to the address cache
+ * @dev: PCI device whose I/O addresses we are interested in.
+ *
+ * In order to support the fast lookup of devices based on addresses,
+ * we maintain a cache of devices that can be quickly searched.
+ * This routine adds a device to that cache.
+ */
+void eeh_addr_cache_insert_dev(struct pci_dev *dev)
+{
+	unsigned long flags;
+
+	/* Ignore PCI bridges */
+	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
+		return;
+
+	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
+	__eeh_addr_cache_insert_dev(dev);
+	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
+}
+
+static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
+{
+	struct rb_node *n;
+
+restart:
+	n = rb_first(&pci_io_addr_cache_root.rb_root);
+	while (n) {
+		struct pci_io_addr_range *piar;
+		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
+
+		if (piar->pcidev == dev) {
+			rb_erase(n, &pci_io_addr_cache_root.rb_root);
+			pci_dev_put(piar->pcidev);
+			kfree(piar);
+			goto restart;
+		}
+		n = rb_next(n);
+	}
+}
+
+/**
+ * eeh_addr_cache_rmv_dev - remove pci device from addr cache
+ * @dev: device to remove
+ *
+ * Remove a device from the addr-cache tree.
+ * This is potentially expensive, since it will walk
+ * the tree multiple times (once per resource).
+ * But so what; device removal doesn't need to be that fast.
+ */
+void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
+	__eeh_addr_cache_rmv_dev(dev);
+	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
+}
+
+/**
+ * eeh_addr_cache_build - Build a cache of I/O addresses
+ *
+ * Build a cache of pci i/o addresses.  This cache will be used to
+ * find the pci device that corresponds to a given address.
+ * This routine scans all pci busses to build the cache.
+ * Must be run late in boot process, after the pci controllers
+ * have been scanned for devices (after all device resources are known).
+ */
+void __init eeh_addr_cache_build(void)
+{
+	struct device_node *dn;
+	struct eeh_dev *edev;
+	struct pci_dev *dev = NULL;
+
+	spin_lock_init(&pci_io_addr_cache_root.piar_lock);
+
+	for_each_pci_dev(dev) {
+		eeh_addr_cache_insert_dev(dev);
+
+		dn = pci_device_to_OF_node(dev);
+		if (!dn)
+			continue;
+
+		edev = of_node_to_eeh_dev(dn);
+		if (!edev)
+			continue;
+
+		pci_dev_get(dev);  /* matching put is in eeh_remove_device() */
+		dev->dev.archdata.edev = edev;
+		edev->pdev = dev;
+
+		eeh_sysfs_add_device(dev);
+	}
+
+#ifdef DEBUG
+	/* Verify tree built up above, echo back the list of addrs. */
+	eeh_addr_cache_print(&pci_io_addr_cache_root);
+#endif
+}
+
diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c
new file mode 100644
index 0000000..1efa28f
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_dev.c
@@ -0,0 +1,112 @@ 
+/*
+ * 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 *eeh_dev_init(struct device_node *dn, void *data)
+{
+	struct pci_controller *phb = data;
+	struct eeh_dev *edev;
+
+	/* Allocate EEH device */
+	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+	if (!edev) {
+		pr_warning("%s: out of memory\n", __func__);
+		return NULL;
+	}
+
+	/* Associate EEH device with OF node */
+	PCI_DN(dn)->edev = edev;
+	edev->dn  = dn;
+	edev->phb = phb;
+	INIT_LIST_HEAD(&edev->list);
+
+	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 eeh_dev_phb_init_dynamic(struct pci_controller *phb)
+{
+	struct device_node *dn = phb->dn;
+
+	/* EEH PE for PHB */
+	eeh_phb_pe_create(phb);
+
+	/* 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
+ */
+static int __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);
+
+	pr_info("EEH: devices created\n");
+
+	return 0;
+}
+
+core_initcall(eeh_dev_phb_init);
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
new file mode 100644
index 0000000..a3fefb6
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -0,0 +1,552 @@ 
+/*
+ * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
+ * Copyright IBM Corp. 2004 2005
+ * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/eeh.h>
+#include <asm/eeh_event.h>
+#include <asm/ppc-pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/prom.h>
+#include <asm/rtas.h>
+
+/**
+ * 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)
+		return pdev->dev.driver->name;
+	return "";
+}
+
+/**
+ * eeh_pcid_get - Get the PCI device driver
+ * @pdev: PCI device
+ *
+ * The function is used to retrieve the PCI device driver for
+ * the indicated PCI device. Besides, we will increase the reference
+ * of the PCI device driver to prevent that being unloaded on
+ * the fly. Otherwise, kernel crash would be seen.
+ */
+static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
+{
+	if (!pdev || !pdev->driver)
+		return NULL;
+
+	if (!try_module_get(pdev->driver->driver.owner))
+		return NULL;
+
+	return pdev->driver;
+}
+
+/**
+ * eeh_pcid_put - Dereference on the PCI device driver
+ * @pdev: PCI device
+ *
+ * The function is called to do dereference on the PCI device
+ * driver of the indicated PCI device.
+ */
+static inline void eeh_pcid_put(struct pci_dev *pdev)
+{
+	if (!pdev || !pdev->driver)
+		return;
+
+	module_put(pdev->driver->driver.owner);
+}
+
+#if 0
+static void print_device_node_tree(struct pci_dn *pdn, int dent)
+{
+	int i;
+	struct device_node *pc;
+
+	if (!pdn)
+		return;
+	for (i = 0; i < dent; i++)
+		printk(" ");
+	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
+		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
+		pdn->eeh_pe_config_addr, pdn->node->full_name);
+	dent += 3;
+	pc = pdn->node->child;
+	while (pc) {
+		print_device_node_tree(PCI_DN(pc), dent);
+		pc = pc->sibling;
+	}
+}
+#endif
+
+/**
+ * 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)
+{
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
+
+	/* Don't disable MSI and MSI-X interrupts. They are
+	 * effectively disabled by the DMA Stopped state
+	 * when an EEH error occurs.
+	 */
+	if (dev->msi_enabled || dev->msix_enabled)
+		return;
+
+	if (!irq_has_action(dev->irq))
+		return;
+
+	edev->mode |= EEH_DEV_IRQ_DISABLED;
+	disable_irq_nosync(dev->irq);
+}
+
+/**
+ * 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)
+{
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
+
+	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
+		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
+		enable_irq(dev->irq);
+	}
+}
+
+/**
+ * eeh_report_error - Report pci error to each device driver
+ * @data: eeh device
+ * @userdata: return value
+ * 
+ * Report an EEH error to each device driver, collect up and 
+ * merge the device driver responses. Cumulative response 
+ * passed back in "userdata".
+ */
+static void *eeh_report_error(void *data, void *userdata)
+{
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+	enum pci_ers_result rc, *res = userdata;
+	struct pci_driver *driver;
+
+	/* We might not have the associated PCI device,
+	 * then we should continue for next one.
+	 */
+	if (!dev) return NULL;
+	dev->error_state = pci_channel_io_frozen;
+
+	driver = eeh_pcid_get(dev);
+	if (!driver) return NULL;
+
+	eeh_disable_irq(dev);
+
+	if (!driver->err_handler ||
+	    !driver->err_handler->error_detected) {
+		eeh_pcid_put(dev);
+		return NULL;
+	}
+
+	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
+
+	/* A driver that needs a reset trumps all others */
+	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
+	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+
+	eeh_pcid_put(dev);
+	return NULL;
+}
+
+/**
+ * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
+ * @data: eeh device
+ * @userdata: return value
+ *
+ * Tells each device driver that IO ports, MMIO and config space I/O
+ * are now enabled. Collects up and merges the device driver responses.
+ * Cumulative response passed back in "userdata".
+ */
+static void *eeh_report_mmio_enabled(void *data, void *userdata)
+{
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+	enum pci_ers_result rc, *res = userdata;
+	struct pci_driver *driver;
+
+	driver = eeh_pcid_get(dev);
+	if (!driver) return NULL;
+
+	if (!driver->err_handler ||
+	    !driver->err_handler->mmio_enabled) {
+		eeh_pcid_put(dev);
+		return NULL;
+	}
+
+	rc = driver->err_handler->mmio_enabled(dev);
+
+	/* A driver that needs a reset trumps all others */
+	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
+	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
+
+	eeh_pcid_put(dev);
+	return NULL;
+}
+
+/**
+ * eeh_report_reset - Tell device that slot has been reset
+ * @data: eeh 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 void *eeh_report_reset(void *data, void *userdata)
+{
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+	enum pci_ers_result rc, *res = userdata;
+	struct pci_driver *driver;
+
+	if (!dev) return NULL;
+	dev->error_state = pci_channel_io_normal;
+
+	driver = eeh_pcid_get(dev);
+	if (!driver) return NULL;
+
+	eeh_enable_irq(dev);
+
+	if (!driver->err_handler ||
+	    !driver->err_handler->slot_reset) {
+		eeh_pcid_put(dev);
+		return NULL;
+	}
+
+	rc = driver->err_handler->slot_reset(dev);
+	if ((*res == PCI_ERS_RESULT_NONE) ||
+	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
+	if (*res == PCI_ERS_RESULT_DISCONNECT &&
+	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
+
+	eeh_pcid_put(dev);
+	return NULL;
+}
+
+/**
+ * eeh_report_resume - Tell device to resume normal operations
+ * @data: eeh 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 void *eeh_report_resume(void *data, void *userdata)
+{
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+	struct pci_driver *driver;
+
+	if (!dev) return NULL;
+	dev->error_state = pci_channel_io_normal;
+
+	driver = eeh_pcid_get(dev);
+	if (!driver) return NULL;
+
+	eeh_enable_irq(dev);
+
+	if (!driver->err_handler ||
+	    !driver->err_handler->resume) {
+		eeh_pcid_put(dev);
+		return NULL;
+	}
+
+	driver->err_handler->resume(dev);
+
+	eeh_pcid_put(dev);
+	return NULL;
+}
+
+/**
+ * eeh_report_failure - Tell device driver that device is dead.
+ * @data: eeh device
+ * @userdata: return value
+ *
+ * This informs the device driver that the device is permanently
+ * dead, and that no further recovery attempts will be made on it.
+ */
+static void *eeh_report_failure(void *data, void *userdata)
+{
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
+	struct pci_driver *driver;
+
+	if (!dev) return NULL;
+	dev->error_state = pci_channel_io_perm_failure;
+
+	driver = eeh_pcid_get(dev);
+	if (!driver) return NULL;
+
+	eeh_disable_irq(dev);
+
+	if (!driver->err_handler ||
+	    !driver->err_handler->error_detected) {
+		eeh_pcid_put(dev);
+		return NULL;
+	}
+
+	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
+
+	eeh_pcid_put(dev);
+	return NULL;
+}
+
+/**
+ * eeh_reset_device - Perform actual reset of a pci slot
+ * @pe: EEH PE
+ * @bus: PCI bus corresponding to the isolcated slot
+ *
+ * This routine must be called to do reset on the indicated PE.
+ * During the reset, udev might be invoked because those affected
+ * PCI devices will be removed and then added.
+ */
+static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
+{
+	int cnt, rc;
+
+	/* pcibios will clear the counter; save the value */
+	cnt = pe->freeze_count;
+
+	/*
+	 * We don't remove the corresponding PE instances because
+	 * we need the information afterwords. The attached EEH
+	 * devices are expected to be attached soon when calling
+	 * into pcibios_add_pci_devices().
+	 */
+	if (bus)
+		__pcibios_remove_pci_devices(bus, 0);
+
+	/* Reset the pci controller. (Asserts RST#; resets config space).
+	 * Reconfigure bridges and devices. Don't try to bring the system
+	 * up if the reset failed for some reason.
+	 */
+	rc = eeh_reset_pe(pe);
+	if (rc)
+		return rc;
+
+	/* Restore PE */
+	eeh_ops->configure_bridge(pe);
+	eeh_pe_restore_bars(pe);
+
+	/* Give the system 5 seconds to finish running the user-space
+	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes, 
+	 * this is a hack, but if we don't do this, and try to bring 
+	 * the device up before the scripts have taken it down, 
+	 * potentially weird things happen.
+	 */
+	if (bus) {
+		ssleep(5);
+		pcibios_add_pci_devices(bus);
+	}
+	pe->freeze_count = cnt;
+
+	return 0;
+}
+
+/* The longest amount of time to wait for a pci device
+ * to come back on line, in seconds.
+ */
+#define MAX_WAIT_FOR_RECOVERY 150
+
+/**
+ * eeh_handle_event - Reset a PCI device after hard lockup.
+ * @pe: EEH PE
+ *
+ * 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).
+ */
+void eeh_handle_event(struct eeh_pe *pe)
+{
+	struct pci_bus *frozen_bus;
+	int rc = 0;
+	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
+
+	frozen_bus = eeh_pe_bus_get(pe);
+	if (!frozen_bus) {
+		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
+			__func__, pe->phb->global_number, pe->addr);
+		return;
+	}
+
+	pe->freeze_count++;
+	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
+		goto excess_failures;
+	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
+		pe->freeze_count);
+
+	/* Walk the various device drivers attached to this slot through
+	 * a reset sequence, giving each an opportunity to do what it needs
+	 * to accomplish the reset.  Each child gets a report of the
+	 * status ... if any child can't handle the reset, then the entire
+	 * slot is dlpar removed and added.
+	 */
+	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
+
+	/* Get the current PCI slot state. This can take a long time,
+	 * sometimes over 3 seconds for certain systems.
+	 */
+	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
+	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
+		printk(KERN_WARNING "EEH: Permanent failure\n");
+		goto hard_fail;
+	}
+
+	/* Since rtas may enable MMIO when posting the error log,
+	 * don't post the error log until after all dev drivers
+	 * have been informed.
+	 */
+	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
+
+	/* If all device drivers were EEH-unaware, then shut
+	 * down all of the device drivers, and hope they
+	 * go down willingly, without panicing the system.
+	 */
+	if (result == PCI_ERS_RESULT_NONE) {
+		rc = eeh_reset_device(pe, frozen_bus);
+		if (rc) {
+			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
+			goto hard_fail;
+		}
+	}
+
+	/* If all devices reported they can proceed, then re-enable MMIO */
+	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
+		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
+
+		if (rc < 0)
+			goto hard_fail;
+		if (rc) {
+			result = PCI_ERS_RESULT_NEED_RESET;
+		} else {
+			result = PCI_ERS_RESULT_NONE;
+			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
+		}
+	}
+
+	/* If all devices reported they can proceed, then re-enable DMA */
+	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
+		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
+
+		if (rc < 0)
+			goto hard_fail;
+		if (rc)
+			result = PCI_ERS_RESULT_NEED_RESET;
+		else
+			result = PCI_ERS_RESULT_RECOVERED;
+	}
+
+	/* If any device has a hard failure, then shut off everything. */
+	if (result == PCI_ERS_RESULT_DISCONNECT) {
+		printk(KERN_WARNING "EEH: Device driver gave up\n");
+		goto hard_fail;
+	}
+
+	/* If any device called out for a reset, then reset the slot */
+	if (result == PCI_ERS_RESULT_NEED_RESET) {
+		rc = eeh_reset_device(pe, NULL);
+		if (rc) {
+			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
+			goto hard_fail;
+		}
+		result = PCI_ERS_RESULT_NONE;
+		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
+	}
+
+	/* All devices should claim they have recovered by now. */
+	if ((result != PCI_ERS_RESULT_RECOVERED) &&
+	    (result != PCI_ERS_RESULT_NONE)) {
+		printk(KERN_WARNING "EEH: Not recovered\n");
+		goto hard_fail;
+	}
+
+	/* Tell all device drivers that they can resume operations */
+	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
+
+	return;
+	
+excess_failures:
+	/*
+	 * About 90% of all real-life EEH failures in the field
+	 * are due to poorly seated PCI cards. Only 10% or so are
+	 * due to actual, failed cards.
+	 */
+	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
+	       "last hour and has been permanently disabled.\n"
+	       "Please try reseating or replacing it.\n",
+		pe->phb->global_number, pe->addr,
+		pe->freeze_count);
+	goto perm_error;
+
+hard_fail:
+	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
+	       "Please try reseating or replacing it\n",
+		pe->phb->global_number, pe->addr);
+
+perm_error:
+	eeh_slot_error_detail(pe, EEH_LOG_PERM);
+
+	/* Notify all devices that they're about to go down. */
+	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
+
+	/* Shut down the device drivers for good. */
+	if (frozen_bus)
+		pcibios_remove_pci_devices(frozen_bus);
+}
+
diff --git a/arch/powerpc/kernel/eeh_event.c b/arch/powerpc/kernel/eeh_event.c
new file mode 100644
index 0000000..185bedd
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_event.c
@@ -0,0 +1,142 @@ 
+/*
+ * 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
+ *
+ * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
+ */
+
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <asm/eeh_event.h>
+#include <asm/ppc-pci.h>
+
+/** Overview:
+ *  EEH error states may be detected within exception handlers;
+ *  however, the recovery processing needs to occur asynchronously
+ *  in a normal kernel context and not an interrupt context.
+ *  This pair of routines creates an event and queues it onto a
+ *  work-queue, where a worker thread can drive recovery.
+ */
+
+/* EEH event workqueue setup. */
+static DEFINE_SPINLOCK(eeh_eventlist_lock);
+LIST_HEAD(eeh_eventlist);
+static void eeh_thread_launcher(struct work_struct *);
+DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
+
+/* Serialize reset sequences for a given pci device */
+DEFINE_MUTEX(eeh_event_mutex);
+
+/**
+ * eeh_event_handler - Dispatch EEH events.
+ * @dummy - unused
+ *
+ * The detection of a frozen slot can occur inside an interrupt,
+ * where it can be hard to do anything about it.  The goal of this
+ * routine is to pull these detection events out of the context
+ * of the interrupt handler, and re-dispatch them for processing
+ * at a later time in a normal context.
+ */
+static int eeh_event_handler(void * dummy)
+{
+	unsigned long flags;
+	struct eeh_event *event;
+	struct eeh_pe *pe;
+
+	spin_lock_irqsave(&eeh_eventlist_lock, flags);
+	event = NULL;
+
+	/* Unqueue the event, get ready to process. */
+	if (!list_empty(&eeh_eventlist)) {
+		event = list_entry(eeh_eventlist.next, struct eeh_event, list);
+		list_del(&event->list);
+	}
+	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
+
+	if (event == NULL)
+		return 0;
+
+	/* Serialize processing of EEH events */
+	mutex_lock(&eeh_event_mutex);
+	pe = event->pe;
+	eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
+	pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
+		pe->phb->global_number, pe->addr);
+
+	set_current_state(TASK_INTERRUPTIBLE);	/* Don't add to load average */
+	eeh_handle_event(pe);
+	eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
+
+	kfree(event);
+	mutex_unlock(&eeh_event_mutex);
+
+	/* If there are no new errors after an hour, clear the counter. */
+	if (pe && pe->freeze_count > 0) {
+		msleep_interruptible(3600*1000);
+		if (pe->freeze_count > 0)
+			pe->freeze_count--;
+
+	}
+
+	return 0;
+}
+
+/**
+ * eeh_thread_launcher - Start kernel thread to handle EEH events
+ * @dummy - unused
+ *
+ * This routine is called to start the kernel thread for processing
+ * EEH event.
+ */
+static void eeh_thread_launcher(struct work_struct *dummy)
+{
+	if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
+		printk(KERN_ERR "Failed to start EEH daemon\n");
+}
+
+/**
+ * eeh_send_failure_event - Generate a PCI error event
+ * @pe: EEH PE
+ *
+ * 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 eeh_pe *pe)
+{
+	unsigned long flags;
+	struct eeh_event *event;
+
+	event = kzalloc(sizeof(*event), GFP_ATOMIC);
+	if (!event) {
+		pr_err("EEH: out of memory, event not handled\n");
+		return -ENOMEM;
+	}
+	event->pe = pe;
+
+	/* We may or may not be called in an interrupt context */
+	spin_lock_irqsave(&eeh_eventlist_lock, flags);
+	list_add(&event->list, &eeh_eventlist);
+	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
+
+	schedule_work(&eeh_event_wq);
+
+	return 0;
+}
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c
new file mode 100644
index 0000000..9d4a9e8
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_pe.c
@@ -0,0 +1,653 @@ 
+/*
+ * The file intends to implement PE based on the information from
+ * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
+ * All the PEs should be organized as hierarchy tree. The first level
+ * of the tree will be associated to existing PHBs since the particular
+ * PE is only meaningful in one PHB domain.
+ *
+ * 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>
+
+static LIST_HEAD(eeh_phb_pe);
+
+/**
+ * eeh_pe_alloc - Allocate PE
+ * @phb: PCI controller
+ * @type: PE type
+ *
+ * Allocate PE instance dynamically.
+ */
+static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
+{
+	struct eeh_pe *pe;
+
+	/* Allocate PHB PE */
+	pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
+	if (!pe) return NULL;
+
+	/* Initialize PHB PE */
+	pe->type = type;
+	pe->phb = phb;
+	INIT_LIST_HEAD(&pe->child_list);
+	INIT_LIST_HEAD(&pe->child);
+	INIT_LIST_HEAD(&pe->edevs);
+
+	return pe;
+}
+
+/**
+ * eeh_phb_pe_create - Create PHB PE
+ * @phb: PCI controller
+ *
+ * The function should be called while the PHB is detected during
+ * system boot or PCI hotplug in order to create PHB PE.
+ */
+int eeh_phb_pe_create(struct pci_controller *phb)
+{
+	struct eeh_pe *pe;
+
+	/* Allocate PHB PE */
+	pe = eeh_pe_alloc(phb, EEH_PE_PHB);
+	if (!pe) {
+		pr_err("%s: out of memory!\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* Put it into the list */
+	eeh_lock();
+	list_add_tail(&pe->child, &eeh_phb_pe);
+	eeh_unlock();
+
+	pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
+
+	return 0;
+}
+
+/**
+ * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
+ * @phb: PCI controller
+ *
+ * The overall PEs form hierarchy tree. The first layer of the
+ * hierarchy tree is composed of PHB PEs. The function is used
+ * to retrieve the corresponding PHB PE according to the given PHB.
+ */
+static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
+{
+	struct eeh_pe *pe;
+
+	list_for_each_entry(pe, &eeh_phb_pe, child) {
+		/*
+		 * Actually, we needn't check the type since
+		 * the PE for PHB has been determined when that
+		 * was created.
+		 */
+		if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
+			return pe;
+	}
+
+	return NULL;
+}
+
+/**
+ * eeh_pe_next - Retrieve the next PE in the tree
+ * @pe: current PE
+ * @root: root PE
+ *
+ * The function is used to retrieve the next PE in the
+ * hierarchy PE tree.
+ */
+static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
+				  struct eeh_pe *root)
+{
+	struct list_head *next = pe->child_list.next;
+
+	if (next == &pe->child_list) {
+		while (1) {
+			if (pe == root)
+				return NULL;
+			next = pe->child.next;
+			if (next != &pe->parent->child_list)
+				break;
+			pe = pe->parent;
+		}
+	}
+
+	return list_entry(next, struct eeh_pe, child);
+}
+
+/**
+ * eeh_pe_traverse - Traverse PEs in the specified PHB
+ * @root: root PE
+ * @fn: callback
+ * @flag: extra parameter to callback
+ *
+ * The function is used to traverse the specified PE and its
+ * child PEs. The traversing is to be terminated once the
+ * callback returns something other than NULL, or no more PEs
+ * to be traversed.
+ */
+static void *eeh_pe_traverse(struct eeh_pe *root,
+			eeh_traverse_func fn, void *flag)
+{
+	struct eeh_pe *pe;
+	void *ret;
+
+	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
+		ret = fn(pe, flag);
+		if (ret) return ret;
+	}
+
+	return NULL;
+}
+
+/**
+ * eeh_pe_dev_traverse - Traverse the devices from the PE
+ * @root: EEH PE
+ * @fn: function callback
+ * @flag: extra parameter to callback
+ *
+ * The function is used to traverse the devices of the specified
+ * PE and its child PEs.
+ */
+void *eeh_pe_dev_traverse(struct eeh_pe *root,
+		eeh_traverse_func fn, void *flag)
+{
+	struct eeh_pe *pe;
+	struct eeh_dev *edev;
+	void *ret;
+
+	if (!root) {
+		pr_warning("%s: Invalid PE %p\n", __func__, root);
+		return NULL;
+	}
+
+	eeh_lock();
+
+	/* Traverse root PE */
+	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
+		eeh_pe_for_each_dev(pe, edev) {
+			ret = fn(edev, flag);
+			if (ret) {
+				eeh_unlock();
+				return ret;
+			}
+		}
+	}
+
+	eeh_unlock();
+
+	return NULL;
+}
+
+/**
+ * __eeh_pe_get - Check the PE address
+ * @data: EEH PE
+ * @flag: EEH device
+ *
+ * For one particular PE, it can be identified by PE address
+ * or tranditional BDF address. BDF address is composed of
+ * Bus/Device/Function number. The extra data referred by flag
+ * indicates which type of address should be used.
+ */
+static void *__eeh_pe_get(void *data, void *flag)
+{
+	struct eeh_pe *pe = (struct eeh_pe *)data;
+	struct eeh_dev *edev = (struct eeh_dev *)flag;
+
+	/* Unexpected PHB PE */
+	if (pe->type & EEH_PE_PHB)
+		return NULL;
+
+	/* We prefer PE address */
+	if (edev->pe_config_addr &&
+	   (edev->pe_config_addr == pe->addr))
+		return pe;
+
+	/* Try BDF address */
+	if (edev->pe_config_addr &&
+	   (edev->config_addr == pe->config_addr))
+		return pe;
+
+	return NULL;
+}
+
+/**
+ * eeh_pe_get - Search PE based on the given address
+ * @edev: EEH device
+ *
+ * Search the corresponding PE based on the specified address which
+ * is included in the eeh device. The function is used to check if
+ * the associated PE has been created against the PE address. It's
+ * notable that the PE address has 2 format: traditional PE address
+ * which is composed of PCI bus/device/function number, or unified
+ * PE address.
+ */
+static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
+{
+	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
+	struct eeh_pe *pe;
+
+	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
+
+	return pe;
+}
+
+/**
+ * eeh_pe_get_parent - Retrieve the parent PE
+ * @edev: EEH device
+ *
+ * The whole PEs existing in the system are organized as hierarchy
+ * tree. The function is used to retrieve the parent PE according
+ * to the parent EEH device.
+ */
+static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
+{
+	struct device_node *dn;
+	struct eeh_dev *parent;
+
+	/*
+	 * It might have the case for the indirect parent
+	 * EEH device already having associated PE, but
+	 * the direct parent EEH device doesn't have yet.
+	 */
+	dn = edev->dn->parent;
+	while (dn) {
+		/* We're poking out of PCI territory */
+		if (!PCI_DN(dn)) return NULL;
+
+		parent = of_node_to_eeh_dev(dn);
+		/* We're poking out of PCI territory */
+		if (!parent) return NULL;
+
+		if (parent->pe)
+			return parent->pe;
+
+		dn = dn->parent;
+	}
+
+	return NULL;
+}
+
+/**
+ * eeh_add_to_parent_pe - Add EEH device to parent PE
+ * @edev: EEH device
+ *
+ * Add EEH device to the parent PE. If the parent PE already
+ * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
+ * we have to create new PE to hold the EEH device and the new
+ * PE will be linked to its parent PE as well.
+ */
+int eeh_add_to_parent_pe(struct eeh_dev *edev)
+{
+	struct eeh_pe *pe, *parent;
+
+	eeh_lock();
+
+	/*
+	 * Search the PE has been existing or not according
+	 * to the PE address. If that has been existing, the
+	 * PE should be composed of PCI bus and its subordinate
+	 * components.
+	 */
+	pe = eeh_pe_get(edev);
+	if (pe && !(pe->type & EEH_PE_INVALID)) {
+		if (!edev->pe_config_addr) {
+			eeh_unlock();
+			pr_err("%s: PE with addr 0x%x already exists\n",
+				__func__, edev->config_addr);
+			return -EEXIST;
+		}
+
+		/* Mark the PE as type of PCI bus */
+		pe->type = EEH_PE_BUS;
+		edev->pe = pe;
+
+		/* Put the edev to PE */
+		list_add_tail(&edev->list, &pe->edevs);
+		eeh_unlock();
+		pr_debug("EEH: Add %s to Bus PE#%x\n",
+			edev->dn->full_name, pe->addr);
+
+		return 0;
+	} else if (pe && (pe->type & EEH_PE_INVALID)) {
+		list_add_tail(&edev->list, &pe->edevs);
+		edev->pe = pe;
+		/*
+		 * We're running to here because of PCI hotplug caused by
+		 * EEH recovery. We need clear EEH_PE_INVALID until the top.
+		 */
+		parent = pe;
+		while (parent) {
+			if (!(parent->type & EEH_PE_INVALID))
+				break;
+			parent->type &= ~EEH_PE_INVALID;
+			parent = parent->parent;
+		}
+		eeh_unlock();
+		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
+			edev->dn->full_name, pe->addr, pe->parent->addr);
+
+		return 0;
+	}
+
+	/* Create a new EEH PE */
+	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
+	if (!pe) {
+		eeh_unlock();
+		pr_err("%s: out of memory!\n", __func__);
+		return -ENOMEM;
+	}
+	pe->addr	= edev->pe_config_addr;
+	pe->config_addr	= edev->config_addr;
+
+	/*
+	 * Put the new EEH PE into hierarchy tree. If the parent
+	 * can't be found, the newly created PE will be attached
+	 * to PHB directly. Otherwise, we have to associate the
+	 * PE with its parent.
+	 */
+	parent = eeh_pe_get_parent(edev);
+	if (!parent) {
+		parent = eeh_phb_pe_get(edev->phb);
+		if (!parent) {
+			eeh_unlock();
+			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
+				__func__, edev->phb->global_number);
+			edev->pe = NULL;
+			kfree(pe);
+			return -EEXIST;
+		}
+	}
+	pe->parent = parent;
+
+	/*
+	 * Put the newly created PE into the child list and
+	 * link the EEH device accordingly.
+	 */
+	list_add_tail(&pe->child, &parent->child_list);
+	list_add_tail(&edev->list, &pe->edevs);
+	edev->pe = pe;
+	eeh_unlock();
+	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
+		edev->dn->full_name, pe->addr, pe->parent->addr);
+
+	return 0;
+}
+
+/**
+ * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
+ * @edev: EEH device
+ * @purge_pe: remove PE or not
+ *
+ * The PE hierarchy tree might be changed when doing PCI hotplug.
+ * Also, the PCI devices or buses could be removed from the system
+ * during EEH recovery. So we have to call the function remove the
+ * corresponding PE accordingly if necessary.
+ */
+int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
+{
+	struct eeh_pe *pe, *parent, *child;
+	int cnt;
+
+	if (!edev->pe) {
+		pr_warning("%s: No PE found for EEH device %s\n",
+			__func__, edev->dn->full_name);
+		return -EEXIST;
+	}
+
+	eeh_lock();
+
+	/* Remove the EEH device */
+	pe = edev->pe;
+	edev->pe = NULL;
+	list_del(&edev->list);
+
+	/*
+	 * Check if the parent PE includes any EEH devices.
+	 * If not, we should delete that. Also, we should
+	 * delete the parent PE if it doesn't have associated
+	 * child PEs and EEH devices.
+	 */
+	while (1) {
+		parent = pe->parent;
+		if (pe->type & EEH_PE_PHB)
+			break;
+
+		if (purge_pe) {
+			if (list_empty(&pe->edevs) &&
+			    list_empty(&pe->child_list)) {
+				list_del(&pe->child);
+				kfree(pe);
+			} else {
+				break;
+			}
+		} else {
+			if (list_empty(&pe->edevs)) {
+				cnt = 0;
+				list_for_each_entry(child, &pe->child_list, child) {
+					if (!(child->type & EEH_PE_INVALID)) {
+						cnt++;
+						break;
+					}
+				}
+
+				if (!cnt)
+					pe->type |= EEH_PE_INVALID;
+				else
+					break;
+			}
+		}
+
+		pe = parent;
+	}
+
+	eeh_unlock();
+
+	return 0;
+}
+
+/**
+ * __eeh_pe_state_mark - Mark the state for the PE
+ * @data: EEH PE
+ * @flag: state
+ *
+ * The function is used to mark the indicated state for the given
+ * PE. Also, the associated PCI devices will be put into IO frozen
+ * state as well.
+ */
+static void *__eeh_pe_state_mark(void *data, void *flag)
+{
+	struct eeh_pe *pe = (struct eeh_pe *)data;
+	int state = *((int *)flag);
+	struct eeh_dev *tmp;
+	struct pci_dev *pdev;
+
+	/*
+	 * Mark the PE with the indicated state. Also,
+	 * the associated PCI device will be put into
+	 * I/O frozen state to avoid I/O accesses from
+	 * the PCI device driver.
+	 */
+	pe->state |= state;
+	eeh_pe_for_each_dev(pe, tmp) {
+		pdev = eeh_dev_to_pci_dev(tmp);
+		if (pdev)
+			pdev->error_state = pci_channel_io_frozen;
+	}
+
+	return NULL;
+}
+
+/**
+ * eeh_pe_state_mark - Mark specified state for PE and its associated device
+ * @pe: EEH PE
+ *
+ * EEH error affects the current PE and its child PEs. The function
+ * is used to mark appropriate state for the affected PEs and the
+ * associated devices.
+ */
+void eeh_pe_state_mark(struct eeh_pe *pe, int state)
+{
+	eeh_lock();
+	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
+	eeh_unlock();
+}
+
+/**
+ * __eeh_pe_state_clear - Clear state for the PE
+ * @data: EEH PE
+ * @flag: state
+ *
+ * The function is used to clear the indicated state from the
+ * given PE. Besides, we also clear the check count of the PE
+ * as well.
+ */
+static void *__eeh_pe_state_clear(void *data, void *flag)
+{
+	struct eeh_pe *pe = (struct eeh_pe *)data;
+	int state = *((int *)flag);
+
+	pe->state &= ~state;
+	pe->check_count = 0;
+
+	return NULL;
+}
+
+/**
+ * eeh_pe_state_clear - Clear state for the PE and its children
+ * @pe: PE
+ * @state: state to be cleared
+ *
+ * When the PE and its children has been recovered from error,
+ * we need clear the error state for that. The function is used
+ * for the purpose.
+ */
+void eeh_pe_state_clear(struct eeh_pe *pe, int state)
+{
+	eeh_lock();
+	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
+	eeh_unlock();
+}
+
+/**
+ * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
+ * @data: EEH device
+ * @flag: Unused
+ *
+ * Loads the PCI configuration space base address registers,
+ * the expansion ROM base address, the latency timer, and etc.
+ * from the saved values in the device node.
+ */
+static void *eeh_restore_one_device_bars(void *data, void *flag)
+{
+	int i;
+	u32 cmd;
+	struct eeh_dev *edev = (struct eeh_dev *)data;
+	struct device_node *dn = eeh_dev_to_of_node(edev);
+
+	for (i = 4; i < 10; i++)
+		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
+	/* 12 == Expansion ROM Address */
+	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
+
+#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
+#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
+
+	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
+		SAVED_BYTE(PCI_CACHE_LINE_SIZE));
+	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
+		SAVED_BYTE(PCI_LATENCY_TIMER));
+
+	/* max latency, min grant, interrupt pin and line */
+	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
+
+	/*
+	 * Restore PERR & SERR bits, some devices require it,
+	 * don't touch the other command bits
+	 */
+	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
+	if (edev->config_space[1] & PCI_COMMAND_PARITY)
+		cmd |= PCI_COMMAND_PARITY;
+	else
+		cmd &= ~PCI_COMMAND_PARITY;
+	if (edev->config_space[1] & PCI_COMMAND_SERR)
+		cmd |= PCI_COMMAND_SERR;
+	else
+		cmd &= ~PCI_COMMAND_SERR;
+	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
+
+	return NULL;
+}
+
+/**
+ * eeh_pe_restore_bars - Restore the PCI config space info
+ * @pe: EEH PE
+ *
+ * This routine performs a recursive walk to the children
+ * of this device as well.
+ */
+void eeh_pe_restore_bars(struct eeh_pe *pe)
+{
+	/*
+	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
+	 * will take that.
+	 */
+	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
+}
+
+/**
+ * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
+ * @pe: EEH PE
+ *
+ * Retrieve the PCI bus according to the given PE. Basically,
+ * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
+ * primary PCI bus will be retrieved. The parent bus will be
+ * returned for BUS PE. However, we don't have associated PCI
+ * bus for DEVICE PE.
+ */
+struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
+{
+	struct pci_bus *bus = NULL;
+	struct eeh_dev *edev;
+	struct pci_dev *pdev;
+
+	eeh_lock();
+
+	if (pe->type & EEH_PE_PHB) {
+		bus = pe->phb->bus;
+	} else if (pe->type & EEH_PE_BUS ||
+		   pe->type & EEH_PE_DEVICE) {
+		edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
+		pdev = eeh_dev_to_pci_dev(edev);
+		if (pdev)
+			bus = pdev->bus;
+	}
+
+	eeh_unlock();
+
+	return bus;
+}
diff --git a/arch/powerpc/kernel/eeh_sysfs.c b/arch/powerpc/kernel/eeh_sysfs.c
new file mode 100644
index 0000000..d377083
--- /dev/null
+++ b/arch/powerpc/kernel/eeh_sysfs.c
@@ -0,0 +1,75 @@ 
+/*
+ * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
+ * Copyright IBM Corporation 2007
+ * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
+ *
+ * All rights reserved.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
+ */
+#include <linux/pci.h>
+#include <linux/stat.h>
+#include <asm/ppc-pci.h>
+#include <asm/pci-bridge.h>
+
+/**
+ * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
+ * @_name: name of file in sysfs directory
+ * @_memb: name of member in struct pci_dn to access
+ * @_format: printf format for display
+ *
+ * All of the attributes look very similar, so just
+ * auto-gen a cut-n-paste routine to display them.
+ */
+#define EEH_SHOW_ATTR(_name,_memb,_format)               \
+static ssize_t eeh_show_##_name(struct device *dev,      \
+		struct device_attribute *attr, char *buf)          \
+{                                                        \
+	struct pci_dev *pdev = to_pci_dev(dev);               \
+	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
+	                                                      \
+	if (!edev)                                            \
+		return 0;                                     \
+	                                                      \
+	return sprintf(buf, _format "\n", edev->_memb);       \
+}                                                        \
+static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
+
+EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
+EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
+EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
+
+void eeh_sysfs_add_device(struct pci_dev *pdev)
+{
+	int rc=0;
+
+	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
+	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
+	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
+
+	if (rc)
+		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
+}
+
+void eeh_sysfs_remove_device(struct pci_dev *pdev)
+{
+	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
+	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
+	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
+}
+
diff --git a/arch/powerpc/kernel/pci_hotplug.c b/arch/powerpc/kernel/pci_hotplug.c
new file mode 100644
index 0000000..3f60880
--- /dev/null
+++ b/arch/powerpc/kernel/pci_hotplug.c
@@ -0,0 +1,111 @@ 
+/*
+ * Derived from "arch/powerpc/platforms/pseries/pci_dlpar.c"
+ *
+ * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
+ * Copyright (C) 2005 International Business Machines
+ *
+ * Updates, 2005, John Rose <johnrose@austin.ibm.com>
+ * Updates, 2005, Linas Vepstas <linas@austin.ibm.com>
+ * Updates, 2013, Gavin Shan <shangw@linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+
+#include <linux/pci.h>
+#include <linux/export.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
+#include <asm/firmware.h>
+#include <asm/eeh.h>
+
+/**
+ * __pcibios_remove_pci_devices - remove all devices under this bus
+ * @bus: the indicated PCI bus
+ * @purge_pe: destroy the PE on removal of PCI devices
+ *
+ * Remove all of the PCI devices under this bus both from the
+ * linux pci device tree, and from the powerpc EEH address cache.
+ * By default, the corresponding PE will be destroied during the
+ * normal PCI hotplug path. For PCI hotplug during EEH recovery,
+ * the corresponding PE won't be destroied and deallocated.
+ */
+void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
+{
+	struct pci_dev *dev, *tmp;
+	struct pci_bus *child_bus;
+
+	/* First go down child busses */
+	list_for_each_entry(child_bus, &bus->children, node)
+		__pcibios_remove_pci_devices(child_bus, purge_pe);
+
+	pr_debug("PCI: Removing devices on bus %04x:%02x\n",
+		 pci_domain_nr(bus),  bus->number);
+	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
+		pr_debug("     * Removing %s...\n", pci_name(dev));
+		eeh_remove_bus_device(dev, purge_pe);
+		pci_stop_and_remove_bus_device(dev);
+	}
+}
+
+/**
+ * pcibios_remove_pci_devices - remove all devices under this bus
+ * @bus: the indicated PCI bus
+ *
+ * Remove all of the PCI devices under this bus both from the
+ * linux pci device tree, and from the powerpc EEH address cache.
+ */
+void pcibios_remove_pci_devices(struct pci_bus *bus)
+{
+	__pcibios_remove_pci_devices(bus, 1);
+}
+EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
+
+/**
+ * pcibios_add_pci_devices - adds new pci devices to bus
+ * @bus: the indicated PCI bus
+ *
+ * This routine will find and fixup new pci devices under
+ * the indicated bus. This routine presumes that there
+ * might already be some devices under this bridge, so
+ * it carefully tries to add only new devices.  (And that
+ * is how this routine differs from other, similar pcibios
+ * routines.)
+ */
+void pcibios_add_pci_devices(struct pci_bus * bus)
+{
+	int slotno, num, mode, pass, max;
+	struct pci_dev *dev;
+	struct device_node *dn = pci_bus_to_OF_node(bus);
+
+	eeh_add_device_tree_early(dn);
+
+	mode = PCI_PROBE_NORMAL;
+	if (ppc_md.pci_probe_mode)
+		mode = ppc_md.pci_probe_mode(bus);
+
+	if (mode == PCI_PROBE_DEVTREE) {
+		/* use ofdt-based probe */
+		of_rescan_bus(dn, bus);
+	} else if (mode == PCI_PROBE_NORMAL) {
+		/* use legacy probe */
+		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
+		num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
+		if (!num)
+			return;
+		pcibios_setup_bus_devices(bus);
+		max = bus->busn_res.start;
+		for (pass = 0; pass < 2; pass++) {
+			list_for_each_entry(dev, &bus->devices, bus_list) {
+				if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
+				    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+					max = pci_scan_bridge(bus, dev,
+							      max, pass);
+			}
+		}
+	}
+	pcibios_finish_adding_to_bus(bus);
+}
+EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index b62aab3..bed8c60 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -164,6 +164,11 @@  config IBMEBUS
 	help
 	  Bus device driver for GX bus based adapters.
 
+config EEH
+	bool
+	depends on (PPC_POWERNV || PPC_PSERIES) && PCI
+	default y
+
 config PPC_MPC106
 	bool
 	default n
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 4459eff..1bd3399 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -33,11 +33,6 @@  config PPC_SPLPAR
 	  processors, that is, which share physical processors between
 	  two or more partitions.
 
-config EEH
-	bool
-	depends on PPC_PSERIES && PCI
-	default y
-
 config PSERIES_MSI
        bool
        depends on PCI_MSI && EEH
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 53866e5..8ae0103 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -6,9 +6,7 @@  obj-y			:= lpar.o hvCall.o nvram.o reconfig.o \
 			   firmware.o power.o dlpar.o mobility.o
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_SCANLOG)	+= scanlog.o
-obj-$(CONFIG_EEH)	+= eeh.o eeh_pe.o eeh_dev.o eeh_cache.o \
-			   eeh_driver.o eeh_event.o eeh_sysfs.o \
-			   eeh_pseries.o
+obj-$(CONFIG_EEH)	+= eeh_pseries.o
 obj-$(CONFIG_KEXEC)	+= kexec.o
 obj-$(CONFIG_PCI)	+= pci.o pci_dlpar.o
 obj-$(CONFIG_PSERIES_MSI)	+= msi.o
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
deleted file mode 100644
index 6b73d6c..0000000
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ /dev/null
@@ -1,942 +0,0 @@ 
-/*
- * Copyright IBM Corporation 2001, 2005, 2006
- * Copyright Dave Engebretsen & Todd Inglett 2001
- * Copyright Linas Vepstas 2005, 2006
- * Copyright 2001-2012 IBM Corporation.
- *
- * 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
- *
- * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
- */
-
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/rbtree.h>
-#include <linux/seq_file.h>
-#include <linux/spinlock.h>
-#include <linux/export.h>
-#include <linux/of.h>
-
-#include <linux/atomic.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>
-
-
-/** Overview:
- *  EEH, or "Extended Error Handling" is a PCI bridge technology for
- *  dealing with PCI bus errors that can't be dealt with within the
- *  usual PCI framework, except by check-stopping the CPU.  Systems
- *  that are designed for high-availability/reliability cannot afford
- *  to crash due to a "mere" PCI error, thus the need for EEH.
- *  An EEH-capable bridge operates by converting a detected error
- *  into a "slot freeze", taking the PCI adapter off-line, making
- *  the slot behave, from the OS'es point of view, as if the slot
- *  were "empty": all reads return 0xff's and all writes are silently
- *  ignored.  EEH slot isolation events can be triggered by parity
- *  errors on the address or data busses (e.g. during posted writes),
- *  which in turn might be caused by low voltage on the bus, dust,
- *  vibration, humidity, radioactivity or plain-old failed hardware.
- *
- *  Note, however, that one of the leading causes of EEH slot
- *  freeze events are buggy device drivers, buggy device microcode,
- *  or buggy device hardware.  This is because any attempt by the
- *  device to bus-master data to a memory address that is not
- *  assigned to the device will trigger a slot freeze.   (The idea
- *  is to prevent devices-gone-wild from corrupting system memory).
- *  Buggy hardware/drivers will have a miserable time co-existing
- *  with EEH.
- *
- *  Ideally, a PCI device driver, when suspecting that an isolation
- *  event has occurred (e.g. by reading 0xff's), will then ask EEH
- *  whether this is the case, and then take appropriate steps to
- *  reset the PCI slot, the PCI device, and then resume operations.
- *  However, until that day,  the checking is done here, with the
- *  eeh_check_failure() routine embedded in the MMIO macros.  If
- *  the slot is found to be isolated, an "EEH Event" is synthesized
- *  and sent out for processing.
- */
-
-/* If a device driver keeps reading an MMIO register in an interrupt
- * handler after a slot isolation event, it might be broken.
- * This sets the threshold for how many read attempts we allow
- * before printing an error message.
- */
-#define EEH_MAX_FAILS	2100000
-
-/* Time to wait for a PCI slot to report status, in milliseconds */
-#define PCI_BUS_RESET_WAIT_MSEC (60*1000)
-
-/* Platform dependent EEH operations */
-struct eeh_ops *eeh_ops = NULL;
-
-int eeh_subsystem_enabled;
-EXPORT_SYMBOL(eeh_subsystem_enabled);
-
-/*
- * EEH probe mode support. The intention is to support multiple
- * platforms for EEH. Some platforms like pSeries do PCI emunation
- * based on device tree. However, other platforms like powernv probe
- * PCI devices from hardware. The flag is used to distinguish that.
- * In addition, struct eeh_ops::probe would be invoked for particular
- * OF node or PCI device so that the corresponding PE would be created
- * there.
- */
-int eeh_probe_mode;
-
-/* Global EEH mutex */
-DEFINE_MUTEX(eeh_mutex);
-
-/* Lock to avoid races due to multiple reports of an error */
-static DEFINE_RAW_SPINLOCK(confirm_error_lock);
-
-/* Buffer for reporting pci register dumps. Its here in BSS, and
- * not dynamically alloced, so that it ends up in RMO where RTAS
- * can access it.
- */
-#define EEH_PCI_REGS_LOG_LEN 4096
-static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN];
-
-/*
- * The struct is used to maintain the EEH global statistic
- * information. Besides, the EEH global statistics will be
- * exported to user space through procfs
- */
-struct eeh_stats {
-	u64 no_device;		/* PCI device not found		*/
-	u64 no_dn;		/* OF node not found		*/
-	u64 no_cfg_addr;	/* Config address not found	*/
-	u64 ignored_check;	/* EEH check skipped		*/
-	u64 total_mmio_ffs;	/* Total EEH checks		*/
-	u64 false_positives;	/* Unnecessary EEH checks	*/
-	u64 slot_resets;	/* PE reset			*/
-};
-
-static struct eeh_stats eeh_stats;
-
-#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
-
-/**
- * eeh_gather_pci_data - Copy assorted PCI config space registers to buff
- * @edev: device to report data for
- * @buf: point to buffer in which to log
- * @len: amount of room in buffer
- *
- * This routine captures assorted PCI configuration space data,
- * and puts them into a buffer for RTAS error logging.
- */
-static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
-{
-	struct device_node *dn = eeh_dev_to_of_node(edev);
-	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
-	u32 cfg;
-	int cap, i;
-	int n = 0;
-
-	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name);
-	printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name);
-
-	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg);
-	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);
-	printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg);
-
-	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg);
-	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
-	printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
-
-	if (!dev) {
-		printk(KERN_WARNING "EEH: no PCI device for this of node\n");
-		return n;
-	}
-
-	/* Gather bridge-specific registers */
-	if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
-		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
-		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
-		printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
-
-		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg);
-		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
-		printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
-	}
-
-	/* Dump out the PCI-X command and status regs */
-	cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
-	if (cap) {
-		eeh_ops->read_config(dn, cap, 4, &cfg);
-		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
-		printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg);
-
-		eeh_ops->read_config(dn, cap+4, 4, &cfg);
-		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);
-		printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg);
-	}
-
-	/* If PCI-E capable, dump PCI-E cap 10, and the AER */
-	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
-	if (cap) {
-		n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
-		printk(KERN_WARNING
-		       "EEH: PCI-E capabilities and status follow:\n");
-
-		for (i=0; i<=8; i++) {
-			eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
-			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
-			printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
-		}
-
-		cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-		if (cap) {
-			n += scnprintf(buf+n, len-n, "pci-e AER:\n");
-			printk(KERN_WARNING
-			       "EEH: PCI-E AER capability register set follows:\n");
-
-			for (i=0; i<14; i++) {
-				eeh_ops->read_config(dn, cap+4*i, 4, &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);
-			}
-		}
-	}
-
-	return n;
-}
-
-/**
- * eeh_slot_error_detail - Generate combined log including driver log and error log
- * @pe: EEH PE
- * @severity: temporary or permanent error log
- *
- * This routine should be called to generate the combined log, which
- * is comprised of driver log and error log. The driver log is figured
- * out from the config space of the corresponding PCI device, while
- * the error log is fetched through platform dependent function call.
- */
-void eeh_slot_error_detail(struct eeh_pe *pe, int severity)
-{
-	size_t loglen = 0;
-	struct eeh_dev *edev;
-
-	eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
-	eeh_ops->configure_bridge(pe);
-	eeh_pe_restore_bars(pe);
-
-	pci_regs_buf[0] = 0;
-	eeh_pe_for_each_dev(pe, edev) {
-		loglen += eeh_gather_pci_data(edev, pci_regs_buf,
-				EEH_PCI_REGS_LOG_LEN);
-        }
-
-	eeh_ops->get_log(pe, severity, pci_regs_buf, loglen);
-}
-
-/**
- * eeh_token_to_phys - Convert EEH address token to phys address
- * @token: I/O token, should be address in the form 0xA....
- *
- * This routine should be called to convert virtual I/O address
- * to physical one.
- */
-static inline unsigned long eeh_token_to_phys(unsigned long token)
-{
-	pte_t *ptep;
-	unsigned long pa;
-
-	ptep = find_linux_pte(init_mm.pgd, token);
-	if (!ptep)
-		return token;
-	pa = pte_pfn(*ptep) << PAGE_SHIFT;
-
-	return pa | (token & (PAGE_SIZE-1));
-}
-
-/**
- * eeh_dev_check_failure - Check if all 1's data is due to EEH slot freeze
- * @edev: eeh device
- *
- * Check for an EEH failure for the given device node.  Call this
- * routine if the result of a read was all 0xff's and you want to
- * find out if this is due to an EEH slot freeze.  This routine
- * will query firmware for the EEH status.
- *
- * Returns 0 if there has not been an EEH error; otherwise returns
- * a non-zero value and queues up a slot isolation event notification.
- *
- * It is safe to call this routine in an interrupt context.
- */
-int eeh_dev_check_failure(struct eeh_dev *edev)
-{
-	int ret;
-	unsigned long flags;
-	struct device_node *dn;
-	struct pci_dev *dev;
-	struct eeh_pe *pe;
-	int rc = 0;
-	const char *location;
-
-	eeh_stats.total_mmio_ffs++;
-
-	if (!eeh_subsystem_enabled)
-		return 0;
-
-	if (!edev) {
-		eeh_stats.no_dn++;
-		return 0;
-	}
-	dn = eeh_dev_to_of_node(edev);
-	dev = eeh_dev_to_pci_dev(edev);
-	pe = edev->pe;
-
-	/* Access to IO BARs might get this far and still not want checking. */
-	if (!pe) {
-		eeh_stats.ignored_check++;
-		pr_debug("EEH: Ignored check for %s %s\n",
-			eeh_pci_name(dev), dn->full_name);
-		return 0;
-	}
-
-	if (!pe->addr && !pe->config_addr) {
-		eeh_stats.no_cfg_addr++;
-		return 0;
-	}
-
-	/* If we already have a pending isolation event for this
-	 * slot, we know it's bad already, we don't need to check.
-	 * Do this checking under a lock; as multiple PCI devices
-	 * in one slot might report errors simultaneously, and we
-	 * only want one error recovery routine running.
-	 */
-	raw_spin_lock_irqsave(&confirm_error_lock, flags);
-	rc = 1;
-	if (pe->state & EEH_PE_ISOLATED) {
-		pe->check_count++;
-		if (pe->check_count % EEH_MAX_FAILS == 0) {
-			location = of_get_property(dn, "ibm,loc-code", NULL);
-			printk(KERN_ERR "EEH: %d reads ignored for recovering device at "
-				"location=%s driver=%s pci addr=%s\n",
-				pe->check_count, location,
-				eeh_driver_name(dev), eeh_pci_name(dev));
-			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",
-				eeh_driver_name(dev));
-			dump_stack();
-		}
-		goto dn_unlock;
-	}
-
-	/*
-	 * Now test for an EEH failure.  This is VERY expensive.
-	 * Note that the eeh_config_addr may be a parent device
-	 * in the case of a device behind a bridge, or it may be
-	 * function zero of a multi-function device.
-	 * In any case they must share a common PHB.
-	 */
-	ret = eeh_ops->get_state(pe, NULL);
-
-	/* Note that config-io to empty slots may fail;
-	 * they are empty when they don't have children.
-	 * We will punt with the following conditions: Failure to get
-	 * PE's state, EEH not support and Permanently unavailable
-	 * state, PE is in good state.
-	 */
-	if ((ret < 0) ||
-	    (ret == EEH_STATE_NOT_SUPPORT) ||
-	    (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
-	    (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
-		eeh_stats.false_positives++;
-		pe->false_positives++;
-		rc = 0;
-		goto dn_unlock;
-	}
-
-	eeh_stats.slot_resets++;
- 
-	/* Avoid repeated reports of this failure, including problems
-	 * with other functions on this device, and functions under
-	 * bridges.
-	 */
-	eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
-	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
-
-	eeh_send_failure_event(pe);
-
-	/* Most EEH events are due to device driver bugs.  Having
-	 * a stack trace will help the device-driver authors figure
-	 * out what happened.  So print that out.
-	 */
-	WARN(1, "EEH: failure detected\n");
-	return 1;
-
-dn_unlock:
-	raw_spin_unlock_irqrestore(&confirm_error_lock, flags);
-	return rc;
-}
-
-EXPORT_SYMBOL_GPL(eeh_dev_check_failure);
-
-/**
- * eeh_check_failure - Check if all 1's data is due to EEH slot freeze
- * @token: I/O token, should be address in the form 0xA....
- * @val: value, should be all 1's (XXX why do we need this arg??)
- *
- * Check for an EEH failure at the given token address.  Call this
- * routine if the result of a read was all 0xff's and you want to
- * find out if this is due to an EEH slot freeze event.  This routine
- * will query firmware for the EEH status.
- *
- * Note this routine is safe to call in an interrupt context.
- */
-unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val)
-{
-	unsigned long addr;
-	struct eeh_dev *edev;
-
-	/* Finding the phys addr + pci device; this is pretty quick. */
-	addr = eeh_token_to_phys((unsigned long __force) token);
-	edev = eeh_addr_cache_get_dev(addr);
-	if (!edev) {
-		eeh_stats.no_device++;
-		return val;
-	}
-
-	eeh_dev_check_failure(edev);
-
-	pci_dev_put(eeh_dev_to_pci_dev(edev));
-	return val;
-}
-
-EXPORT_SYMBOL(eeh_check_failure);
-
-
-/**
- * eeh_pci_enable - Enable MMIO or DMA transfers for this slot
- * @pe: EEH PE
- *
- * 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_pe *pe, int function)
-{
-	int rc;
-
-	rc = eeh_ops->set_option(pe, function);
-	if (rc)
-		pr_warning("%s: Unexpected state change %d on PHB#%d-PE#%x, err=%d\n",
-			__func__, function, pe->phb->global_number, pe->addr, rc);
-
-	rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
-	if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) &&
-	   (function == EEH_OPT_THAW_MMIO))
-		return 0;
-
-	return rc;
-}
-
-/**
- * pcibios_set_pcie_slot_reset - Set PCI-E reset state
- * @dev: pci device struct
- * @state: reset state to enter
- *
- * Return value:
- * 	0 if success
- */
-int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
-{
-	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
-	struct eeh_pe *pe = edev->pe;
-
-	if (!pe) {
-		pr_err("%s: No PE found on PCI device %s\n",
-			__func__, pci_name(dev));
-		return -EINVAL;
-	}
-
-	switch (state) {
-	case pcie_deassert_reset:
-		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
-		break;
-	case pcie_hot_reset:
-		eeh_ops->reset(pe, EEH_RESET_HOT);
-		break;
-	case pcie_warm_reset:
-		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
-		break;
-	default:
-		return -EINVAL;
-	};
-
-	return 0;
-}
-
-/**
- * eeh_set_pe_freset - Check the required reset for the indicated device
- * @data: EEH device
- * @flag: 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.
- */
-static void *eeh_set_dev_freset(void *data, void *flag)
-{
-	struct pci_dev *dev;
-	unsigned int *freset = (unsigned int *)flag;
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-
-	dev = eeh_dev_to_pci_dev(edev);
-	if (dev)
-		*freset |= dev->needs_freset;
-
-	return NULL;
-}
-
-/**
- * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second
- * @pe: EEH PE
- *
- * Assert the PCI #RST line for 1/4 second.
- */
-static void eeh_reset_pe_once(struct eeh_pe *pe)
-{
-	unsigned int freset = 0;
-
-	/* Determine type of EEH reset required for
-	 * Partitionable Endpoint, a hot-reset (1)
-	 * or a fundamental reset (3).
-	 * A fundamental reset required by any device under
-	 * Partitionable Endpoint trumps hot-reset.
-  	 */
-	eeh_pe_dev_traverse(pe, eeh_set_dev_freset, &freset);
-
-	if (freset)
-		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);
-	else
-		eeh_ops->reset(pe, EEH_RESET_HOT);
-
-	/* The PCI bus requires that the reset be held high for at least
-	 * a 100 milliseconds. We wait a bit longer 'just in case'.
-	 */
-#define PCI_BUS_RST_HOLD_TIME_MSEC 250
-	msleep(PCI_BUS_RST_HOLD_TIME_MSEC);
-	
-	/* We might get hit with another EEH freeze as soon as the 
-	 * pci slot reset line is dropped. Make sure we don't miss
-	 * these, and clear the flag now.
-	 */
-	eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
-
-	eeh_ops->reset(pe, EEH_RESET_DEACTIVATE);
-
-	/* 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
-	 * up traffic.
-	 */
-#define PCI_BUS_SETTLE_TIME_MSEC 1800
-	msleep(PCI_BUS_SETTLE_TIME_MSEC);
-}
-
-/**
- * eeh_reset_pe - Reset the indicated PE
- * @pe: EEH PE
- *
- * 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_pe *pe)
-{
-	int i, rc;
-
-	/* Take three shots at resetting the bus */
-	for (i=0; i<3; i++) {
-		eeh_reset_pe_once(pe);
-
-		rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
-		if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE))
-			return 0;
-
-		if (rc < 0) {
-			pr_err("%s: Unrecoverable slot failure on PHB#%d-PE#%x",
-				__func__, pe->phb->global_number, pe->addr);
-			return -1;
-		}
-		pr_err("EEH: bus reset %d failed on PHB#%d-PE#%x, rc=%d\n",
-			i+1, pe->phb->global_number, pe->addr, rc);
-	}
-
-	return -1;
-}
-
-/**
- * eeh_save_bars - Save device bars
- * @edev: PCI device associated EEH device
- *
- * Save the values of the device bars. Unlike the restore
- * routine, this routine is *not* recursive. This is because
- * PCI devices are added individually; but, for the restore,
- * an entire slot is reset at a time.
- */
-void eeh_save_bars(struct eeh_dev *edev)
-{
-	int i;
-	struct device_node *dn;
-
-	if (!edev)
-		return;
-	dn = eeh_dev_to_of_node(edev);
-	
-	for (i = 0; i < 16; i++)
-		eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]);
-}
-
-/**
- * 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.
- * As a side effect we can determine here if eeh is supported at all.
- * Note that we leave EEH on so failed config cycles won't cause a machine
- * check.  If a user turns off EEH for a particular adapter they are really
- * telling Linux to ignore errors.  Some hardware (e.g. POWER5) won't
- * grant access to a slot if EEH isn't enabled, and so we always enable
- * EEH for all slots/all devices.
- *
- * The eeh-force-off option disables EEH checking globally, for all slots.
- * Even if force-off is set, the EEH hardware is still enabled, so that
- * newer systems can boot.
- */
-static int __init eeh_init(void)
-{
-	struct pci_controller *hose, *tmp;
-	struct device_node *phb;
-	int ret;
-
-	/* call platform initialization function */
-	if (!eeh_ops) {
-		pr_warning("%s: Platform EEH operation not found\n",
-			__func__);
-		return -EEXIST;
-	} else if ((ret = eeh_ops->init())) {
-		pr_warning("%s: Failed to call platform init function (%d)\n",
-			__func__, ret);
-		return ret;
-	}
-
-	raw_spin_lock_init(&confirm_error_lock);
-
-	/* Enable EEH for all adapters */
-	if (eeh_probe_mode_devtree()) {
-		list_for_each_entry_safe(hose, tmp,
-			&hose_list, list_node) {
-			phb = hose->dn;
-			traverse_pci_devices(phb, eeh_ops->of_probe, NULL);
-		}
-	}
-
-	if (eeh_subsystem_enabled)
-		pr_info("EEH: PCI Enhanced I/O Error Handling Enabled\n");
-	else
-		pr_warning("EEH: No capable adapters found\n");
-
-	return ret;
-}
-
-core_initcall_sync(eeh_init);
-
-/**
- * eeh_add_device_early - Enable EEH for the indicated device_node
- * @dn: device node for which to set up EEH
- *
- * This routine must be used to perform EEH initialization for PCI
- * devices that were added after system boot (e.g. hotplug, dlpar).
- * This routine must be called before any i/o is performed to the
- * adapter (inluding any config-space i/o).
- * Whether this actually enables EEH or not for this device depends
- * on the CEC architecture, type of the device, on earlier boot
- * command-line arguments & etc.
- */
-static void eeh_add_device_early(struct device_node *dn)
-{
-	struct pci_controller *phb;
-
-	if (!of_node_to_eeh_dev(dn))
-		return;
-	phb = of_node_to_eeh_dev(dn)->phb;
-
-	/* USB Bus children of PCI devices will not have BUID's */
-	if (NULL == phb || 0 == phb->buid)
-		return;
-
-	/* FIXME: hotplug support on POWERNV */
-	eeh_ops->of_probe(dn, NULL);
-}
-
-/**
- * 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)
-{
-	struct device_node *sib;
-
-	for_each_child_of_node(dn, sib)
-		eeh_add_device_tree_early(sib);
-	eeh_add_device_early(dn);
-}
-EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
-
-/**
- * eeh_add_device_late - Perform EEH initialization for the indicated pci device
- * @dev: pci device for which to set up EEH
- *
- * This routine must be used to complete EEH initialization for PCI
- * devices that were added after system boot (e.g. hotplug, dlpar).
- */
-static void eeh_add_device_late(struct pci_dev *dev)
-{
-	struct device_node *dn;
-	struct eeh_dev *edev;
-
-	if (!dev || !eeh_subsystem_enabled)
-		return;
-
-	pr_debug("EEH: Adding device %s\n", pci_name(dev));
-
-	dn = pci_device_to_OF_node(dev);
-	edev = of_node_to_eeh_dev(dn);
-	if (edev->pdev == dev) {
-		pr_debug("EEH: Already referenced !\n");
-		return;
-	}
-	WARN_ON(edev->pdev);
-
-	pci_dev_get(dev);
-	edev->pdev = dev;
-	dev->dev.archdata.edev = edev;
-
-	eeh_addr_cache_insert_dev(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)
-{
-	struct pci_dev *dev;
-
-	list_for_each_entry(dev, &bus->devices, bus_list) {
- 		eeh_add_device_late(dev);
- 		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
- 			struct pci_bus *subbus = dev->subordinate;
- 			if (subbus)
- 				eeh_add_device_tree_late(subbus);
- 		}
-	}
-}
-EXPORT_SYMBOL_GPL(eeh_add_device_tree_late);
-
-/**
- * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus
- * @bus: PCI bus
- *
- * This routine must be used to add EEH sysfs files for PCI
- * devices which are attached to the indicated PCI bus. The PCI bus
- * is added after system boot through hotplug or dlpar.
- */
-void eeh_add_sysfs_files(struct pci_bus *bus)
-{
-	struct pci_dev *dev;
-
-	list_for_each_entry(dev, &bus->devices, bus_list) {
-		eeh_sysfs_add_device(dev);
-		if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-			struct pci_bus *subbus = dev->subordinate;
-			if (subbus)
-				eeh_add_sysfs_files(subbus);
-		}
-	}
-}
-EXPORT_SYMBOL_GPL(eeh_add_sysfs_files);
-
-/**
- * eeh_remove_device - Undo EEH setup for the indicated pci device
- * @dev: pci device to be removed
- * @purge_pe: remove the PE or not
- *
- * This routine should be called when a device is removed from
- * a running system (e.g. by hotplug or dlpar).  It unregisters
- * the PCI device from the EEH subsystem.  I/O errors affecting
- * this device will no longer be detected after this call; thus,
- * i/o errors affecting this slot may leave this device unusable.
- */
-static void eeh_remove_device(struct pci_dev *dev, int purge_pe)
-{
-	struct eeh_dev *edev;
-
-	if (!dev || !eeh_subsystem_enabled)
-		return;
-	edev = pci_dev_to_eeh_dev(dev);
-
-	/* Unregister the device with the EEH/PCI address search system */
-	pr_debug("EEH: Removing device %s\n", pci_name(dev));
-
-	if (!edev || !edev->pdev) {
-		pr_debug("EEH: Not referenced !\n");
-		return;
-	}
-	edev->pdev = NULL;
-	dev->dev.archdata.edev = NULL;
-	pci_dev_put(dev);
-
-	eeh_rmv_from_parent_pe(edev, purge_pe);
-	eeh_addr_cache_rmv_dev(dev);
-	eeh_sysfs_remove_device(dev);
-}
-
-/**
- * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device
- * @dev: PCI device
- * @purge_pe: remove the corresponding PE or not
- *
- * 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, int purge_pe)
-{
-	struct pci_bus *bus = dev->subordinate;
-	struct pci_dev *child, *tmp;
-
-	eeh_remove_device(dev, purge_pe);
-
-	if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
-		list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
-			 eeh_remove_bus_device(child, purge_pe);
-	}
-}
-EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
-
-static int proc_eeh_show(struct seq_file *m, void *v)
-{
-	if (0 == eeh_subsystem_enabled) {
-		seq_printf(m, "EEH Subsystem is globally disabled\n");
-		seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs);
-	} else {
-		seq_printf(m, "EEH Subsystem is enabled\n");
-		seq_printf(m,
-				"no device=%llu\n"
-				"no device node=%llu\n"
-				"no config address=%llu\n"
-				"check not wanted=%llu\n"
-				"eeh_total_mmio_ffs=%llu\n"
-				"eeh_false_positives=%llu\n"
-				"eeh_slot_resets=%llu\n",
-				eeh_stats.no_device,
-				eeh_stats.no_dn,
-				eeh_stats.no_cfg_addr,
-				eeh_stats.ignored_check,
-				eeh_stats.total_mmio_ffs,
-				eeh_stats.false_positives,
-				eeh_stats.slot_resets);
-	}
-
-	return 0;
-}
-
-static int proc_eeh_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, proc_eeh_show, NULL);
-}
-
-static const struct file_operations proc_eeh_operations = {
-	.open      = proc_eeh_open,
-	.read      = seq_read,
-	.llseek    = seq_lseek,
-	.release   = single_release,
-};
-
-static int __init eeh_init_proc(void)
-{
-	if (machine_is(pseries))
-		proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations);
-	return 0;
-}
-__initcall(eeh_init_proc);
diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c
deleted file mode 100644
index 5a4c879..0000000
--- a/arch/powerpc/platforms/pseries/eeh_cache.c
+++ /dev/null
@@ -1,319 +0,0 @@ 
-/*
- * PCI address cache; allows the lookup of PCI devices based on I/O address
- *
- * Copyright IBM Corporation 2004
- * Copyright Linas Vepstas <linas@austin.ibm.com> 2004
- *
- * 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/list.h>
-#include <linux/pci.h>
-#include <linux/rbtree.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/atomic.h>
-#include <asm/pci-bridge.h>
-#include <asm/ppc-pci.h>
-
-
-/**
- * The pci address cache subsystem.  This subsystem places
- * PCI device address resources into a red-black tree, sorted
- * according to the address range, so that given only an i/o
- * address, the corresponding PCI device can be **quickly**
- * found. It is safe to perform an address lookup in an interrupt
- * context; this ability is an important feature.
- *
- * Currently, the only customer of this code is the EEH subsystem;
- * thus, this code has been somewhat tailored to suit EEH better.
- * In particular, the cache does *not* hold the addresses of devices
- * for which EEH is not enabled.
- *
- * (Implementation Note: The RB tree seems to be better/faster
- * than any hash algo I could think of for this problem, even
- * with the penalty of slow pointer chases for d-cache misses).
- */
-struct pci_io_addr_range {
-	struct rb_node rb_node;
-	unsigned long addr_lo;
-	unsigned long addr_hi;
-	struct eeh_dev *edev;
-	struct pci_dev *pcidev;
-	unsigned int flags;
-};
-
-static struct pci_io_addr_cache {
-	struct rb_root rb_root;
-	spinlock_t piar_lock;
-} pci_io_addr_cache_root;
-
-static inline struct eeh_dev *__eeh_addr_cache_get_device(unsigned long addr)
-{
-	struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node;
-
-	while (n) {
-		struct pci_io_addr_range *piar;
-		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
-
-		if (addr < piar->addr_lo) {
-			n = n->rb_left;
-		} else {
-			if (addr > piar->addr_hi) {
-				n = n->rb_right;
-			} else {
-				pci_dev_get(piar->pcidev);
-				return piar->edev;
-			}
-		}
-	}
-
-	return NULL;
-}
-
-/**
- * eeh_addr_cache_get_dev - Get device, given only address
- * @addr: mmio (PIO) phys address or i/o port number
- *
- * Given an mmio phys address, or a port number, find a pci device
- * that implements this address.  Be sure to pci_dev_put the device
- * when finished.  I/O port numbers are assumed to be offset
- * from zero (that is, they do *not* have pci_io_addr added in).
- * It is safe to call this function within an interrupt.
- */
-struct eeh_dev *eeh_addr_cache_get_dev(unsigned long addr)
-{
-	struct eeh_dev *edev;
-	unsigned long flags;
-
-	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
-	edev = __eeh_addr_cache_get_device(addr);
-	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
-	return edev;
-}
-
-#ifdef DEBUG
-/*
- * Handy-dandy debug print routine, does nothing more
- * than print out the contents of our addr cache.
- */
-static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
-{
-	struct rb_node *n;
-	int cnt = 0;
-
-	n = rb_first(&cache->rb_root);
-	while (n) {
-		struct pci_io_addr_range *piar;
-		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
-		pr_debug("PCI: %s addr range %d [%lx-%lx]: %s\n",
-		       (piar->flags & IORESOURCE_IO) ? "i/o" : "mem", cnt,
-		       piar->addr_lo, piar->addr_hi, pci_name(piar->pcidev));
-		cnt++;
-		n = rb_next(n);
-	}
-}
-#endif
-
-/* Insert address range into the rb tree. */
-static struct pci_io_addr_range *
-eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
-		      unsigned long ahi, unsigned int flags)
-{
-	struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
-	struct rb_node *parent = NULL;
-	struct pci_io_addr_range *piar;
-
-	/* Walk tree, find a place to insert into tree */
-	while (*p) {
-		parent = *p;
-		piar = rb_entry(parent, struct pci_io_addr_range, rb_node);
-		if (ahi < piar->addr_lo) {
-			p = &parent->rb_left;
-		} else if (alo > piar->addr_hi) {
-			p = &parent->rb_right;
-		} else {
-			if (dev != piar->pcidev ||
-			    alo != piar->addr_lo || ahi != piar->addr_hi) {
-				pr_warning("PIAR: overlapping address range\n");
-			}
-			return piar;
-		}
-	}
-	piar = kzalloc(sizeof(struct pci_io_addr_range), GFP_ATOMIC);
-	if (!piar)
-		return NULL;
-
-	pci_dev_get(dev);
-	piar->addr_lo = alo;
-	piar->addr_hi = ahi;
-	piar->edev = pci_dev_to_eeh_dev(dev);
-	piar->pcidev = dev;
-	piar->flags = flags;
-
-#ifdef DEBUG
-	pr_debug("PIAR: insert range=[%lx:%lx] dev=%s\n",
-	                  alo, ahi, pci_name(dev));
-#endif
-
-	rb_link_node(&piar->rb_node, parent, p);
-	rb_insert_color(&piar->rb_node, &pci_io_addr_cache_root.rb_root);
-
-	return piar;
-}
-
-static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
-{
-	struct device_node *dn;
-	struct eeh_dev *edev;
-	int i;
-
-	dn = pci_device_to_OF_node(dev);
-	if (!dn) {
-		pr_warning("PCI: no pci dn found for dev=%s\n", pci_name(dev));
-		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. */
-	if (!edev->pe) {
-#ifdef DEBUG
-		pr_info("PCI: skip building address cache for=%s - %s\n",
-			pci_name(dev), dn->full_name);
-#endif
-		return;
-	}
-
-	/* Walk resources on this device, poke them into the tree */
-	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
-		unsigned long start = pci_resource_start(dev,i);
-		unsigned long end = pci_resource_end(dev,i);
-		unsigned int flags = pci_resource_flags(dev,i);
-
-		/* We are interested only bus addresses, not dma or other stuff */
-		if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
-			continue;
-		if (start == 0 || ~start == 0 || end == 0 || ~end == 0)
-			 continue;
-		eeh_addr_cache_insert(dev, start, end, flags);
-	}
-}
-
-/**
- * eeh_addr_cache_insert_dev - Add a device to the address cache
- * @dev: PCI device whose I/O addresses we are interested in.
- *
- * In order to support the fast lookup of devices based on addresses,
- * we maintain a cache of devices that can be quickly searched.
- * This routine adds a device to that cache.
- */
-void eeh_addr_cache_insert_dev(struct pci_dev *dev)
-{
-	unsigned long flags;
-
-	/* Ignore PCI bridges */
-	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
-		return;
-
-	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
-	__eeh_addr_cache_insert_dev(dev);
-	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
-}
-
-static inline void __eeh_addr_cache_rmv_dev(struct pci_dev *dev)
-{
-	struct rb_node *n;
-
-restart:
-	n = rb_first(&pci_io_addr_cache_root.rb_root);
-	while (n) {
-		struct pci_io_addr_range *piar;
-		piar = rb_entry(n, struct pci_io_addr_range, rb_node);
-
-		if (piar->pcidev == dev) {
-			rb_erase(n, &pci_io_addr_cache_root.rb_root);
-			pci_dev_put(piar->pcidev);
-			kfree(piar);
-			goto restart;
-		}
-		n = rb_next(n);
-	}
-}
-
-/**
- * eeh_addr_cache_rmv_dev - remove pci device from addr cache
- * @dev: device to remove
- *
- * Remove a device from the addr-cache tree.
- * This is potentially expensive, since it will walk
- * the tree multiple times (once per resource).
- * But so what; device removal doesn't need to be that fast.
- */
-void eeh_addr_cache_rmv_dev(struct pci_dev *dev)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags);
-	__eeh_addr_cache_rmv_dev(dev);
-	spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags);
-}
-
-/**
- * eeh_addr_cache_build - Build a cache of I/O addresses
- *
- * Build a cache of pci i/o addresses.  This cache will be used to
- * find the pci device that corresponds to a given address.
- * This routine scans all pci busses to build the cache.
- * Must be run late in boot process, after the pci controllers
- * have been scanned for devices (after all device resources are known).
- */
-void __init eeh_addr_cache_build(void)
-{
-	struct device_node *dn;
-	struct eeh_dev *edev;
-	struct pci_dev *dev = NULL;
-
-	spin_lock_init(&pci_io_addr_cache_root.piar_lock);
-
-	for_each_pci_dev(dev) {
-		eeh_addr_cache_insert_dev(dev);
-
-		dn = pci_device_to_OF_node(dev);
-		if (!dn)
-			continue;
-
-		edev = of_node_to_eeh_dev(dn);
-		if (!edev)
-			continue;
-
-		pci_dev_get(dev);  /* matching put is in eeh_remove_device() */
-		dev->dev.archdata.edev = edev;
-		edev->pdev = dev;
-
-		eeh_sysfs_add_device(dev);
-	}
-
-#ifdef DEBUG
-	/* Verify tree built up above, echo back the list of addrs. */
-	eeh_addr_cache_print(&pci_io_addr_cache_root);
-#endif
-}
-
diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c
deleted file mode 100644
index 1efa28f..0000000
--- a/arch/powerpc/platforms/pseries/eeh_dev.c
+++ /dev/null
@@ -1,112 +0,0 @@ 
-/*
- * 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 *eeh_dev_init(struct device_node *dn, void *data)
-{
-	struct pci_controller *phb = data;
-	struct eeh_dev *edev;
-
-	/* Allocate EEH device */
-	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
-	if (!edev) {
-		pr_warning("%s: out of memory\n", __func__);
-		return NULL;
-	}
-
-	/* Associate EEH device with OF node */
-	PCI_DN(dn)->edev = edev;
-	edev->dn  = dn;
-	edev->phb = phb;
-	INIT_LIST_HEAD(&edev->list);
-
-	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 eeh_dev_phb_init_dynamic(struct pci_controller *phb)
-{
-	struct device_node *dn = phb->dn;
-
-	/* EEH PE for PHB */
-	eeh_phb_pe_create(phb);
-
-	/* 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
- */
-static int __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);
-
-	pr_info("EEH: devices created\n");
-
-	return 0;
-}
-
-core_initcall(eeh_dev_phb_init);
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c
deleted file mode 100644
index a3fefb6..0000000
--- a/arch/powerpc/platforms/pseries/eeh_driver.c
+++ /dev/null
@@ -1,552 +0,0 @@ 
-/*
- * PCI Error Recovery Driver for RPA-compliant PPC64 platform.
- * Copyright IBM Corp. 2004 2005
- * Copyright Linas Vepstas <linas@linas.org> 2004, 2005
- *
- * All rights reserved.
- *
- * 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, GOOD TITLE or
- * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
- */
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <asm/eeh.h>
-#include <asm/eeh_event.h>
-#include <asm/ppc-pci.h>
-#include <asm/pci-bridge.h>
-#include <asm/prom.h>
-#include <asm/rtas.h>
-
-/**
- * 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)
-		return pdev->dev.driver->name;
-	return "";
-}
-
-/**
- * eeh_pcid_get - Get the PCI device driver
- * @pdev: PCI device
- *
- * The function is used to retrieve the PCI device driver for
- * the indicated PCI device. Besides, we will increase the reference
- * of the PCI device driver to prevent that being unloaded on
- * the fly. Otherwise, kernel crash would be seen.
- */
-static inline struct pci_driver *eeh_pcid_get(struct pci_dev *pdev)
-{
-	if (!pdev || !pdev->driver)
-		return NULL;
-
-	if (!try_module_get(pdev->driver->driver.owner))
-		return NULL;
-
-	return pdev->driver;
-}
-
-/**
- * eeh_pcid_put - Dereference on the PCI device driver
- * @pdev: PCI device
- *
- * The function is called to do dereference on the PCI device
- * driver of the indicated PCI device.
- */
-static inline void eeh_pcid_put(struct pci_dev *pdev)
-{
-	if (!pdev || !pdev->driver)
-		return;
-
-	module_put(pdev->driver->driver.owner);
-}
-
-#if 0
-static void print_device_node_tree(struct pci_dn *pdn, int dent)
-{
-	int i;
-	struct device_node *pc;
-
-	if (!pdn)
-		return;
-	for (i = 0; i < dent; i++)
-		printk(" ");
-	printk("dn=%s mode=%x \tcfg_addr=%x pe_addr=%x \tfull=%s\n",
-		pdn->node->name, pdn->eeh_mode, pdn->eeh_config_addr,
-		pdn->eeh_pe_config_addr, pdn->node->full_name);
-	dent += 3;
-	pc = pdn->node->child;
-	while (pc) {
-		print_device_node_tree(PCI_DN(pc), dent);
-		pc = pc->sibling;
-	}
-}
-#endif
-
-/**
- * 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)
-{
-	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
-
-	/* Don't disable MSI and MSI-X interrupts. They are
-	 * effectively disabled by the DMA Stopped state
-	 * when an EEH error occurs.
-	 */
-	if (dev->msi_enabled || dev->msix_enabled)
-		return;
-
-	if (!irq_has_action(dev->irq))
-		return;
-
-	edev->mode |= EEH_DEV_IRQ_DISABLED;
-	disable_irq_nosync(dev->irq);
-}
-
-/**
- * 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)
-{
-	struct eeh_dev *edev = pci_dev_to_eeh_dev(dev);
-
-	if ((edev->mode) & EEH_DEV_IRQ_DISABLED) {
-		edev->mode &= ~EEH_DEV_IRQ_DISABLED;
-		enable_irq(dev->irq);
-	}
-}
-
-/**
- * eeh_report_error - Report pci error to each device driver
- * @data: eeh device
- * @userdata: return value
- * 
- * Report an EEH error to each device driver, collect up and 
- * merge the device driver responses. Cumulative response 
- * passed back in "userdata".
- */
-static void *eeh_report_error(void *data, void *userdata)
-{
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
-	enum pci_ers_result rc, *res = userdata;
-	struct pci_driver *driver;
-
-	/* We might not have the associated PCI device,
-	 * then we should continue for next one.
-	 */
-	if (!dev) return NULL;
-	dev->error_state = pci_channel_io_frozen;
-
-	driver = eeh_pcid_get(dev);
-	if (!driver) return NULL;
-
-	eeh_disable_irq(dev);
-
-	if (!driver->err_handler ||
-	    !driver->err_handler->error_detected) {
-		eeh_pcid_put(dev);
-		return NULL;
-	}
-
-	rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen);
-
-	/* A driver that needs a reset trumps all others */
-	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
-	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
-
-	eeh_pcid_put(dev);
-	return NULL;
-}
-
-/**
- * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled
- * @data: eeh device
- * @userdata: return value
- *
- * Tells each device driver that IO ports, MMIO and config space I/O
- * are now enabled. Collects up and merges the device driver responses.
- * Cumulative response passed back in "userdata".
- */
-static void *eeh_report_mmio_enabled(void *data, void *userdata)
-{
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
-	enum pci_ers_result rc, *res = userdata;
-	struct pci_driver *driver;
-
-	driver = eeh_pcid_get(dev);
-	if (!driver) return NULL;
-
-	if (!driver->err_handler ||
-	    !driver->err_handler->mmio_enabled) {
-		eeh_pcid_put(dev);
-		return NULL;
-	}
-
-	rc = driver->err_handler->mmio_enabled(dev);
-
-	/* A driver that needs a reset trumps all others */
-	if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
-	if (*res == PCI_ERS_RESULT_NONE) *res = rc;
-
-	eeh_pcid_put(dev);
-	return NULL;
-}
-
-/**
- * eeh_report_reset - Tell device that slot has been reset
- * @data: eeh 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 void *eeh_report_reset(void *data, void *userdata)
-{
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
-	enum pci_ers_result rc, *res = userdata;
-	struct pci_driver *driver;
-
-	if (!dev) return NULL;
-	dev->error_state = pci_channel_io_normal;
-
-	driver = eeh_pcid_get(dev);
-	if (!driver) return NULL;
-
-	eeh_enable_irq(dev);
-
-	if (!driver->err_handler ||
-	    !driver->err_handler->slot_reset) {
-		eeh_pcid_put(dev);
-		return NULL;
-	}
-
-	rc = driver->err_handler->slot_reset(dev);
-	if ((*res == PCI_ERS_RESULT_NONE) ||
-	    (*res == PCI_ERS_RESULT_RECOVERED)) *res = rc;
-	if (*res == PCI_ERS_RESULT_DISCONNECT &&
-	     rc == PCI_ERS_RESULT_NEED_RESET) *res = rc;
-
-	eeh_pcid_put(dev);
-	return NULL;
-}
-
-/**
- * eeh_report_resume - Tell device to resume normal operations
- * @data: eeh 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 void *eeh_report_resume(void *data, void *userdata)
-{
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
-	struct pci_driver *driver;
-
-	if (!dev) return NULL;
-	dev->error_state = pci_channel_io_normal;
-
-	driver = eeh_pcid_get(dev);
-	if (!driver) return NULL;
-
-	eeh_enable_irq(dev);
-
-	if (!driver->err_handler ||
-	    !driver->err_handler->resume) {
-		eeh_pcid_put(dev);
-		return NULL;
-	}
-
-	driver->err_handler->resume(dev);
-
-	eeh_pcid_put(dev);
-	return NULL;
-}
-
-/**
- * eeh_report_failure - Tell device driver that device is dead.
- * @data: eeh device
- * @userdata: return value
- *
- * This informs the device driver that the device is permanently
- * dead, and that no further recovery attempts will be made on it.
- */
-static void *eeh_report_failure(void *data, void *userdata)
-{
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-	struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
-	struct pci_driver *driver;
-
-	if (!dev) return NULL;
-	dev->error_state = pci_channel_io_perm_failure;
-
-	driver = eeh_pcid_get(dev);
-	if (!driver) return NULL;
-
-	eeh_disable_irq(dev);
-
-	if (!driver->err_handler ||
-	    !driver->err_handler->error_detected) {
-		eeh_pcid_put(dev);
-		return NULL;
-	}
-
-	driver->err_handler->error_detected(dev, pci_channel_io_perm_failure);
-
-	eeh_pcid_put(dev);
-	return NULL;
-}
-
-/**
- * eeh_reset_device - Perform actual reset of a pci slot
- * @pe: EEH PE
- * @bus: PCI bus corresponding to the isolcated slot
- *
- * This routine must be called to do reset on the indicated PE.
- * During the reset, udev might be invoked because those affected
- * PCI devices will be removed and then added.
- */
-static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
-{
-	int cnt, rc;
-
-	/* pcibios will clear the counter; save the value */
-	cnt = pe->freeze_count;
-
-	/*
-	 * We don't remove the corresponding PE instances because
-	 * we need the information afterwords. The attached EEH
-	 * devices are expected to be attached soon when calling
-	 * into pcibios_add_pci_devices().
-	 */
-	if (bus)
-		__pcibios_remove_pci_devices(bus, 0);
-
-	/* Reset the pci controller. (Asserts RST#; resets config space).
-	 * Reconfigure bridges and devices. Don't try to bring the system
-	 * up if the reset failed for some reason.
-	 */
-	rc = eeh_reset_pe(pe);
-	if (rc)
-		return rc;
-
-	/* Restore PE */
-	eeh_ops->configure_bridge(pe);
-	eeh_pe_restore_bars(pe);
-
-	/* Give the system 5 seconds to finish running the user-space
-	 * hotplug shutdown scripts, e.g. ifdown for ethernet.  Yes, 
-	 * this is a hack, but if we don't do this, and try to bring 
-	 * the device up before the scripts have taken it down, 
-	 * potentially weird things happen.
-	 */
-	if (bus) {
-		ssleep(5);
-		pcibios_add_pci_devices(bus);
-	}
-	pe->freeze_count = cnt;
-
-	return 0;
-}
-
-/* The longest amount of time to wait for a pci device
- * to come back on line, in seconds.
- */
-#define MAX_WAIT_FOR_RECOVERY 150
-
-/**
- * eeh_handle_event - Reset a PCI device after hard lockup.
- * @pe: EEH PE
- *
- * 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).
- */
-void eeh_handle_event(struct eeh_pe *pe)
-{
-	struct pci_bus *frozen_bus;
-	int rc = 0;
-	enum pci_ers_result result = PCI_ERS_RESULT_NONE;
-
-	frozen_bus = eeh_pe_bus_get(pe);
-	if (!frozen_bus) {
-		pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
-			__func__, pe->phb->global_number, pe->addr);
-		return;
-	}
-
-	pe->freeze_count++;
-	if (pe->freeze_count > EEH_MAX_ALLOWED_FREEZES)
-		goto excess_failures;
-	pr_warning("EEH: This PCI device has failed %d times in the last hour\n",
-		pe->freeze_count);
-
-	/* Walk the various device drivers attached to this slot through
-	 * a reset sequence, giving each an opportunity to do what it needs
-	 * to accomplish the reset.  Each child gets a report of the
-	 * status ... if any child can't handle the reset, then the entire
-	 * slot is dlpar removed and added.
-	 */
-	eeh_pe_dev_traverse(pe, eeh_report_error, &result);
-
-	/* Get the current PCI slot state. This can take a long time,
-	 * sometimes over 3 seconds for certain systems.
-	 */
-	rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
-	if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
-		printk(KERN_WARNING "EEH: Permanent failure\n");
-		goto hard_fail;
-	}
-
-	/* Since rtas may enable MMIO when posting the error log,
-	 * don't post the error log until after all dev drivers
-	 * have been informed.
-	 */
-	eeh_slot_error_detail(pe, EEH_LOG_TEMP);
-
-	/* If all device drivers were EEH-unaware, then shut
-	 * down all of the device drivers, and hope they
-	 * go down willingly, without panicing the system.
-	 */
-	if (result == PCI_ERS_RESULT_NONE) {
-		rc = eeh_reset_device(pe, frozen_bus);
-		if (rc) {
-			printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc);
-			goto hard_fail;
-		}
-	}
-
-	/* If all devices reported they can proceed, then re-enable MMIO */
-	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
-		rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO);
-
-		if (rc < 0)
-			goto hard_fail;
-		if (rc) {
-			result = PCI_ERS_RESULT_NEED_RESET;
-		} else {
-			result = PCI_ERS_RESULT_NONE;
-			eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result);
-		}
-	}
-
-	/* If all devices reported they can proceed, then re-enable DMA */
-	if (result == PCI_ERS_RESULT_CAN_RECOVER) {
-		rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA);
-
-		if (rc < 0)
-			goto hard_fail;
-		if (rc)
-			result = PCI_ERS_RESULT_NEED_RESET;
-		else
-			result = PCI_ERS_RESULT_RECOVERED;
-	}
-
-	/* If any device has a hard failure, then shut off everything. */
-	if (result == PCI_ERS_RESULT_DISCONNECT) {
-		printk(KERN_WARNING "EEH: Device driver gave up\n");
-		goto hard_fail;
-	}
-
-	/* If any device called out for a reset, then reset the slot */
-	if (result == PCI_ERS_RESULT_NEED_RESET) {
-		rc = eeh_reset_device(pe, NULL);
-		if (rc) {
-			printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc);
-			goto hard_fail;
-		}
-		result = PCI_ERS_RESULT_NONE;
-		eeh_pe_dev_traverse(pe, eeh_report_reset, &result);
-	}
-
-	/* All devices should claim they have recovered by now. */
-	if ((result != PCI_ERS_RESULT_RECOVERED) &&
-	    (result != PCI_ERS_RESULT_NONE)) {
-		printk(KERN_WARNING "EEH: Not recovered\n");
-		goto hard_fail;
-	}
-
-	/* Tell all device drivers that they can resume operations */
-	eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
-
-	return;
-	
-excess_failures:
-	/*
-	 * About 90% of all real-life EEH failures in the field
-	 * are due to poorly seated PCI cards. Only 10% or so are
-	 * due to actual, failed cards.
-	 */
-	pr_err("EEH: PHB#%d-PE#%x has failed %d times in the\n"
-	       "last hour and has been permanently disabled.\n"
-	       "Please try reseating or replacing it.\n",
-		pe->phb->global_number, pe->addr,
-		pe->freeze_count);
-	goto perm_error;
-
-hard_fail:
-	pr_err("EEH: Unable to recover from failure from PHB#%d-PE#%x.\n"
-	       "Please try reseating or replacing it\n",
-		pe->phb->global_number, pe->addr);
-
-perm_error:
-	eeh_slot_error_detail(pe, EEH_LOG_PERM);
-
-	/* Notify all devices that they're about to go down. */
-	eeh_pe_dev_traverse(pe, eeh_report_failure, NULL);
-
-	/* Shut down the device drivers for good. */
-	if (frozen_bus)
-		pcibios_remove_pci_devices(frozen_bus);
-}
-
diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c
deleted file mode 100644
index 185bedd..0000000
--- a/arch/powerpc/platforms/pseries/eeh_event.c
+++ /dev/null
@@ -1,142 +0,0 @@ 
-/*
- * 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
- *
- * Copyright (c) 2005 Linas Vepstas <linas@linas.org>
- */
-
-#include <linux/delay.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/sched.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <linux/kthread.h>
-#include <asm/eeh_event.h>
-#include <asm/ppc-pci.h>
-
-/** Overview:
- *  EEH error states may be detected within exception handlers;
- *  however, the recovery processing needs to occur asynchronously
- *  in a normal kernel context and not an interrupt context.
- *  This pair of routines creates an event and queues it onto a
- *  work-queue, where a worker thread can drive recovery.
- */
-
-/* EEH event workqueue setup. */
-static DEFINE_SPINLOCK(eeh_eventlist_lock);
-LIST_HEAD(eeh_eventlist);
-static void eeh_thread_launcher(struct work_struct *);
-DECLARE_WORK(eeh_event_wq, eeh_thread_launcher);
-
-/* Serialize reset sequences for a given pci device */
-DEFINE_MUTEX(eeh_event_mutex);
-
-/**
- * eeh_event_handler - Dispatch EEH events.
- * @dummy - unused
- *
- * The detection of a frozen slot can occur inside an interrupt,
- * where it can be hard to do anything about it.  The goal of this
- * routine is to pull these detection events out of the context
- * of the interrupt handler, and re-dispatch them for processing
- * at a later time in a normal context.
- */
-static int eeh_event_handler(void * dummy)
-{
-	unsigned long flags;
-	struct eeh_event *event;
-	struct eeh_pe *pe;
-
-	spin_lock_irqsave(&eeh_eventlist_lock, flags);
-	event = NULL;
-
-	/* Unqueue the event, get ready to process. */
-	if (!list_empty(&eeh_eventlist)) {
-		event = list_entry(eeh_eventlist.next, struct eeh_event, list);
-		list_del(&event->list);
-	}
-	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
-
-	if (event == NULL)
-		return 0;
-
-	/* Serialize processing of EEH events */
-	mutex_lock(&eeh_event_mutex);
-	pe = event->pe;
-	eeh_pe_state_mark(pe, EEH_PE_RECOVERING);
-	pr_info("EEH: Detected PCI bus error on PHB#%d-PE#%x\n",
-		pe->phb->global_number, pe->addr);
-
-	set_current_state(TASK_INTERRUPTIBLE);	/* Don't add to load average */
-	eeh_handle_event(pe);
-	eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
-
-	kfree(event);
-	mutex_unlock(&eeh_event_mutex);
-
-	/* If there are no new errors after an hour, clear the counter. */
-	if (pe && pe->freeze_count > 0) {
-		msleep_interruptible(3600*1000);
-		if (pe->freeze_count > 0)
-			pe->freeze_count--;
-
-	}
-
-	return 0;
-}
-
-/**
- * eeh_thread_launcher - Start kernel thread to handle EEH events
- * @dummy - unused
- *
- * This routine is called to start the kernel thread for processing
- * EEH event.
- */
-static void eeh_thread_launcher(struct work_struct *dummy)
-{
-	if (IS_ERR(kthread_run(eeh_event_handler, NULL, "eehd")))
-		printk(KERN_ERR "Failed to start EEH daemon\n");
-}
-
-/**
- * eeh_send_failure_event - Generate a PCI error event
- * @pe: EEH PE
- *
- * 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 eeh_pe *pe)
-{
-	unsigned long flags;
-	struct eeh_event *event;
-
-	event = kzalloc(sizeof(*event), GFP_ATOMIC);
-	if (!event) {
-		pr_err("EEH: out of memory, event not handled\n");
-		return -ENOMEM;
-	}
-	event->pe = pe;
-
-	/* We may or may not be called in an interrupt context */
-	spin_lock_irqsave(&eeh_eventlist_lock, flags);
-	list_add(&event->list, &eeh_eventlist);
-	spin_unlock_irqrestore(&eeh_eventlist_lock, flags);
-
-	schedule_work(&eeh_event_wq);
-
-	return 0;
-}
diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c
deleted file mode 100644
index 9d4a9e8..0000000
--- a/arch/powerpc/platforms/pseries/eeh_pe.c
+++ /dev/null
@@ -1,653 +0,0 @@ 
-/*
- * The file intends to implement PE based on the information from
- * platforms. Basically, there have 3 types of PEs: PHB/Bus/Device.
- * All the PEs should be organized as hierarchy tree. The first level
- * of the tree will be associated to existing PHBs since the particular
- * PE is only meaningful in one PHB domain.
- *
- * 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>
-
-static LIST_HEAD(eeh_phb_pe);
-
-/**
- * eeh_pe_alloc - Allocate PE
- * @phb: PCI controller
- * @type: PE type
- *
- * Allocate PE instance dynamically.
- */
-static struct eeh_pe *eeh_pe_alloc(struct pci_controller *phb, int type)
-{
-	struct eeh_pe *pe;
-
-	/* Allocate PHB PE */
-	pe = kzalloc(sizeof(struct eeh_pe), GFP_KERNEL);
-	if (!pe) return NULL;
-
-	/* Initialize PHB PE */
-	pe->type = type;
-	pe->phb = phb;
-	INIT_LIST_HEAD(&pe->child_list);
-	INIT_LIST_HEAD(&pe->child);
-	INIT_LIST_HEAD(&pe->edevs);
-
-	return pe;
-}
-
-/**
- * eeh_phb_pe_create - Create PHB PE
- * @phb: PCI controller
- *
- * The function should be called while the PHB is detected during
- * system boot or PCI hotplug in order to create PHB PE.
- */
-int eeh_phb_pe_create(struct pci_controller *phb)
-{
-	struct eeh_pe *pe;
-
-	/* Allocate PHB PE */
-	pe = eeh_pe_alloc(phb, EEH_PE_PHB);
-	if (!pe) {
-		pr_err("%s: out of memory!\n", __func__);
-		return -ENOMEM;
-	}
-
-	/* Put it into the list */
-	eeh_lock();
-	list_add_tail(&pe->child, &eeh_phb_pe);
-	eeh_unlock();
-
-	pr_debug("EEH: Add PE for PHB#%d\n", phb->global_number);
-
-	return 0;
-}
-
-/**
- * eeh_phb_pe_get - Retrieve PHB PE based on the given PHB
- * @phb: PCI controller
- *
- * The overall PEs form hierarchy tree. The first layer of the
- * hierarchy tree is composed of PHB PEs. The function is used
- * to retrieve the corresponding PHB PE according to the given PHB.
- */
-static struct eeh_pe *eeh_phb_pe_get(struct pci_controller *phb)
-{
-	struct eeh_pe *pe;
-
-	list_for_each_entry(pe, &eeh_phb_pe, child) {
-		/*
-		 * Actually, we needn't check the type since
-		 * the PE for PHB has been determined when that
-		 * was created.
-		 */
-		if ((pe->type & EEH_PE_PHB) && pe->phb == phb)
-			return pe;
-	}
-
-	return NULL;
-}
-
-/**
- * eeh_pe_next - Retrieve the next PE in the tree
- * @pe: current PE
- * @root: root PE
- *
- * The function is used to retrieve the next PE in the
- * hierarchy PE tree.
- */
-static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe,
-				  struct eeh_pe *root)
-{
-	struct list_head *next = pe->child_list.next;
-
-	if (next == &pe->child_list) {
-		while (1) {
-			if (pe == root)
-				return NULL;
-			next = pe->child.next;
-			if (next != &pe->parent->child_list)
-				break;
-			pe = pe->parent;
-		}
-	}
-
-	return list_entry(next, struct eeh_pe, child);
-}
-
-/**
- * eeh_pe_traverse - Traverse PEs in the specified PHB
- * @root: root PE
- * @fn: callback
- * @flag: extra parameter to callback
- *
- * The function is used to traverse the specified PE and its
- * child PEs. The traversing is to be terminated once the
- * callback returns something other than NULL, or no more PEs
- * to be traversed.
- */
-static void *eeh_pe_traverse(struct eeh_pe *root,
-			eeh_traverse_func fn, void *flag)
-{
-	struct eeh_pe *pe;
-	void *ret;
-
-	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
-		ret = fn(pe, flag);
-		if (ret) return ret;
-	}
-
-	return NULL;
-}
-
-/**
- * eeh_pe_dev_traverse - Traverse the devices from the PE
- * @root: EEH PE
- * @fn: function callback
- * @flag: extra parameter to callback
- *
- * The function is used to traverse the devices of the specified
- * PE and its child PEs.
- */
-void *eeh_pe_dev_traverse(struct eeh_pe *root,
-		eeh_traverse_func fn, void *flag)
-{
-	struct eeh_pe *pe;
-	struct eeh_dev *edev;
-	void *ret;
-
-	if (!root) {
-		pr_warning("%s: Invalid PE %p\n", __func__, root);
-		return NULL;
-	}
-
-	eeh_lock();
-
-	/* Traverse root PE */
-	for (pe = root; pe; pe = eeh_pe_next(pe, root)) {
-		eeh_pe_for_each_dev(pe, edev) {
-			ret = fn(edev, flag);
-			if (ret) {
-				eeh_unlock();
-				return ret;
-			}
-		}
-	}
-
-	eeh_unlock();
-
-	return NULL;
-}
-
-/**
- * __eeh_pe_get - Check the PE address
- * @data: EEH PE
- * @flag: EEH device
- *
- * For one particular PE, it can be identified by PE address
- * or tranditional BDF address. BDF address is composed of
- * Bus/Device/Function number. The extra data referred by flag
- * indicates which type of address should be used.
- */
-static void *__eeh_pe_get(void *data, void *flag)
-{
-	struct eeh_pe *pe = (struct eeh_pe *)data;
-	struct eeh_dev *edev = (struct eeh_dev *)flag;
-
-	/* Unexpected PHB PE */
-	if (pe->type & EEH_PE_PHB)
-		return NULL;
-
-	/* We prefer PE address */
-	if (edev->pe_config_addr &&
-	   (edev->pe_config_addr == pe->addr))
-		return pe;
-
-	/* Try BDF address */
-	if (edev->pe_config_addr &&
-	   (edev->config_addr == pe->config_addr))
-		return pe;
-
-	return NULL;
-}
-
-/**
- * eeh_pe_get - Search PE based on the given address
- * @edev: EEH device
- *
- * Search the corresponding PE based on the specified address which
- * is included in the eeh device. The function is used to check if
- * the associated PE has been created against the PE address. It's
- * notable that the PE address has 2 format: traditional PE address
- * which is composed of PCI bus/device/function number, or unified
- * PE address.
- */
-static struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)
-{
-	struct eeh_pe *root = eeh_phb_pe_get(edev->phb);
-	struct eeh_pe *pe;
-
-	pe = eeh_pe_traverse(root, __eeh_pe_get, edev);
-
-	return pe;
-}
-
-/**
- * eeh_pe_get_parent - Retrieve the parent PE
- * @edev: EEH device
- *
- * The whole PEs existing in the system are organized as hierarchy
- * tree. The function is used to retrieve the parent PE according
- * to the parent EEH device.
- */
-static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)
-{
-	struct device_node *dn;
-	struct eeh_dev *parent;
-
-	/*
-	 * It might have the case for the indirect parent
-	 * EEH device already having associated PE, but
-	 * the direct parent EEH device doesn't have yet.
-	 */
-	dn = edev->dn->parent;
-	while (dn) {
-		/* We're poking out of PCI territory */
-		if (!PCI_DN(dn)) return NULL;
-
-		parent = of_node_to_eeh_dev(dn);
-		/* We're poking out of PCI territory */
-		if (!parent) return NULL;
-
-		if (parent->pe)
-			return parent->pe;
-
-		dn = dn->parent;
-	}
-
-	return NULL;
-}
-
-/**
- * eeh_add_to_parent_pe - Add EEH device to parent PE
- * @edev: EEH device
- *
- * Add EEH device to the parent PE. If the parent PE already
- * exists, the PE type will be changed to EEH_PE_BUS. Otherwise,
- * we have to create new PE to hold the EEH device and the new
- * PE will be linked to its parent PE as well.
- */
-int eeh_add_to_parent_pe(struct eeh_dev *edev)
-{
-	struct eeh_pe *pe, *parent;
-
-	eeh_lock();
-
-	/*
-	 * Search the PE has been existing or not according
-	 * to the PE address. If that has been existing, the
-	 * PE should be composed of PCI bus and its subordinate
-	 * components.
-	 */
-	pe = eeh_pe_get(edev);
-	if (pe && !(pe->type & EEH_PE_INVALID)) {
-		if (!edev->pe_config_addr) {
-			eeh_unlock();
-			pr_err("%s: PE with addr 0x%x already exists\n",
-				__func__, edev->config_addr);
-			return -EEXIST;
-		}
-
-		/* Mark the PE as type of PCI bus */
-		pe->type = EEH_PE_BUS;
-		edev->pe = pe;
-
-		/* Put the edev to PE */
-		list_add_tail(&edev->list, &pe->edevs);
-		eeh_unlock();
-		pr_debug("EEH: Add %s to Bus PE#%x\n",
-			edev->dn->full_name, pe->addr);
-
-		return 0;
-	} else if (pe && (pe->type & EEH_PE_INVALID)) {
-		list_add_tail(&edev->list, &pe->edevs);
-		edev->pe = pe;
-		/*
-		 * We're running to here because of PCI hotplug caused by
-		 * EEH recovery. We need clear EEH_PE_INVALID until the top.
-		 */
-		parent = pe;
-		while (parent) {
-			if (!(parent->type & EEH_PE_INVALID))
-				break;
-			parent->type &= ~EEH_PE_INVALID;
-			parent = parent->parent;
-		}
-		eeh_unlock();
-		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
-			edev->dn->full_name, pe->addr, pe->parent->addr);
-
-		return 0;
-	}
-
-	/* Create a new EEH PE */
-	pe = eeh_pe_alloc(edev->phb, EEH_PE_DEVICE);
-	if (!pe) {
-		eeh_unlock();
-		pr_err("%s: out of memory!\n", __func__);
-		return -ENOMEM;
-	}
-	pe->addr	= edev->pe_config_addr;
-	pe->config_addr	= edev->config_addr;
-
-	/*
-	 * Put the new EEH PE into hierarchy tree. If the parent
-	 * can't be found, the newly created PE will be attached
-	 * to PHB directly. Otherwise, we have to associate the
-	 * PE with its parent.
-	 */
-	parent = eeh_pe_get_parent(edev);
-	if (!parent) {
-		parent = eeh_phb_pe_get(edev->phb);
-		if (!parent) {
-			eeh_unlock();
-			pr_err("%s: No PHB PE is found (PHB Domain=%d)\n",
-				__func__, edev->phb->global_number);
-			edev->pe = NULL;
-			kfree(pe);
-			return -EEXIST;
-		}
-	}
-	pe->parent = parent;
-
-	/*
-	 * Put the newly created PE into the child list and
-	 * link the EEH device accordingly.
-	 */
-	list_add_tail(&pe->child, &parent->child_list);
-	list_add_tail(&edev->list, &pe->edevs);
-	edev->pe = pe;
-	eeh_unlock();
-	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n",
-		edev->dn->full_name, pe->addr, pe->parent->addr);
-
-	return 0;
-}
-
-/**
- * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE
- * @edev: EEH device
- * @purge_pe: remove PE or not
- *
- * The PE hierarchy tree might be changed when doing PCI hotplug.
- * Also, the PCI devices or buses could be removed from the system
- * during EEH recovery. So we have to call the function remove the
- * corresponding PE accordingly if necessary.
- */
-int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe)
-{
-	struct eeh_pe *pe, *parent, *child;
-	int cnt;
-
-	if (!edev->pe) {
-		pr_warning("%s: No PE found for EEH device %s\n",
-			__func__, edev->dn->full_name);
-		return -EEXIST;
-	}
-
-	eeh_lock();
-
-	/* Remove the EEH device */
-	pe = edev->pe;
-	edev->pe = NULL;
-	list_del(&edev->list);
-
-	/*
-	 * Check if the parent PE includes any EEH devices.
-	 * If not, we should delete that. Also, we should
-	 * delete the parent PE if it doesn't have associated
-	 * child PEs and EEH devices.
-	 */
-	while (1) {
-		parent = pe->parent;
-		if (pe->type & EEH_PE_PHB)
-			break;
-
-		if (purge_pe) {
-			if (list_empty(&pe->edevs) &&
-			    list_empty(&pe->child_list)) {
-				list_del(&pe->child);
-				kfree(pe);
-			} else {
-				break;
-			}
-		} else {
-			if (list_empty(&pe->edevs)) {
-				cnt = 0;
-				list_for_each_entry(child, &pe->child_list, child) {
-					if (!(child->type & EEH_PE_INVALID)) {
-						cnt++;
-						break;
-					}
-				}
-
-				if (!cnt)
-					pe->type |= EEH_PE_INVALID;
-				else
-					break;
-			}
-		}
-
-		pe = parent;
-	}
-
-	eeh_unlock();
-
-	return 0;
-}
-
-/**
- * __eeh_pe_state_mark - Mark the state for the PE
- * @data: EEH PE
- * @flag: state
- *
- * The function is used to mark the indicated state for the given
- * PE. Also, the associated PCI devices will be put into IO frozen
- * state as well.
- */
-static void *__eeh_pe_state_mark(void *data, void *flag)
-{
-	struct eeh_pe *pe = (struct eeh_pe *)data;
-	int state = *((int *)flag);
-	struct eeh_dev *tmp;
-	struct pci_dev *pdev;
-
-	/*
-	 * Mark the PE with the indicated state. Also,
-	 * the associated PCI device will be put into
-	 * I/O frozen state to avoid I/O accesses from
-	 * the PCI device driver.
-	 */
-	pe->state |= state;
-	eeh_pe_for_each_dev(pe, tmp) {
-		pdev = eeh_dev_to_pci_dev(tmp);
-		if (pdev)
-			pdev->error_state = pci_channel_io_frozen;
-	}
-
-	return NULL;
-}
-
-/**
- * eeh_pe_state_mark - Mark specified state for PE and its associated device
- * @pe: EEH PE
- *
- * EEH error affects the current PE and its child PEs. The function
- * is used to mark appropriate state for the affected PEs and the
- * associated devices.
- */
-void eeh_pe_state_mark(struct eeh_pe *pe, int state)
-{
-	eeh_lock();
-	eeh_pe_traverse(pe, __eeh_pe_state_mark, &state);
-	eeh_unlock();
-}
-
-/**
- * __eeh_pe_state_clear - Clear state for the PE
- * @data: EEH PE
- * @flag: state
- *
- * The function is used to clear the indicated state from the
- * given PE. Besides, we also clear the check count of the PE
- * as well.
- */
-static void *__eeh_pe_state_clear(void *data, void *flag)
-{
-	struct eeh_pe *pe = (struct eeh_pe *)data;
-	int state = *((int *)flag);
-
-	pe->state &= ~state;
-	pe->check_count = 0;
-
-	return NULL;
-}
-
-/**
- * eeh_pe_state_clear - Clear state for the PE and its children
- * @pe: PE
- * @state: state to be cleared
- *
- * When the PE and its children has been recovered from error,
- * we need clear the error state for that. The function is used
- * for the purpose.
- */
-void eeh_pe_state_clear(struct eeh_pe *pe, int state)
-{
-	eeh_lock();
-	eeh_pe_traverse(pe, __eeh_pe_state_clear, &state);
-	eeh_unlock();
-}
-
-/**
- * eeh_restore_one_device_bars - Restore the Base Address Registers for one device
- * @data: EEH device
- * @flag: Unused
- *
- * Loads the PCI configuration space base address registers,
- * the expansion ROM base address, the latency timer, and etc.
- * from the saved values in the device node.
- */
-static void *eeh_restore_one_device_bars(void *data, void *flag)
-{
-	int i;
-	u32 cmd;
-	struct eeh_dev *edev = (struct eeh_dev *)data;
-	struct device_node *dn = eeh_dev_to_of_node(edev);
-
-	for (i = 4; i < 10; i++)
-		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]);
-	/* 12 == Expansion ROM Address */
-	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]);
-
-#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF))
-#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)])
-
-	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1,
-		SAVED_BYTE(PCI_CACHE_LINE_SIZE));
-	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1,
-		SAVED_BYTE(PCI_LATENCY_TIMER));
-
-	/* max latency, min grant, interrupt pin and line */
-	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]);
-
-	/*
-	 * Restore PERR & SERR bits, some devices require it,
-	 * don't touch the other command bits
-	 */
-	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd);
-	if (edev->config_space[1] & PCI_COMMAND_PARITY)
-		cmd |= PCI_COMMAND_PARITY;
-	else
-		cmd &= ~PCI_COMMAND_PARITY;
-	if (edev->config_space[1] & PCI_COMMAND_SERR)
-		cmd |= PCI_COMMAND_SERR;
-	else
-		cmd &= ~PCI_COMMAND_SERR;
-	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd);
-
-	return NULL;
-}
-
-/**
- * eeh_pe_restore_bars - Restore the PCI config space info
- * @pe: EEH PE
- *
- * This routine performs a recursive walk to the children
- * of this device as well.
- */
-void eeh_pe_restore_bars(struct eeh_pe *pe)
-{
-	/*
-	 * We needn't take the EEH lock since eeh_pe_dev_traverse()
-	 * will take that.
-	 */
-	eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
-}
-
-/**
- * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
- * @pe: EEH PE
- *
- * Retrieve the PCI bus according to the given PE. Basically,
- * there're 3 types of PEs: PHB/Bus/Device. For PHB PE, the
- * primary PCI bus will be retrieved. The parent bus will be
- * returned for BUS PE. However, we don't have associated PCI
- * bus for DEVICE PE.
- */
-struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe)
-{
-	struct pci_bus *bus = NULL;
-	struct eeh_dev *edev;
-	struct pci_dev *pdev;
-
-	eeh_lock();
-
-	if (pe->type & EEH_PE_PHB) {
-		bus = pe->phb->bus;
-	} else if (pe->type & EEH_PE_BUS ||
-		   pe->type & EEH_PE_DEVICE) {
-		edev = list_first_entry(&pe->edevs, struct eeh_dev, list);
-		pdev = eeh_dev_to_pci_dev(edev);
-		if (pdev)
-			bus = pdev->bus;
-	}
-
-	eeh_unlock();
-
-	return bus;
-}
diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c
deleted file mode 100644
index d377083..0000000
--- a/arch/powerpc/platforms/pseries/eeh_sysfs.c
+++ /dev/null
@@ -1,75 +0,0 @@ 
-/*
- * Sysfs entries for PCI Error Recovery for PAPR-compliant platform.
- * Copyright IBM Corporation 2007
- * Copyright Linas Vepstas <linas@austin.ibm.com> 2007
- *
- * All rights reserved.
- *
- * 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, GOOD TITLE or
- * NON INFRINGEMENT.  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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Send comments and feedback to Linas Vepstas <linas@austin.ibm.com>
- */
-#include <linux/pci.h>
-#include <linux/stat.h>
-#include <asm/ppc-pci.h>
-#include <asm/pci-bridge.h>
-
-/**
- * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic
- * @_name: name of file in sysfs directory
- * @_memb: name of member in struct pci_dn to access
- * @_format: printf format for display
- *
- * All of the attributes look very similar, so just
- * auto-gen a cut-n-paste routine to display them.
- */
-#define EEH_SHOW_ATTR(_name,_memb,_format)               \
-static ssize_t eeh_show_##_name(struct device *dev,      \
-		struct device_attribute *attr, char *buf)          \
-{                                                        \
-	struct pci_dev *pdev = to_pci_dev(dev);               \
-	struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev);      \
-	                                                      \
-	if (!edev)                                            \
-		return 0;                                     \
-	                                                      \
-	return sprintf(buf, _format "\n", edev->_memb);       \
-}                                                        \
-static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL);
-
-EEH_SHOW_ATTR(eeh_mode,            mode,            "0x%x");
-EEH_SHOW_ATTR(eeh_config_addr,     config_addr,     "0x%x");
-EEH_SHOW_ATTR(eeh_pe_config_addr,  pe_config_addr,  "0x%x");
-
-void eeh_sysfs_add_device(struct pci_dev *pdev)
-{
-	int rc=0;
-
-	rc += device_create_file(&pdev->dev, &dev_attr_eeh_mode);
-	rc += device_create_file(&pdev->dev, &dev_attr_eeh_config_addr);
-	rc += device_create_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
-
-	if (rc)
-		printk(KERN_WARNING "EEH: Unable to create sysfs entries\n");
-}
-
-void eeh_sysfs_remove_device(struct pci_dev *pdev)
-{
-	device_remove_file(&pdev->dev, &dev_attr_eeh_mode);
-	device_remove_file(&pdev->dev, &dev_attr_eeh_config_addr);
-	device_remove_file(&pdev->dev, &dev_attr_eeh_pe_config_addr);
-}
-
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
index c91b22b..efe6137 100644
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
@@ -64,91 +64,6 @@  pcibios_find_pci_bus(struct device_node *dn)
 }
 EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
 
-/**
- * __pcibios_remove_pci_devices - remove all devices under this bus
- * @bus: the indicated PCI bus
- * @purge_pe: destroy the PE on removal of PCI devices
- *
- * Remove all of the PCI devices under this bus both from the
- * linux pci device tree, and from the powerpc EEH address cache.
- * By default, the corresponding PE will be destroied during the
- * normal PCI hotplug path. For PCI hotplug during EEH recovery,
- * the corresponding PE won't be destroied and deallocated.
- */
-void __pcibios_remove_pci_devices(struct pci_bus *bus, int purge_pe)
-{
-	struct pci_dev *dev, *tmp;
-	struct pci_bus *child_bus;
-
-	/* First go down child busses */
-	list_for_each_entry(child_bus, &bus->children, node)
-		__pcibios_remove_pci_devices(child_bus, purge_pe);
-
-	pr_debug("PCI: Removing devices on bus %04x:%02x\n",
-		pci_domain_nr(bus),  bus->number);
-	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
-		pr_debug("     * Removing %s...\n", pci_name(dev));
-		eeh_remove_bus_device(dev, purge_pe);
-		pci_stop_and_remove_bus_device(dev);
-	}
-}
-
-/**
- * pcibios_remove_pci_devices - remove all devices under this bus
- *
- * Remove all of the PCI devices under this bus both from the
- * linux pci device tree, and from the powerpc EEH address cache.
- */
-void pcibios_remove_pci_devices(struct pci_bus *bus)
-{
-	__pcibios_remove_pci_devices(bus, 1);
-}
-EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
-
-/**
- * pcibios_add_pci_devices - adds new pci devices to bus
- *
- * This routine will find and fixup new pci devices under
- * the indicated bus. This routine presumes that there
- * might already be some devices under this bridge, so
- * it carefully tries to add only new devices.  (And that
- * is how this routine differs from other, similar pcibios
- * routines.)
- */
-void pcibios_add_pci_devices(struct pci_bus * bus)
-{
-	int slotno, num, mode, pass, max;
-	struct pci_dev *dev;
-	struct device_node *dn = pci_bus_to_OF_node(bus);
-
-	eeh_add_device_tree_early(dn);
-
-	mode = PCI_PROBE_NORMAL;
-	if (ppc_md.pci_probe_mode)
-		mode = ppc_md.pci_probe_mode(bus);
-
-	if (mode == PCI_PROBE_DEVTREE) {
-		/* use ofdt-based probe */
-		of_rescan_bus(dn, bus);
-	} else if (mode == PCI_PROBE_NORMAL) {
-		/* use legacy probe */
-		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
-		num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
-		if (!num)
-			return;
-		pcibios_setup_bus_devices(bus);
-		max = bus->busn_res.start;
-		for (pass=0; pass < 2; pass++)
-			list_for_each_entry(dev, &bus->devices, bus_list) {
-			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
-			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
-				max = pci_scan_bridge(bus, dev, max, pass);
-		}
-	}
-	pcibios_finish_adding_to_bus(bus);
-}
-EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
-
 struct pci_controller *init_phb_dynamic(struct device_node *dn)
 {
 	struct pci_controller *phb;