diff options
| author | Michael Ellerman | 2015-04-07 05:24:55 +0200 | 
|---|---|---|
| committer | Michael Ellerman | 2015-04-07 05:24:55 +0200 | 
| commit | 428d4d6520a0b8683fe9eac6df3077001e13d00b (patch) | |
| tree | 8afa1af0babc8f2c375acc244aae969846dfe199 /arch/powerpc | |
| parent | Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/scottwoo... (diff) | |
| parent | powerpc/powernv: Don't map M64 segments using M32DT (diff) | |
| download | kernel-qcow2-linux-428d4d6520a0b8683fe9eac6df3077001e13d00b.tar.gz kernel-qcow2-linux-428d4d6520a0b8683fe9eac6df3077001e13d00b.tar.xz kernel-qcow2-linux-428d4d6520a0b8683fe9eac6df3077001e13d00b.zip | |
Merge branch 'next-eeh' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc into next
Diffstat (limited to 'arch/powerpc')
25 files changed, 1649 insertions, 1642 deletions
| diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 38faeded7d59..9f1371bab5fc 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -8,6 +8,9 @@  struct dma_map_ops;  struct device_node; +#ifdef CONFIG_PPC64 +struct pci_dn; +#endif  /*   * Arch extensions to struct device. @@ -34,6 +37,9 @@ struct dev_archdata {  #ifdef CONFIG_SWIOTLB  	dma_addr_t		max_direct_dma_addr;  #endif +#ifdef CONFIG_PPC64 +	struct pci_dn		*pci_data; +#endif  #ifdef CONFIG_EEH  	struct eeh_dev		*edev;  #endif diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 55abfd09e47f..a52db28ecc1e 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -29,7 +29,7 @@  struct pci_dev;  struct pci_bus; -struct device_node; +struct pci_dn;  #ifdef CONFIG_EEH @@ -136,14 +136,14 @@ struct eeh_dev {  	struct eeh_pe *pe;		/* Associated PE		*/  	struct list_head list;		/* Form link list in the PE	*/  	struct pci_controller *phb;	/* Associated PHB		*/ -	struct device_node *dn;		/* Associated device node	*/ +	struct pci_dn *pdn;		/* Associated PCI device node	*/  	struct pci_dev *pdev;		/* Associated PCI device	*/  	struct pci_bus *bus;		/* PCI bus for partial hotplug	*/  }; -static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev) +static inline struct pci_dn *eeh_dev_to_pdn(struct eeh_dev *edev)  { -	return edev ? edev->dn : NULL; +	return edev ? edev->pdn : NULL;  }  static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev) @@ -200,8 +200,7 @@ struct eeh_ops {  	char *name;  	int (*init)(void);  	int (*post_init)(void); -	void* (*of_probe)(struct device_node *dn, void *flag); -	int (*dev_probe)(struct pci_dev *dev, void *flag); +	void* (*probe)(struct pci_dn *pdn, void *data);  	int (*set_option)(struct eeh_pe *pe, int option);  	int (*get_pe_addr)(struct eeh_pe *pe);  	int (*get_state)(struct eeh_pe *pe, int *state); @@ -211,10 +210,10 @@ struct eeh_ops {  	int (*configure_bridge)(struct eeh_pe *pe);  	int (*err_inject)(struct eeh_pe *pe, int type, int func,  			  unsigned long addr, unsigned long mask); -	int (*read_config)(struct device_node *dn, int where, int size, u32 *val); -	int (*write_config)(struct device_node *dn, int where, int size, u32 val); +	int (*read_config)(struct pci_dn *pdn, int where, int size, u32 *val); +	int (*write_config)(struct pci_dn *pdn, int where, int size, u32 val);  	int (*next_error)(struct eeh_pe **pe); -	int (*restore_config)(struct device_node *dn); +	int (*restore_config)(struct pci_dn *pdn);  };  extern int eeh_subsystem_flags; @@ -272,7 +271,7 @@ void eeh_pe_restore_bars(struct eeh_pe *pe);  const char *eeh_pe_loc_get(struct eeh_pe *pe);  struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe); -void *eeh_dev_init(struct device_node *dn, void *data); +void *eeh_dev_init(struct pci_dn *pdn, void *data);  void eeh_dev_phb_init_dynamic(struct pci_controller *phb);  int eeh_init(void);  int __init eeh_ops_register(struct eeh_ops *ops); @@ -280,8 +279,8 @@ int __exit eeh_ops_unregister(const char *name);  int eeh_check_failure(const volatile void __iomem *token);  int eeh_dev_check_failure(struct eeh_dev *edev);  void eeh_addr_cache_build(void); -void eeh_add_device_early(struct device_node *); -void eeh_add_device_tree_early(struct device_node *); +void eeh_add_device_early(struct pci_dn *); +void eeh_add_device_tree_early(struct pci_dn *);  void eeh_add_device_late(struct pci_dev *);  void eeh_add_device_tree_late(struct pci_bus *);  void eeh_add_sysfs_files(struct pci_bus *); @@ -323,7 +322,7 @@ static inline int eeh_init(void)  	return 0;  } -static inline void *eeh_dev_init(struct device_node *dn, void *data) +static inline void *eeh_dev_init(struct pci_dn *pdn, void *data)  {  	return NULL;  } @@ -339,9 +338,9 @@ static inline int eeh_check_failure(const volatile void __iomem *token)  static inline void eeh_addr_cache_build(void) { } -static inline void eeh_add_device_early(struct device_node *dn) { } +static inline void eeh_add_device_early(struct pci_dn *pdn) { } -static inline void eeh_add_device_tree_early(struct device_node *dn) { } +static inline void eeh_add_device_tree_early(struct pci_dn *pdn) { }  static inline void eeh_add_device_late(struct pci_dev *dev) { } diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index c8175a3fe560..098d51e924ea 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -125,7 +125,7 @@ struct machdep_calls {  	unsigned int	(*get_irq)(void);  	/* PCI stuff */ -	/* Called after scanning the bus, before allocating resources */ +	/* Called after allocating resources */  	void		(*pcibios_fixup)(void);  	int		(*pci_probe_mode)(struct pci_bus *);  	void		(*pci_irq_fixup)(struct pci_dev *dev); diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 546d036fe925..2c6dc2a3d14a 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -89,6 +89,7 @@ struct pci_controller {  #ifdef CONFIG_PPC64  	unsigned long buid; +	struct pci_dn *pci_data;  #endif	/* CONFIG_PPC64 */  	void *private_data; @@ -154,9 +155,15 @@ static inline int isa_vaddr_is_ioport(void __iomem *address)  struct iommu_table;  struct pci_dn { +	int     flags; +  	int	busno;			/* pci bus number */  	int	devfn;			/* pci device and function number */ +	int	vendor_id;		/* Vendor ID */ +	int	device_id;		/* Device ID */ +	int	class_code;		/* Device class code */ +	struct  pci_dn *parent;  	struct  pci_controller *phb;	/* for pci devices */  	struct	iommu_table *iommu_table;	/* for phb's or bridges */  	struct	device_node *node;	/* back-pointer to the device_node */ @@ -171,14 +178,17 @@ struct pci_dn {  #ifdef CONFIG_PPC_POWERNV  	int	pe_number;  #endif +	struct list_head child_list; +	struct list_head list;  };  /* Get the pointer to a device_node's pci_dn */  #define PCI_DN(dn)	((struct pci_dn *) (dn)->data) +extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, +					   int devfn);  extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev); - -extern void * update_dn_pci_info(struct device_node *dn, void *data); +extern void *update_dn_pci_info(struct device_node *dn, void *data);  static inline int pci_device_from_OF_node(struct device_node *np,  					  u8 *bus, u8 *devfn) @@ -191,20 +201,12 @@ static inline int pci_device_from_OF_node(struct device_node *np,  }  #if defined(CONFIG_EEH) -static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn) +static inline struct eeh_dev *pdn_to_eeh_dev(struct pci_dn *pdn)  { -	/* -	 * For those OF nodes whose parent isn't PCI bridge, they -	 * don't have PCI_DN actually. So we have to skip them for -	 * any EEH operations. -	 */ -	if (!dn || !PCI_DN(dn)) -		return NULL; - -	return PCI_DN(dn)->edev; +	return pdn ? pdn->edev : NULL;  }  #else -#define of_node_to_eeh_dev(x) (NULL) +#define pdn_to_eeh_dev(x)	(NULL)  #endif  /** Find the bus corresponding to the indicated device node */ diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index db1e2b8eff3c..ade75238ceb5 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -33,9 +33,14 @@ extern struct pci_dev *isa_bridge_pcidev;	/* may be NULL if no ISA bus */  /* PCI device_node operations */  struct device_node; +struct pci_dn; +  typedef void *(*traverse_func)(struct device_node *me, void *data);  void *traverse_pci_devices(struct device_node *start, traverse_func pre,  		void *data); +void *traverse_pci_dn(struct pci_dn *root, +		      void *(*fn)(struct pci_dn *, void *), +		      void *data);  extern void pci_devs_phb_init(void);  extern void pci_devs_phb_init_dynamic(struct pci_controller *phb); diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 3b2252e7731b..76253eb146be 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -164,30 +164,34 @@ __setup("eeh=", eeh_setup);   */  static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)  { -	struct device_node *dn = eeh_dev_to_of_node(edev); +	struct pci_dn *pdn = eeh_dev_to_pdn(edev);  	u32 cfg;  	int cap, i;  	int n = 0, l = 0;  	char buffer[128]; -	n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); -	pr_warn("EEH: of node=%s\n", dn->full_name); +	n += scnprintf(buf+n, len-n, "%04x:%02x:%02x:%01x\n", +		       edev->phb->global_number, pdn->busno, +		       PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); +	pr_warn("EEH: of node=%04x:%02x:%02x:%01x\n", +		edev->phb->global_number, pdn->busno, +		PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn)); -	eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg); +	eeh_ops->read_config(pdn, PCI_VENDOR_ID, 4, &cfg);  	n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg);  	pr_warn("EEH: PCI device/vendor: %08x\n", cfg); -	eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg); +	eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cfg);  	n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);  	pr_warn("EEH: PCI cmd/status register: %08x\n", cfg);  	/* Gather bridge-specific registers */  	if (edev->mode & EEH_DEV_BRIDGE) { -		eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg); +		eeh_ops->read_config(pdn, PCI_SEC_STATUS, 2, &cfg);  		n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);  		pr_warn("EEH: Bridge secondary status: %04x\n", cfg); -		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg); +		eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg);  		n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);  		pr_warn("EEH: Bridge control: %04x\n", cfg);  	} @@ -195,11 +199,11 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)  	/* Dump out the PCI-X command and status regs */  	cap = edev->pcix_cap;  	if (cap) { -		eeh_ops->read_config(dn, cap, 4, &cfg); +		eeh_ops->read_config(pdn, cap, 4, &cfg);  		n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);  		pr_warn("EEH: PCI-X cmd: %08x\n", cfg); -		eeh_ops->read_config(dn, cap+4, 4, &cfg); +		eeh_ops->read_config(pdn, cap+4, 4, &cfg);  		n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg);  		pr_warn("EEH: PCI-X status: %08x\n", cfg);  	} @@ -211,7 +215,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)  		pr_warn("EEH: PCI-E capabilities and status follow:\n");  		for (i=0; i<=8; i++) { -			eeh_ops->read_config(dn, cap+4*i, 4, &cfg); +			eeh_ops->read_config(pdn, cap+4*i, 4, &cfg);  			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);  			if ((i % 4) == 0) { @@ -238,7 +242,7 @@ static size_t eeh_dump_dev_log(struct eeh_dev *edev, char *buf, size_t len)  		pr_warn("EEH: PCI-E AER capability register set follows:\n");  		for (i=0; i<=13; i++) { -			eeh_ops->read_config(dn, cap+4*i, 4, &cfg); +			eeh_ops->read_config(pdn, cap+4*i, 4, &cfg);  			n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);  			if ((i % 4) == 0) { @@ -414,11 +418,11 @@ int eeh_dev_check_failure(struct eeh_dev *edev)  	int ret;  	int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);  	unsigned long flags; -	struct device_node *dn; +	struct pci_dn *pdn;  	struct pci_dev *dev;  	struct eeh_pe *pe, *parent_pe, *phb_pe;  	int rc = 0; -	const char *location; +	const char *location = NULL;  	eeh_stats.total_mmio_ffs++; @@ -429,15 +433,14 @@ int eeh_dev_check_failure(struct eeh_dev *edev)  		eeh_stats.no_dn++;  		return 0;  	} -	dn = eeh_dev_to_of_node(edev);  	dev = eeh_dev_to_pci_dev(edev);  	pe = eeh_dev_to_pe(edev);  	/* 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); +		pr_debug("EEH: Ignored check for %s\n", +			eeh_pci_name(dev));  		return 0;  	} @@ -473,10 +476,13 @@ int eeh_dev_check_failure(struct eeh_dev *edev)  	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); +			pdn = eeh_dev_to_pdn(edev); +			if (pdn->node) +				location = of_get_property(pdn->node, "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, +				pe->check_count, +				location ? location : "unknown",  				eeh_driver_name(dev), eeh_pci_name(dev));  			printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n",  				eeh_driver_name(dev)); @@ -667,6 +673,55 @@ int eeh_pci_enable(struct eeh_pe *pe, int function)  	return rc;  } +static void *eeh_disable_and_save_dev_state(void *data, void *userdata) +{ +	struct eeh_dev *edev = data; +	struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); +	struct pci_dev *dev = userdata; + +	/* +	 * The caller should have disabled and saved the +	 * state for the specified device +	 */ +	if (!pdev || pdev == dev) +		return NULL; + +	/* Ensure we have D0 power state */ +	pci_set_power_state(pdev, PCI_D0); + +	/* Save device state */ +	pci_save_state(pdev); + +	/* +	 * Disable device to avoid any DMA traffic and +	 * interrupt from the device +	 */ +	pci_write_config_word(pdev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); + +	return NULL; +} + +static void *eeh_restore_dev_state(void *data, void *userdata) +{ +	struct eeh_dev *edev = data; +	struct pci_dn *pdn = eeh_dev_to_pdn(edev); +	struct pci_dev *pdev = eeh_dev_to_pci_dev(edev); +	struct pci_dev *dev = userdata; + +	if (!pdev) +		return NULL; + +	/* Apply customization from firmware */ +	if (pdn && eeh_ops->restore_config) +		eeh_ops->restore_config(pdn); + +	/* The caller should restore state for the specified device */ +	if (pdev != dev) +		pci_save_state(pdev); + +	return NULL; +} +  /**   * pcibios_set_pcie_slot_reset - Set PCI-E reset state   * @dev: pci device struct @@ -689,13 +744,19 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat  	switch (state) {  	case pcie_deassert_reset:  		eeh_ops->reset(pe, EEH_RESET_DEACTIVATE); +		eeh_unfreeze_pe(pe, false);  		eeh_pe_state_clear(pe, EEH_PE_CFG_BLOCKED); +		eeh_pe_dev_traverse(pe, eeh_restore_dev_state, dev);  		break;  	case pcie_hot_reset: +		eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); +		eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);  		eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);  		eeh_ops->reset(pe, EEH_RESET_HOT);  		break;  	case pcie_warm_reset: +		eeh_ops->set_option(pe, EEH_OPT_FREEZE_PE); +		eeh_pe_dev_traverse(pe, eeh_disable_and_save_dev_state, dev);  		eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);  		eeh_ops->reset(pe, EEH_RESET_FUNDAMENTAL);  		break; @@ -815,15 +876,15 @@ out:   */  void eeh_save_bars(struct eeh_dev *edev)  { +	struct pci_dn *pdn;  	int i; -	struct device_node *dn; -	if (!edev) +	pdn = eeh_dev_to_pdn(edev); +	if (!pdn)  		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->read_config(pdn, i * 4, 4, &edev->config_space[i]);  	/*  	 * For PCI bridges including root port, we need enable bus @@ -914,7 +975,7 @@ static struct notifier_block eeh_reboot_nb = {  int eeh_init(void)  {  	struct pci_controller *hose, *tmp; -	struct device_node *phb; +	struct pci_dn *pdn;  	static int cnt = 0;  	int ret = 0; @@ -949,20 +1010,9 @@ int eeh_init(void)  		return ret;  	/* Enable EEH for all adapters */ -	if (eeh_has_flag(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); -		} -	} else if (eeh_has_flag(EEH_PROBE_MODE_DEV)) { -		list_for_each_entry_safe(hose, tmp, -			&hose_list, list_node) -			pci_walk_bus(hose->bus, eeh_ops->dev_probe, NULL); -	} else { -		pr_warn("%s: Invalid probe mode %x", -			__func__, eeh_subsystem_flags); -		return -EINVAL; +	list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { +		pdn = hose->pci_data; +		traverse_pci_dn(pdn, eeh_ops->probe, NULL);  	}  	/* @@ -987,8 +1037,8 @@ int eeh_init(void)  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 + * eeh_add_device_early - Enable EEH for the indicated device node + * @pdn: PCI 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). @@ -998,44 +1048,41 @@ core_initcall_sync(eeh_init);   * on the CEC architecture, type of the device, on earlier boot   * command-line arguments & etc.   */ -void eeh_add_device_early(struct device_node *dn) +void eeh_add_device_early(struct pci_dn *pdn)  {  	struct pci_controller *phb; +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn); -	/* -	 * If we're doing EEH probe based on PCI device, we -	 * would delay the probe until late stage because -	 * the PCI device isn't available this moment. -	 */ -	if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) -		return; - -	if (!of_node_to_eeh_dev(dn)) +	if (!edev)  		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) +	phb = edev->phb; +	if (NULL == phb || +	    (eeh_has_flag(EEH_PROBE_MODE_DEVTREE) && 0 == phb->buid))  		return; -	eeh_ops->of_probe(dn, NULL); +	eeh_ops->probe(pdn, NULL);  }  /**   * eeh_add_device_tree_early - Enable EEH for the indicated device - * @dn: device node + * @pdn: PCI device node   *   * This routine must be used to perform EEH initialization for the   * indicated PCI device that was added after system boot (e.g.   * hotplug, dlpar).   */ -void eeh_add_device_tree_early(struct device_node *dn) +void eeh_add_device_tree_early(struct pci_dn *pdn)  { -	struct device_node *sib; +	struct pci_dn *n; -	for_each_child_of_node(dn, sib) -		eeh_add_device_tree_early(sib); -	eeh_add_device_early(dn); +	if (!pdn) +		return; + +	list_for_each_entry(n, &pdn->child_list, list) +		eeh_add_device_tree_early(n); +	eeh_add_device_early(pdn);  }  EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); @@ -1048,7 +1095,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);   */  void eeh_add_device_late(struct pci_dev *dev)  { -	struct device_node *dn; +	struct pci_dn *pdn;  	struct eeh_dev *edev;  	if (!dev || !eeh_enabled()) @@ -1056,8 +1103,8 @@ void eeh_add_device_late(struct pci_dev *dev)  	pr_debug("EEH: Adding device %s\n", pci_name(dev)); -	dn = pci_device_to_OF_node(dev); -	edev = of_node_to_eeh_dev(dn); +	pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); +	edev = pdn_to_eeh_dev(pdn);  	if (edev->pdev == dev) {  		pr_debug("EEH: Already referenced !\n");  		return; @@ -1089,13 +1136,6 @@ void eeh_add_device_late(struct pci_dev *dev)  	edev->pdev = dev;  	dev->dev.archdata.edev = edev; -	/* -	 * We have to do the EEH probe here because the PCI device -	 * hasn't been created yet in the early stage. -	 */ -	if (eeh_has_flag(EEH_PROBE_MODE_DEV)) -		eeh_ops->dev_probe(dev, NULL); -  	eeh_addr_cache_insert_dev(dev);  } diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c index 07d8a2423a61..eeabeabea49c 100644 --- a/arch/powerpc/kernel/eeh_cache.c +++ b/arch/powerpc/kernel/eeh_cache.c @@ -171,30 +171,27 @@ eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,  static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)  { -	struct device_node *dn; +	struct pci_dn *pdn;  	struct eeh_dev *edev;  	int i; -	dn = pci_device_to_OF_node(dev); -	if (!dn) { +	pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); +	if (!pdn) {  		pr_warn("PCI: no pci dn found for dev=%s\n",  			pci_name(dev));  		return;  	} -	edev = of_node_to_eeh_dev(dn); +	edev = pdn_to_eeh_dev(pdn);  	if (!edev) { -		pr_warn("PCI: no EEH dev found for dn=%s\n", -			dn->full_name); +		pr_warn("PCI: no EEH dev found for %s\n", +			pci_name(dev));  		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 +		dev_dbg(&dev->dev, "EEH: Skip building address cache\n");  		return;  	} @@ -282,18 +279,18 @@ void eeh_addr_cache_rmv_dev(struct pci_dev *dev)   */  void eeh_addr_cache_build(void)  { -	struct device_node *dn; +	struct pci_dn *pdn;  	struct eeh_dev *edev;  	struct pci_dev *dev = NULL;  	spin_lock_init(&pci_io_addr_cache_root.piar_lock);  	for_each_pci_dev(dev) { -		dn = pci_device_to_OF_node(dev); -		if (!dn) +		pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); +		if (!pdn)  			continue; -		edev = of_node_to_eeh_dev(dn); +		edev = pdn_to_eeh_dev(pdn);  		if (!edev)  			continue; diff --git a/arch/powerpc/kernel/eeh_dev.c b/arch/powerpc/kernel/eeh_dev.c index e5274ee9a75f..aabba94ff9cb 100644 --- a/arch/powerpc/kernel/eeh_dev.c +++ b/arch/powerpc/kernel/eeh_dev.c @@ -43,13 +43,13 @@  /**   * eeh_dev_init - Create EEH device according to OF node - * @dn: device node + * @pdn: PCI 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) +void *eeh_dev_init(struct pci_dn *pdn, void *data)  {  	struct pci_controller *phb = data;  	struct eeh_dev *edev; @@ -63,8 +63,8 @@ void *eeh_dev_init(struct device_node *dn, void *data)  	}  	/* Associate EEH device with OF node */ -	PCI_DN(dn)->edev = edev; -	edev->dn  = dn; +	pdn->edev = edev; +	edev->pdn = pdn;  	edev->phb = phb;  	INIT_LIST_HEAD(&edev->list); @@ -80,16 +80,16 @@ void *eeh_dev_init(struct device_node *dn, void *data)   */  void eeh_dev_phb_init_dynamic(struct pci_controller *phb)  { -	struct device_node *dn = phb->dn; +	struct pci_dn *root = phb->pci_data;  	/* EEH PE for PHB */  	eeh_phb_pe_create(phb);  	/* EEH device for PHB */ -	eeh_dev_init(dn, phb); +	eeh_dev_init(root, phb);  	/* EEH devices for children OF nodes */ -	traverse_pci_devices(dn, eeh_dev_init, phb); +	traverse_pci_dn(root, eeh_dev_init, phb);  }  /** diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index d099540c0f56..24768ff3cb73 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -83,28 +83,6 @@ static inline void eeh_pcid_put(struct pci_dev *pdev)  	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 diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 1e4946c36f9e..35f0b62259bb 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -291,27 +291,25 @@ struct eeh_pe *eeh_pe_get(struct eeh_dev *edev)   */  static struct eeh_pe *eeh_pe_get_parent(struct eeh_dev *edev)  { -	struct device_node *dn;  	struct eeh_dev *parent; +	struct pci_dn *pdn = eeh_dev_to_pdn(edev);  	/*  	 * 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) { +	pdn = pdn ? pdn->parent : NULL; +	while (pdn) {  		/* 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; +		parent = pdn_to_eeh_dev(pdn); +		if (!parent) +			return NULL;  		if (parent->pe)  			return parent->pe; -		dn = dn->parent; +		pdn = pdn->parent;  	}  	return NULL; @@ -330,6 +328,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)  {  	struct eeh_pe *pe, *parent; +	/* Check if the PE number is valid */ +	if (!eeh_has_flag(EEH_VALID_PE_ZERO) && !edev->pe_config_addr) { +		pr_err("%s: Invalid PE#0 for edev 0x%x on PHB#%d\n", +		       __func__, edev->config_addr, edev->phb->global_number); +		return -EINVAL; +	} +  	/*  	 * Search the PE has been existing or not according  	 * to the PE address. If that has been existing, the @@ -338,21 +343,18 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)  	 */  	pe = eeh_pe_get(edev);  	if (pe && !(pe->type & EEH_PE_INVALID)) { -		if (!edev->pe_config_addr) { -			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); -		pr_debug("EEH: Add %s to Bus PE#%x\n", -			edev->dn->full_name, pe->addr); - +		pr_debug("EEH: Add %04x:%02x:%02x.%01x to Bus PE#%x\n", +			edev->phb->global_number, +			edev->config_addr >> 8, +			PCI_SLOT(edev->config_addr & 0xFF), +			PCI_FUNC(edev->config_addr & 0xFF), +			pe->addr);  		return 0;  	} else if (pe && (pe->type & EEH_PE_INVALID)) {  		list_add_tail(&edev->list, &pe->edevs); @@ -368,9 +370,14 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)  			parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP);  			parent = parent->parent;  		} -		pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", -			edev->dn->full_name, pe->addr, pe->parent->addr); +		pr_debug("EEH: Add %04x:%02x:%02x.%01x to Device " +			 "PE#%x, Parent PE#%x\n", +			edev->phb->global_number, +			edev->config_addr >> 8, +                        PCI_SLOT(edev->config_addr & 0xFF), +                        PCI_FUNC(edev->config_addr & 0xFF), +			pe->addr, pe->parent->addr);  		return 0;  	} @@ -409,8 +416,13 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev)  	list_add_tail(&pe->child, &parent->child_list);  	list_add_tail(&edev->list, &pe->edevs);  	edev->pe = pe; -	pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", -		edev->dn->full_name, pe->addr, pe->parent->addr); +	pr_debug("EEH: Add %04x:%02x:%02x.%01x to " +		 "Device PE#%x, Parent PE#%x\n", +		 edev->phb->global_number, +		 edev->config_addr >> 8, +		 PCI_SLOT(edev->config_addr & 0xFF), +		 PCI_FUNC(edev->config_addr & 0xFF), +		 pe->addr, pe->parent->addr);  	return 0;  } @@ -430,8 +442,11 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev)  	int cnt;  	if (!edev->pe) { -		pr_debug("%s: No PE found for EEH device %s\n", -			 __func__, edev->dn->full_name); +		pr_debug("%s: No PE found for device %04x:%02x:%02x.%01x\n", +			 __func__,  edev->phb->global_number, +			 edev->config_addr >> 8, +			 PCI_SLOT(edev->config_addr & 0xFF), +			 PCI_FUNC(edev->config_addr & 0xFF));  		return -EEXIST;  	} @@ -653,9 +668,9 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state)   * blocked on normal path during the stage. So we need utilize   * eeh operations, which is always permitted.   */ -static void eeh_bridge_check_link(struct eeh_dev *edev, -				  struct device_node *dn) +static void eeh_bridge_check_link(struct eeh_dev *edev)  { +	struct pci_dn *pdn = eeh_dev_to_pdn(edev);  	int cap;  	uint32_t val;  	int timeout = 0; @@ -675,32 +690,32 @@ static void eeh_bridge_check_link(struct eeh_dev *edev,  	/* Check slot status */  	cap = edev->pcie_cap; -	eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val); +	eeh_ops->read_config(pdn, cap + PCI_EXP_SLTSTA, 2, &val);  	if (!(val & PCI_EXP_SLTSTA_PDS)) {  		pr_debug("  No card in the slot (0x%04x) !\n", val);  		return;  	}  	/* Check power status if we have the capability */ -	eeh_ops->read_config(dn, cap + PCI_EXP_SLTCAP, 2, &val); +	eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCAP, 2, &val);  	if (val & PCI_EXP_SLTCAP_PCP) { -		eeh_ops->read_config(dn, cap + PCI_EXP_SLTCTL, 2, &val); +		eeh_ops->read_config(pdn, cap + PCI_EXP_SLTCTL, 2, &val);  		if (val & PCI_EXP_SLTCTL_PCC) {  			pr_debug("  In power-off state, power it on ...\n");  			val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC);  			val |= (0x0100 & PCI_EXP_SLTCTL_PIC); -			eeh_ops->write_config(dn, cap + PCI_EXP_SLTCTL, 2, val); +			eeh_ops->write_config(pdn, cap + PCI_EXP_SLTCTL, 2, val);  			msleep(2 * 1000);  		}  	}  	/* Enable link */ -	eeh_ops->read_config(dn, cap + PCI_EXP_LNKCTL, 2, &val); +	eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCTL, 2, &val);  	val &= ~PCI_EXP_LNKCTL_LD; -	eeh_ops->write_config(dn, cap + PCI_EXP_LNKCTL, 2, val); +	eeh_ops->write_config(pdn, cap + PCI_EXP_LNKCTL, 2, val);  	/* Check link */ -	eeh_ops->read_config(dn, cap + PCI_EXP_LNKCAP, 4, &val); +	eeh_ops->read_config(pdn, cap + PCI_EXP_LNKCAP, 4, &val);  	if (!(val & PCI_EXP_LNKCAP_DLLLARC)) {  		pr_debug("  No link reporting capability (0x%08x) \n", val);  		msleep(1000); @@ -713,7 +728,7 @@ static void eeh_bridge_check_link(struct eeh_dev *edev,  		msleep(20);  		timeout += 20; -		eeh_ops->read_config(dn, cap + PCI_EXP_LNKSTA, 2, &val); +		eeh_ops->read_config(pdn, cap + PCI_EXP_LNKSTA, 2, &val);  		if (val & PCI_EXP_LNKSTA_DLLLA)  			break;  	} @@ -728,9 +743,9 @@ static void eeh_bridge_check_link(struct eeh_dev *edev,  #define BYTE_SWAP(OFF)	(8*((OFF)/4)+3-(OFF))  #define SAVED_BYTE(OFF)	(((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) -static void eeh_restore_bridge_bars(struct eeh_dev *edev, -				    struct device_node *dn) +static void eeh_restore_bridge_bars(struct eeh_dev *edev)  { +	struct pci_dn *pdn = eeh_dev_to_pdn(edev);  	int i;  	/* @@ -738,49 +753,49 @@ static void eeh_restore_bridge_bars(struct eeh_dev *edev,  	 * Bus numbers and windows: 0x18 - 0x30  	 */  	for (i = 4; i < 13; i++) -		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); +		eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]);  	/* Rom: 0x38 */ -	eeh_ops->write_config(dn, 14*4, 4, edev->config_space[14]); +	eeh_ops->write_config(pdn, 14*4, 4, edev->config_space[14]);  	/* Cache line & Latency timer: 0xC 0xD */ -	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, +	eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1,                  SAVED_BYTE(PCI_CACHE_LINE_SIZE)); -        eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, +        eeh_ops->write_config(pdn, PCI_LATENCY_TIMER, 1,                  SAVED_BYTE(PCI_LATENCY_TIMER));  	/* Max latency, min grant, interrupt ping and line: 0x3C */ -	eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); +	eeh_ops->write_config(pdn, 15*4, 4, edev->config_space[15]);  	/* PCI Command: 0x4 */ -	eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]); +	eeh_ops->write_config(pdn, PCI_COMMAND, 4, edev->config_space[1]);  	/* Check the PCIe link is ready */ -	eeh_bridge_check_link(edev, dn); +	eeh_bridge_check_link(edev);  } -static void eeh_restore_device_bars(struct eeh_dev *edev, -				    struct device_node *dn) +static void eeh_restore_device_bars(struct eeh_dev *edev)  { +	struct pci_dn *pdn = eeh_dev_to_pdn(edev);  	int i;  	u32 cmd;  	for (i = 4; i < 10; i++) -		eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); +		eeh_ops->write_config(pdn, i*4, 4, edev->config_space[i]);  	/* 12 == Expansion ROM Address */ -	eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); +	eeh_ops->write_config(pdn, 12*4, 4, edev->config_space[12]); -	eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, +	eeh_ops->write_config(pdn, PCI_CACHE_LINE_SIZE, 1,  		SAVED_BYTE(PCI_CACHE_LINE_SIZE)); -	eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, +	eeh_ops->write_config(pdn, 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]); +	eeh_ops->write_config(pdn, 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); +	eeh_ops->read_config(pdn, PCI_COMMAND, 4, &cmd);  	if (edev->config_space[1] & PCI_COMMAND_PARITY)  		cmd |= PCI_COMMAND_PARITY;  	else @@ -789,7 +804,7 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,  		cmd |= PCI_COMMAND_SERR;  	else  		cmd &= ~PCI_COMMAND_SERR; -	eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); +	eeh_ops->write_config(pdn, PCI_COMMAND, 4, cmd);  }  /** @@ -804,16 +819,16 @@ static void eeh_restore_device_bars(struct eeh_dev *edev,  static void *eeh_restore_one_device_bars(void *data, void *flag)  {  	struct eeh_dev *edev = (struct eeh_dev *)data; -	struct device_node *dn = eeh_dev_to_of_node(edev); +	struct pci_dn *pdn = eeh_dev_to_pdn(edev);  	/* Do special restore for bridges */  	if (edev->mode & EEH_DEV_BRIDGE) -		eeh_restore_bridge_bars(edev, dn); +		eeh_restore_bridge_bars(edev);  	else -		eeh_restore_device_bars(edev, dn); +		eeh_restore_device_bars(edev); -	if (eeh_ops->restore_config) -		eeh_ops->restore_config(dn); +	if (eeh_ops->restore_config && pdn) +		eeh_ops->restore_config(pdn);  	return NULL;  } diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 2f35a72642c6..b60a67d92ebd 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -72,7 +72,7 @@ static int of_pci_phb_probe(struct platform_device *dev)  	/* Register devices with EEH */  	if (dev->dev.of_node->child) -		eeh_add_device_tree_early(dev->dev.of_node); +		eeh_add_device_tree_early(PCI_DN(dev->dev.of_node));  	/* Scan the bus */  	pcibios_scan_phb(phb); diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 5b789177aa29..18d9575729a3 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -75,7 +75,7 @@ void pcibios_add_pci_devices(struct pci_bus * bus)  	struct pci_dev *dev;  	struct device_node *dn = pci_bus_to_OF_node(bus); -	eeh_add_device_tree_early(dn); +	eeh_add_device_tree_early(PCI_DN(dn));  	mode = PCI_PROBE_NORMAL;  	if (ppc_md.pci_probe_mode) diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 83df3075d3df..65b98367005c 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -32,12 +32,108 @@  #include <asm/ppc-pci.h>  #include <asm/firmware.h> +/* + * The function is used to find the firmware data of one + * specific PCI device, which is attached to the indicated + * PCI bus. For VFs, their firmware data is linked to that + * one of PF's bridge. For other devices, their firmware + * data is linked to that of their bridge. + */ +static struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus) +{ +	struct pci_bus *pbus; +	struct device_node *dn; +	struct pci_dn *pdn; + +	/* +	 * We probably have virtual bus which doesn't +	 * have associated bridge. +	 */ +	pbus = bus; +	while (pbus) { +		if (pci_is_root_bus(pbus) || pbus->self) +			break; + +		pbus = pbus->parent; +	} + +	/* +	 * Except virtual bus, all PCI buses should +	 * have device nodes. +	 */ +	dn = pci_bus_to_OF_node(pbus); +	pdn = dn ? PCI_DN(dn) : NULL; + +	return pdn; +} + +struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus, +				    int devfn) +{ +	struct device_node *dn = NULL; +	struct pci_dn *parent, *pdn; +	struct pci_dev *pdev = NULL; + +	/* Fast path: fetch from PCI device */ +	list_for_each_entry(pdev, &bus->devices, bus_list) { +		if (pdev->devfn == devfn) { +			if (pdev->dev.archdata.pci_data) +				return pdev->dev.archdata.pci_data; + +			dn = pci_device_to_OF_node(pdev); +			break; +		} +	} + +	/* Fast path: fetch from device node */ +	pdn = dn ? PCI_DN(dn) : NULL; +	if (pdn) +		return pdn; + +	/* Slow path: fetch from firmware data hierarchy */ +	parent = pci_bus_to_pdn(bus); +	if (!parent) +		return NULL; + +	list_for_each_entry(pdn, &parent->child_list, list) { +		if (pdn->busno == bus->number && +                    pdn->devfn == devfn) +                        return pdn; +        } + +	return NULL; +} +  struct pci_dn *pci_get_pdn(struct pci_dev *pdev)  { -	struct device_node *dn = pci_device_to_OF_node(pdev); -	if (!dn) +	struct device_node *dn; +	struct pci_dn *parent, *pdn; + +	/* Search device directly */ +	if (pdev->dev.archdata.pci_data) +		return pdev->dev.archdata.pci_data; + +	/* Check device node */ +	dn = pci_device_to_OF_node(pdev); +	pdn = dn ? PCI_DN(dn) : NULL; +	if (pdn) +		return pdn; + +	/* +	 * VFs don't have device nodes. We hook their +	 * firmware data to PF's bridge. +	 */ +	parent = pci_bus_to_pdn(pdev->bus); +	if (!parent)  		return NULL; -	return PCI_DN(dn); + +	list_for_each_entry(pdn, &parent->child_list, list) { +		if (pdn->busno == pdev->bus->number && +		    pdn->devfn == pdev->devfn) +			return pdn; +	} + +	return NULL;  }  /* @@ -49,6 +145,7 @@ void *update_dn_pci_info(struct device_node *dn, void *data)  	struct pci_controller *phb = data;  	const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);  	const __be32 *regs; +	struct device_node *parent;  	struct pci_dn *pdn;  	pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL); @@ -69,7 +166,25 @@ void *update_dn_pci_info(struct device_node *dn, void *data)  		pdn->devfn = (addr >> 8) & 0xff;  	} +	/* vendor/device IDs and class code */ +	regs = of_get_property(dn, "vendor-id", NULL); +	pdn->vendor_id = regs ? of_read_number(regs, 1) : 0; +	regs = of_get_property(dn, "device-id", NULL); +	pdn->device_id = regs ? of_read_number(regs, 1) : 0; +	regs = of_get_property(dn, "class-code", NULL); +	pdn->class_code = regs ? of_read_number(regs, 1) : 0; + +	/* Extended config space */  	pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1); + +	/* Attach to parent node */ +	INIT_LIST_HEAD(&pdn->child_list); +	INIT_LIST_HEAD(&pdn->list); +	parent = of_get_parent(dn); +	pdn->parent = parent ? PCI_DN(parent) : NULL; +	if (pdn->parent) +		list_add_tail(&pdn->list, &pdn->parent->child_list); +  	return NULL;  } @@ -131,6 +246,46 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre,  	return NULL;  } +static struct pci_dn *pci_dn_next_one(struct pci_dn *root, +				      struct pci_dn *pdn) +{ +	struct list_head *next = pdn->child_list.next; + +	if (next != &pdn->child_list) +		return list_entry(next, struct pci_dn, list); + +	while (1) { +		if (pdn == root) +			return NULL; + +		next = pdn->list.next; +		if (next != &pdn->parent->child_list) +			break; + +		pdn = pdn->parent; +	} + +	return list_entry(next, struct pci_dn, list); +} + +void *traverse_pci_dn(struct pci_dn *root, +		      void *(*fn)(struct pci_dn *, void *), +		      void *data) +{ +	struct pci_dn *pdn = root; +	void *ret; + +	/* Only scan the child nodes */ +	for (pdn = pci_dn_next_one(root, pdn); pdn; +	     pdn = pci_dn_next_one(root, pdn)) { +		ret = fn(pdn, data); +		if (ret) +			return ret; +	} + +	return NULL; +} +  /**    * pci_devs_phb_init_dynamic - setup pci devices under this PHB   * phb: pci-to-host bridge (top-level bridge connecting to cpu) @@ -147,8 +302,12 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)  	/* PHB nodes themselves must not match */  	update_dn_pci_info(dn, phb);  	pdn = dn->data; -	if (pdn) +	if (pdn) {  		pdn->devfn = pdn->busno = -1; +		pdn->vendor_id = pdn->device_id = pdn->class_code = 0; +		pdn->phb = phb; +		phb->pci_data = pdn; +	}  	/* Update dn->phb ptrs for new phb and children devices */  	traverse_pci_devices(dn, update_dn_pci_info, phb); @@ -171,3 +330,16 @@ void __init pci_devs_phb_init(void)  	list_for_each_entry_safe(phb, tmp, &hose_list, list_node)  		pci_devs_phb_init_dynamic(phb);  } + +static void pci_dev_pdn_setup(struct pci_dev *pdev) +{ +	struct pci_dn *pdn; + +	if (pdev->dev.archdata.pci_data) +		return; + +	/* Setup the fast path */ +	pdn = pci_get_pdn(pdev); +	pdev->dev.archdata.pci_data = pdn; +} +DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pci_dev_pdn_setup); diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index e6245e9c7d8d..7122dfece393 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -305,7 +305,7 @@ static struct pci_dev *of_scan_pci_dev(struct pci_bus *bus,  	const __be32 *reg;  	int reglen, devfn;  #ifdef CONFIG_EEH -	struct eeh_dev *edev = of_node_to_eeh_dev(dn); +	struct eeh_dev *edev = pdn_to_eeh_dev(PCI_DN(dn));  #endif  	pr_debug("  * %s\n", dn->full_name); diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index ce230da2c015..af29df2517f7 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -113,7 +113,7 @@ static int rtas_pci_read_config(struct pci_bus *bus,  	ret = rtas_read_config(pdn, where, size, val);  	if (*val == EEH_IO_ERROR_VALUE(size) && -	    eeh_dev_check_failure(of_node_to_eeh_dev(dn))) +	    eeh_dev_check_failure(pdn_to_eeh_dev(pdn)))  		return PCIBIOS_DEVICE_NOT_FOUND;  	return ret; diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 6f3c5d33c3af..33e44f37212f 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -5,7 +5,7 @@ obj-y			+= opal-msglog.o opal-hmi.o opal-power.o  obj-$(CONFIG_SMP)	+= smp.o subcore.o subcore-asm.o  obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o -obj-$(CONFIG_EEH)	+= eeh-ioda.o eeh-powernv.o +obj-$(CONFIG_EEH)	+= eeh-powernv.o  obj-$(CONFIG_PPC_SCOM)	+= opal-xscom.o  obj-$(CONFIG_MEMORY_FAILURE)	+= opal-memory-errors.o  obj-$(CONFIG_TRACEPOINTS)	+= opal-tracepoints.o diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c deleted file mode 100644 index 2809c9895288..000000000000 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * The file intends to implement the functions needed by EEH, which is - * built on IODA compliant chip. Actually, lots of functions related - * to EEH would be built based on the OPAL APIs. - * - * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2013. - * - * 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/debugfs.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/kernel.h> -#include <linux/msi.h> -#include <linux/notifier.h> -#include <linux/pci.h> -#include <linux/string.h> - -#include <asm/eeh.h> -#include <asm/eeh_event.h> -#include <asm/io.h> -#include <asm/iommu.h> -#include <asm/msi_bitmap.h> -#include <asm/opal.h> -#include <asm/pci-bridge.h> -#include <asm/ppc-pci.h> -#include <asm/tce.h> - -#include "powernv.h" -#include "pci.h" - -static int ioda_eeh_nb_init = 0; - -static int ioda_eeh_event(struct notifier_block *nb, -			  unsigned long events, void *change) -{ -	uint64_t changed_evts = (uint64_t)change; - -	/* -	 * We simply send special EEH event if EEH has -	 * been enabled, or clear pending events in -	 * case that we enable EEH soon -	 */ -	if (!(changed_evts & OPAL_EVENT_PCI_ERROR) || -	    !(events & OPAL_EVENT_PCI_ERROR)) -		return 0; - -	if (eeh_enabled()) -		eeh_send_failure_event(NULL); -	else -		opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul); - -	return 0; -} - -static struct notifier_block ioda_eeh_nb = { -	.notifier_call	= ioda_eeh_event, -	.next		= NULL, -	.priority	= 0 -}; - -#ifdef CONFIG_DEBUG_FS -static ssize_t ioda_eeh_ei_write(struct file *filp, -				 const char __user *user_buf, -				 size_t count, loff_t *ppos) -{ -	struct pci_controller *hose = filp->private_data; -	struct pnv_phb *phb = hose->private_data; -	struct eeh_dev *edev; -	struct eeh_pe *pe; -	int pe_no, type, func; -	unsigned long addr, mask; -	char buf[50]; -	int ret; - -	if (!phb->eeh_ops || !phb->eeh_ops->err_inject) -		return -ENXIO; - -	ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); -	if (!ret) -		return -EFAULT; - -	/* Retrieve parameters */ -	ret = sscanf(buf, "%x:%x:%x:%lx:%lx", -		     &pe_no, &type, &func, &addr, &mask); -	if (ret != 5) -		return -EINVAL; - -	/* Retrieve PE */ -	edev = kzalloc(sizeof(*edev), GFP_KERNEL); -	if (!edev) -		return -ENOMEM; -	edev->phb = hose; -	edev->pe_config_addr = pe_no; -	pe = eeh_pe_get(edev); -	kfree(edev); -	if (!pe) -		return -ENODEV; - -	/* Do error injection */ -	ret = phb->eeh_ops->err_inject(pe, type, func, addr, mask); -	return ret < 0 ? ret : count; -} - -static const struct file_operations ioda_eeh_ei_fops = { -	.open   = simple_open, -	.llseek = no_llseek, -	.write  = ioda_eeh_ei_write, -}; - -static int ioda_eeh_dbgfs_set(void *data, int offset, u64 val) -{ -	struct pci_controller *hose = data; -	struct pnv_phb *phb = hose->private_data; - -	out_be64(phb->regs + offset, val); -	return 0; -} - -static int ioda_eeh_dbgfs_get(void *data, int offset, u64 *val) -{ -	struct pci_controller *hose = data; -	struct pnv_phb *phb = hose->private_data; - -	*val = in_be64(phb->regs + offset); -	return 0; -} - -static int ioda_eeh_outb_dbgfs_set(void *data, u64 val) -{ -	return ioda_eeh_dbgfs_set(data, 0xD10, val); -} - -static int ioda_eeh_outb_dbgfs_get(void *data, u64 *val) -{ -	return ioda_eeh_dbgfs_get(data, 0xD10, val); -} - -static int ioda_eeh_inbA_dbgfs_set(void *data, u64 val) -{ -	return ioda_eeh_dbgfs_set(data, 0xD90, val); -} - -static int ioda_eeh_inbA_dbgfs_get(void *data, u64 *val) -{ -	return ioda_eeh_dbgfs_get(data, 0xD90, val); -} - -static int ioda_eeh_inbB_dbgfs_set(void *data, u64 val) -{ -	return ioda_eeh_dbgfs_set(data, 0xE10, val); -} - -static int ioda_eeh_inbB_dbgfs_get(void *data, u64 *val) -{ -	return ioda_eeh_dbgfs_get(data, 0xE10, val); -} - -DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_outb_dbgfs_ops, ioda_eeh_outb_dbgfs_get, -			ioda_eeh_outb_dbgfs_set, "0x%llx\n"); -DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbA_dbgfs_ops, ioda_eeh_inbA_dbgfs_get, -			ioda_eeh_inbA_dbgfs_set, "0x%llx\n"); -DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbB_dbgfs_ops, ioda_eeh_inbB_dbgfs_get, -			ioda_eeh_inbB_dbgfs_set, "0x%llx\n"); -#endif /* CONFIG_DEBUG_FS */ - - -/** - * ioda_eeh_post_init - Chip dependent post initialization - * @hose: PCI controller - * - * The function will be called after eeh PEs and devices - * have been built. That means the EEH is ready to supply - * service with I/O cache. - */ -static int ioda_eeh_post_init(struct pci_controller *hose) -{ -	struct pnv_phb *phb = hose->private_data; -	int ret; - -	/* Register OPAL event notifier */ -	if (!ioda_eeh_nb_init) { -		ret = opal_notifier_register(&ioda_eeh_nb); -		if (ret) { -			pr_err("%s: Can't register OPAL event notifier (%d)\n", -			       __func__, ret); -			return ret; -		} - -		ioda_eeh_nb_init = 1; -	} - -#ifdef CONFIG_DEBUG_FS -	if (!phb->has_dbgfs && phb->dbgfs) { -		phb->has_dbgfs = 1; - -		debugfs_create_file("err_injct", 0200, -				    phb->dbgfs, hose, -				    &ioda_eeh_ei_fops); - -		debugfs_create_file("err_injct_outbound", 0600, -				    phb->dbgfs, hose, -				    &ioda_eeh_outb_dbgfs_ops); -		debugfs_create_file("err_injct_inboundA", 0600, -				    phb->dbgfs, hose, -				    &ioda_eeh_inbA_dbgfs_ops); -		debugfs_create_file("err_injct_inboundB", 0600, -				    phb->dbgfs, hose, -				    &ioda_eeh_inbB_dbgfs_ops); -	} -#endif - -	/* If EEH is enabled, we're going to rely on that. -	 * Otherwise, we restore to conventional mechanism -	 * to clear frozen PE during PCI config access. -	 */ -	if (eeh_enabled()) -		phb->flags |= PNV_PHB_FLAG_EEH; -	else -		phb->flags &= ~PNV_PHB_FLAG_EEH; - -	return 0; -} - -/** - * ioda_eeh_set_option - Set EEH operation or I/O setting - * @pe: EEH PE - * @option: options - * - * Enable or disable EEH option for the indicated PE. The - * function also can be used to enable I/O or DMA for the - * PE. - */ -static int ioda_eeh_set_option(struct eeh_pe *pe, int option) -{ -	struct pci_controller *hose = pe->phb; -	struct pnv_phb *phb = hose->private_data; -	bool freeze_pe = false; -	int enable, ret = 0; -	s64 rc; - -	/* Check on PE number */ -	if (pe->addr < 0 || pe->addr >= phb->ioda.total_pe) { -		pr_err("%s: PE address %x out of range [0, %x] " -		       "on PHB#%x\n", -			__func__, pe->addr, phb->ioda.total_pe, -			hose->global_number); -		return -EINVAL; -	} - -	switch (option) { -	case EEH_OPT_DISABLE: -		return -EPERM; -	case EEH_OPT_ENABLE: -		return 0; -	case EEH_OPT_THAW_MMIO: -		enable = OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO; -		break; -	case EEH_OPT_THAW_DMA: -		enable = OPAL_EEH_ACTION_CLEAR_FREEZE_DMA; -		break; -	case EEH_OPT_FREEZE_PE: -		freeze_pe = true; -		enable = OPAL_EEH_ACTION_SET_FREEZE_ALL; -		break; -	default: -		pr_warn("%s: Invalid option %d\n", -			__func__, option); -		return -EINVAL; -	} - -	/* If PHB supports compound PE, to handle it */ -	if (freeze_pe) { -		if (phb->freeze_pe) { -			phb->freeze_pe(phb, pe->addr); -		} else { -			rc = opal_pci_eeh_freeze_set(phb->opal_id, -						     pe->addr, -						     enable); -			if (rc != OPAL_SUCCESS) { -				pr_warn("%s: Failure %lld freezing " -					"PHB#%x-PE#%x\n", -					__func__, rc, -					phb->hose->global_number, pe->addr); -				ret = -EIO; -			} -		} -	} else { -		if (phb->unfreeze_pe) { -			ret = phb->unfreeze_pe(phb, pe->addr, enable); -		} else { -			rc = opal_pci_eeh_freeze_clear(phb->opal_id, -						       pe->addr, -						       enable); -			if (rc != OPAL_SUCCESS) { -				pr_warn("%s: Failure %lld enable %d " -					"for PHB#%x-PE#%x\n", -					__func__, rc, option, -					phb->hose->global_number, pe->addr); -				ret = -EIO; -			} -		} -	} - -	return ret; -} - -static void ioda_eeh_phb_diag(struct eeh_pe *pe) -{ -	struct pnv_phb *phb = pe->phb->private_data; -	long rc; - -	rc = opal_pci_get_phb_diag_data2(phb->opal_id, pe->data, -					 PNV_PCI_DIAG_BUF_SIZE); -	if (rc != OPAL_SUCCESS) -		pr_warn("%s: Failed to get diag-data for PHB#%x (%ld)\n", -			__func__, pe->phb->global_number, rc); -} - -static int ioda_eeh_get_phb_state(struct eeh_pe *pe) -{ -	struct pnv_phb *phb = pe->phb->private_data; -	u8 fstate; -	__be16 pcierr; -	s64 rc; -	int result = 0; - -	rc = opal_pci_eeh_freeze_status(phb->opal_id, -					pe->addr, -					&fstate, -					&pcierr, -					NULL); -	if (rc != OPAL_SUCCESS) { -		pr_warn("%s: Failure %lld getting PHB#%x state\n", -			__func__, rc, phb->hose->global_number); -		return EEH_STATE_NOT_SUPPORT; -	} - -	/* -	 * Check PHB state. If the PHB is frozen for the -	 * first time, to dump the PHB diag-data. -	 */ -	if (be16_to_cpu(pcierr) != OPAL_EEH_PHB_ERROR) { -		result = (EEH_STATE_MMIO_ACTIVE  | -			  EEH_STATE_DMA_ACTIVE   | -			  EEH_STATE_MMIO_ENABLED | -			  EEH_STATE_DMA_ENABLED); -	} else if (!(pe->state & EEH_PE_ISOLATED)) { -		eeh_pe_state_mark(pe, EEH_PE_ISOLATED); -		ioda_eeh_phb_diag(pe); - -		if (eeh_has_flag(EEH_EARLY_DUMP_LOG)) -			pnv_pci_dump_phb_diag_data(pe->phb, pe->data); -	} - -	return result; -} - -static int ioda_eeh_get_pe_state(struct eeh_pe *pe) -{ -	struct pnv_phb *phb = pe->phb->private_data; -	u8 fstate; -	__be16 pcierr; -	s64 rc; -	int result; - -	/* -	 * We don't clobber hardware frozen state until PE -	 * reset is completed. In order to keep EEH core -	 * moving forward, we have to return operational -	 * state during PE reset. -	 */ -	if (pe->state & EEH_PE_RESET) { -		result = (EEH_STATE_MMIO_ACTIVE  | -			  EEH_STATE_DMA_ACTIVE   | -			  EEH_STATE_MMIO_ENABLED | -			  EEH_STATE_DMA_ENABLED); -		return result; -	} - -	/* -	 * Fetch PE state from hardware. If the PHB -	 * supports compound PE, let it handle that. -	 */ -	if (phb->get_pe_state) { -		fstate = phb->get_pe_state(phb, pe->addr); -	} else { -		rc = opal_pci_eeh_freeze_status(phb->opal_id, -						pe->addr, -						&fstate, -						&pcierr, -						NULL); -		if (rc != OPAL_SUCCESS) { -			pr_warn("%s: Failure %lld getting PHB#%x-PE%x state\n", -				__func__, rc, phb->hose->global_number, pe->addr); -			return EEH_STATE_NOT_SUPPORT; -		} -	} - -	/* Figure out state */ -	switch (fstate) { -	case OPAL_EEH_STOPPED_NOT_FROZEN: -		result = (EEH_STATE_MMIO_ACTIVE  | -			  EEH_STATE_DMA_ACTIVE   | -			  EEH_STATE_MMIO_ENABLED | -			  EEH_STATE_DMA_ENABLED); -		break; -	case OPAL_EEH_STOPPED_MMIO_FREEZE: -		result = (EEH_STATE_DMA_ACTIVE | -			  EEH_STATE_DMA_ENABLED); -		break; -	case OPAL_EEH_STOPPED_DMA_FREEZE: -		result = (EEH_STATE_MMIO_ACTIVE | -			  EEH_STATE_MMIO_ENABLED); -		break; -	case OPAL_EEH_STOPPED_MMIO_DMA_FREEZE: -		result = 0; -		break; -	case OPAL_EEH_STOPPED_RESET: -		result = EEH_STATE_RESET_ACTIVE; -		break; -	case OPAL_EEH_STOPPED_TEMP_UNAVAIL: -		result = EEH_STATE_UNAVAILABLE; -		break; -	case OPAL_EEH_STOPPED_PERM_UNAVAIL: -		result = EEH_STATE_NOT_SUPPORT; -		break; -	default: -		result = EEH_STATE_NOT_SUPPORT; -		pr_warn("%s: Invalid PHB#%x-PE#%x state %x\n", -			__func__, phb->hose->global_number, -			pe->addr, fstate); -	} - -	/* -	 * If PHB supports compound PE, to freeze all -	 * slave PEs for consistency. -	 * -	 * If the PE is switching to frozen state for the -	 * first time, to dump the PHB diag-data. -	 */ -	if (!(result & EEH_STATE_NOT_SUPPORT) && -	    !(result & EEH_STATE_UNAVAILABLE) && -	    !(result & EEH_STATE_MMIO_ACTIVE) && -	    !(result & EEH_STATE_DMA_ACTIVE)  && -	    !(pe->state & EEH_PE_ISOLATED)) { -		if (phb->freeze_pe) -			phb->freeze_pe(phb, pe->addr); - -		eeh_pe_state_mark(pe, EEH_PE_ISOLATED); -		ioda_eeh_phb_diag(pe); - -		if (eeh_has_flag(EEH_EARLY_DUMP_LOG)) -			pnv_pci_dump_phb_diag_data(pe->phb, pe->data); -	} - -	return result; -} - -/** - * ioda_eeh_get_state - Retrieve the state of PE - * @pe: EEH PE - * - * The PE's state should be retrieved from the PEEV, PEST - * IODA tables. Since the OPAL has exported the function - * to do it, it'd better to use that. - */ -static int ioda_eeh_get_state(struct eeh_pe *pe) -{ -	struct pnv_phb *phb = pe->phb->private_data; - -	/* Sanity check on PE number. PHB PE should have 0 */ -	if (pe->addr < 0 || -	    pe->addr >= phb->ioda.total_pe) { -		pr_warn("%s: PHB#%x-PE#%x out of range [0, %x]\n", -			__func__, phb->hose->global_number, -			pe->addr, phb->ioda.total_pe); -		return EEH_STATE_NOT_SUPPORT; -	} - -	if (pe->type & EEH_PE_PHB) -		return ioda_eeh_get_phb_state(pe); - -	return ioda_eeh_get_pe_state(pe); -} - -static s64 ioda_eeh_phb_poll(struct pnv_phb *phb) -{ -	s64 rc = OPAL_HARDWARE; - -	while (1) { -		rc = opal_pci_poll(phb->opal_id); -		if (rc <= 0) -			break; - -		if (system_state < SYSTEM_RUNNING) -			udelay(1000 * rc); -		else -			msleep(rc); -	} - -	return rc; -} - -int ioda_eeh_phb_reset(struct pci_controller *hose, int option) -{ -	struct pnv_phb *phb = hose->private_data; -	s64 rc = OPAL_HARDWARE; - -	pr_debug("%s: Reset PHB#%x, option=%d\n", -		 __func__, hose->global_number, option); - -	/* Issue PHB complete reset request */ -	if (option == EEH_RESET_FUNDAMENTAL || -	    option == EEH_RESET_HOT) -		rc = opal_pci_reset(phb->opal_id, -				OPAL_RESET_PHB_COMPLETE, -				OPAL_ASSERT_RESET); -	else if (option == EEH_RESET_DEACTIVATE) -		rc = opal_pci_reset(phb->opal_id, -				OPAL_RESET_PHB_COMPLETE, -				OPAL_DEASSERT_RESET); -	if (rc < 0) -		goto out; - -	/* -	 * Poll state of the PHB until the request is done -	 * successfully. The PHB reset is usually PHB complete -	 * reset followed by hot reset on root bus. So we also -	 * need the PCI bus settlement delay. -	 */ -	rc = ioda_eeh_phb_poll(phb); -	if (option == EEH_RESET_DEACTIVATE) { -		if (system_state < SYSTEM_RUNNING) -			udelay(1000 * EEH_PE_RST_SETTLE_TIME); -		else -			msleep(EEH_PE_RST_SETTLE_TIME); -	} -out: -	if (rc != OPAL_SUCCESS) -		return -EIO; - -	return 0; -} - -static int ioda_eeh_root_reset(struct pci_controller *hose, int option) -{ -	struct pnv_phb *phb = hose->private_data; -	s64 rc = OPAL_SUCCESS; - -	pr_debug("%s: Reset PHB#%x, option=%d\n", -		 __func__, hose->global_number, option); - -	/* -	 * During the reset deassert time, we needn't care -	 * the reset scope because the firmware does nothing -	 * for fundamental or hot reset during deassert phase. -	 */ -	if (option == EEH_RESET_FUNDAMENTAL) -		rc = opal_pci_reset(phb->opal_id, -				OPAL_RESET_PCI_FUNDAMENTAL, -				OPAL_ASSERT_RESET); -	else if (option == EEH_RESET_HOT) -		rc = opal_pci_reset(phb->opal_id, -				OPAL_RESET_PCI_HOT, -				OPAL_ASSERT_RESET); -	else if (option == EEH_RESET_DEACTIVATE) -		rc = opal_pci_reset(phb->opal_id, -				OPAL_RESET_PCI_HOT, -				OPAL_DEASSERT_RESET); -	if (rc < 0) -		goto out; - -	/* Poll state of the PHB until the request is done */ -	rc = ioda_eeh_phb_poll(phb); -	if (option == EEH_RESET_DEACTIVATE) -		msleep(EEH_PE_RST_SETTLE_TIME); -out: -	if (rc != OPAL_SUCCESS) -		return -EIO; - -	return 0; -} - -static int ioda_eeh_bridge_reset(struct pci_dev *dev, int option) - -{ -	struct device_node *dn = pci_device_to_OF_node(dev); -	struct eeh_dev *edev = of_node_to_eeh_dev(dn); -	int aer = edev ? edev->aer_cap : 0; -	u32 ctrl; - -	pr_debug("%s: Reset PCI bus %04x:%02x with option %d\n", -		 __func__, pci_domain_nr(dev->bus), -		 dev->bus->number, option); - -	switch (option) { -	case EEH_RESET_FUNDAMENTAL: -	case EEH_RESET_HOT: -		/* Don't report linkDown event */ -		if (aer) { -			eeh_ops->read_config(dn, aer + PCI_ERR_UNCOR_MASK, -					     4, &ctrl); -			ctrl |= PCI_ERR_UNC_SURPDN; -                        eeh_ops->write_config(dn, aer + PCI_ERR_UNCOR_MASK, -					      4, ctrl); -                } - -		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &ctrl); -		ctrl |= PCI_BRIDGE_CTL_BUS_RESET; -		eeh_ops->write_config(dn, PCI_BRIDGE_CONTROL, 2, ctrl); -		msleep(EEH_PE_RST_HOLD_TIME); - -		break; -	case EEH_RESET_DEACTIVATE: -		eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &ctrl); -		ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; -		eeh_ops->write_config(dn, PCI_BRIDGE_CONTROL, 2, ctrl); -		msleep(EEH_PE_RST_SETTLE_TIME); - -		/* Continue reporting linkDown event */ -		if (aer) { -			eeh_ops->read_config(dn, aer + PCI_ERR_UNCOR_MASK, -					     4, &ctrl); -			ctrl &= ~PCI_ERR_UNC_SURPDN; -			eeh_ops->write_config(dn, aer + PCI_ERR_UNCOR_MASK, -					      4, ctrl); -		} - -		break; -	} - -	return 0; -} - -void pnv_pci_reset_secondary_bus(struct pci_dev *dev) -{ -	struct pci_controller *hose; - -	if (pci_is_root_bus(dev->bus)) { -		hose = pci_bus_to_host(dev->bus); -		ioda_eeh_root_reset(hose, EEH_RESET_HOT); -		ioda_eeh_root_reset(hose, EEH_RESET_DEACTIVATE); -	} else { -		ioda_eeh_bridge_reset(dev, EEH_RESET_HOT); -		ioda_eeh_bridge_reset(dev, EEH_RESET_DEACTIVATE); -	} -} - -/** - * ioda_eeh_reset - Reset the indicated PE - * @pe: EEH PE - * @option: reset option - * - * Do reset on the indicated PE. For PCI bus sensitive PE, - * we need to reset the parent p2p bridge. The PHB has to - * be reinitialized if the p2p bridge is root bridge. For - * PCI device sensitive PE, we will try to reset the device - * through FLR. For now, we don't have OPAL APIs to do HARD - * reset yet, so all reset would be SOFT (HOT) reset. - */ -static int ioda_eeh_reset(struct eeh_pe *pe, int option) -{ -	struct pci_controller *hose = pe->phb; -	struct pci_bus *bus; -	int ret; - -	/* -	 * For PHB reset, we always have complete reset. For those PEs whose -	 * primary bus derived from root complex (root bus) or root port -	 * (usually bus#1), we apply hot or fundamental reset on the root port. -	 * For other PEs, we always have hot reset on the PE primary bus. -	 * -	 * Here, we have different design to pHyp, which always clear the -	 * frozen state during PE reset. However, the good idea here from -	 * benh is to keep frozen state before we get PE reset done completely -	 * (until BAR restore). With the frozen state, HW drops illegal IO -	 * or MMIO access, which can incur recrusive frozen PE during PE -	 * reset. The side effect is that EEH core has to clear the frozen -	 * state explicitly after BAR restore. -	 */ -	if (pe->type & EEH_PE_PHB) { -		ret = ioda_eeh_phb_reset(hose, option); -	} else { -		struct pnv_phb *phb; -		s64 rc; - -		/* -		 * The frozen PE might be caused by PAPR error injection -		 * registers, which are expected to be cleared after hitting -		 * frozen PE as stated in the hardware spec. Unfortunately, -		 * that's not true on P7IOC. So we have to clear it manually -		 * to avoid recursive EEH errors during recovery. -		 */ -		phb = hose->private_data; -		if (phb->model == PNV_PHB_MODEL_P7IOC && -		    (option == EEH_RESET_HOT || -		    option == EEH_RESET_FUNDAMENTAL)) { -			rc = opal_pci_reset(phb->opal_id, -					    OPAL_RESET_PHB_ERROR, -					    OPAL_ASSERT_RESET); -			if (rc != OPAL_SUCCESS) { -				pr_warn("%s: Failure %lld clearing " -					"error injection registers\n", -					__func__, rc); -				return -EIO; -			} -		} - -		bus = eeh_pe_bus_get(pe); -		if (pci_is_root_bus(bus) || -		    pci_is_root_bus(bus->parent)) -			ret = ioda_eeh_root_reset(hose, option); -		else -			ret = ioda_eeh_bridge_reset(bus->self, option); -	} - -	return ret; -} - -/** - * ioda_eeh_get_log - Retrieve error log - * @pe: frozen PE - * @severity: permanent or temporary error - * @drv_log: device driver log - * @len: length of device driver log - * - * Retrieve error log, which contains log from device driver - * and firmware. - */ -static int ioda_eeh_get_log(struct eeh_pe *pe, int severity, -			    char *drv_log, unsigned long len) -{ -	if (!eeh_has_flag(EEH_EARLY_DUMP_LOG)) -		pnv_pci_dump_phb_diag_data(pe->phb, pe->data); - -	return 0; -} - -/** - * ioda_eeh_configure_bridge - Configure the PCI bridges for the indicated PE - * @pe: EEH PE - * - * For particular PE, it might have included PCI bridges. In order - * to make the PE work properly, those PCI bridges should be configured - * correctly. However, we need do nothing on P7IOC since the reset - * function will do everything that should be covered by the function. - */ -static int ioda_eeh_configure_bridge(struct eeh_pe *pe) -{ -	return 0; -} - -static int ioda_eeh_err_inject(struct eeh_pe *pe, int type, int func, -			       unsigned long addr, unsigned long mask) -{ -	struct pci_controller *hose = pe->phb; -	struct pnv_phb *phb = hose->private_data; -	s64 ret; - -	/* Sanity check on error type */ -	if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR && -	    type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) { -		pr_warn("%s: Invalid error type %d\n", -			__func__, type); -		return -ERANGE; -	} - -	if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR || -	    func > OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET) { -		pr_warn("%s: Invalid error function %d\n", -			__func__, func); -		return -ERANGE; -	} - -	/* Firmware supports error injection ? */ -	if (!opal_check_token(OPAL_PCI_ERR_INJECT)) { -		pr_warn("%s: Firmware doesn't support error injection\n", -			__func__); -		return -ENXIO; -	} - -	/* Do error injection */ -	ret = opal_pci_err_inject(phb->opal_id, pe->addr, -				  type, func, addr, mask); -	if (ret != OPAL_SUCCESS) { -		pr_warn("%s: Failure %lld injecting error " -			"%d-%d to PHB#%x-PE#%x\n", -			__func__, ret, type, func, -			hose->global_number, pe->addr); -		return -EIO; -	} - -	return 0; -} - -static void ioda_eeh_hub_diag_common(struct OpalIoP7IOCErrorData *data) -{ -	/* GEM */ -	if (data->gemXfir || data->gemRfir || -	    data->gemRirqfir || data->gemMask || data->gemRwof) -		pr_info("  GEM: %016llx %016llx %016llx %016llx %016llx\n", -			be64_to_cpu(data->gemXfir), -			be64_to_cpu(data->gemRfir), -			be64_to_cpu(data->gemRirqfir), -			be64_to_cpu(data->gemMask), -			be64_to_cpu(data->gemRwof)); - -	/* LEM */ -	if (data->lemFir || data->lemErrMask || -	    data->lemAction0 || data->lemAction1 || data->lemWof) -		pr_info("  LEM: %016llx %016llx %016llx %016llx %016llx\n", -			be64_to_cpu(data->lemFir), -			be64_to_cpu(data->lemErrMask), -			be64_to_cpu(data->lemAction0), -			be64_to_cpu(data->lemAction1), -			be64_to_cpu(data->lemWof)); -} - -static void ioda_eeh_hub_diag(struct pci_controller *hose) -{ -	struct pnv_phb *phb = hose->private_data; -	struct OpalIoP7IOCErrorData *data = &phb->diag.hub_diag; -	long rc; - -	rc = opal_pci_get_hub_diag_data(phb->hub_id, data, sizeof(*data)); -	if (rc != OPAL_SUCCESS) { -		pr_warn("%s: Failed to get HUB#%llx diag-data (%ld)\n", -			__func__, phb->hub_id, rc); -		return; -	} - -	switch (data->type) { -	case OPAL_P7IOC_DIAG_TYPE_RGC: -		pr_info("P7IOC diag-data for RGC\n\n"); -		ioda_eeh_hub_diag_common(data); -		if (data->rgc.rgcStatus || data->rgc.rgcLdcp) -			pr_info("  RGC: %016llx %016llx\n", -				be64_to_cpu(data->rgc.rgcStatus), -				be64_to_cpu(data->rgc.rgcLdcp)); -		break; -	case OPAL_P7IOC_DIAG_TYPE_BI: -		pr_info("P7IOC diag-data for BI %s\n\n", -			data->bi.biDownbound ? "Downbound" : "Upbound"); -		ioda_eeh_hub_diag_common(data); -		if (data->bi.biLdcp0 || data->bi.biLdcp1 || -		    data->bi.biLdcp2 || data->bi.biFenceStatus) -			pr_info("  BI:  %016llx %016llx %016llx %016llx\n", -				be64_to_cpu(data->bi.biLdcp0), -				be64_to_cpu(data->bi.biLdcp1), -				be64_to_cpu(data->bi.biLdcp2), -				be64_to_cpu(data->bi.biFenceStatus)); -		break; -	case OPAL_P7IOC_DIAG_TYPE_CI: -		pr_info("P7IOC diag-data for CI Port %d\n\n", -			data->ci.ciPort); -		ioda_eeh_hub_diag_common(data); -		if (data->ci.ciPortStatus || data->ci.ciPortLdcp) -			pr_info("  CI:  %016llx %016llx\n", -				be64_to_cpu(data->ci.ciPortStatus), -				be64_to_cpu(data->ci.ciPortLdcp)); -		break; -	case OPAL_P7IOC_DIAG_TYPE_MISC: -		pr_info("P7IOC diag-data for MISC\n\n"); -		ioda_eeh_hub_diag_common(data); -		break; -	case OPAL_P7IOC_DIAG_TYPE_I2C: -		pr_info("P7IOC diag-data for I2C\n\n"); -		ioda_eeh_hub_diag_common(data); -		break; -	default: -		pr_warn("%s: Invalid type of HUB#%llx diag-data (%d)\n", -			__func__, phb->hub_id, data->type); -	} -} - -static int ioda_eeh_get_pe(struct pci_controller *hose, -			   u16 pe_no, struct eeh_pe **pe) -{ -	struct pnv_phb *phb = hose->private_data; -	struct pnv_ioda_pe *pnv_pe; -	struct eeh_pe *dev_pe; -	struct eeh_dev edev; - -	/* -	 * If PHB supports compound PE, to fetch -	 * the master PE because slave PE is invisible -	 * to EEH core. -	 */ -	pnv_pe = &phb->ioda.pe_array[pe_no]; -	if (pnv_pe->flags & PNV_IODA_PE_SLAVE) { -		pnv_pe = pnv_pe->master; -		WARN_ON(!pnv_pe || -			!(pnv_pe->flags & PNV_IODA_PE_MASTER)); -		pe_no = pnv_pe->pe_number; -	} - -	/* Find the PE according to PE# */ -	memset(&edev, 0, sizeof(struct eeh_dev)); -	edev.phb = hose; -	edev.pe_config_addr = pe_no; -	dev_pe = eeh_pe_get(&edev); -	if (!dev_pe) -		return -EEXIST; - -	/* Freeze the (compound) PE */ -	*pe = dev_pe; -	if (!(dev_pe->state & EEH_PE_ISOLATED)) -		phb->freeze_pe(phb, pe_no); - -	/* -	 * At this point, we're sure the (compound) PE should -	 * have been frozen. However, we still need poke until -	 * hitting the frozen PE on top level. -	 */ -	dev_pe = dev_pe->parent; -	while (dev_pe && !(dev_pe->type & EEH_PE_PHB)) { -		int ret; -		int active_flags = (EEH_STATE_MMIO_ACTIVE | -				    EEH_STATE_DMA_ACTIVE); - -		ret = eeh_ops->get_state(dev_pe, NULL); -		if (ret <= 0 || (ret & active_flags) == active_flags) { -			dev_pe = dev_pe->parent; -			continue; -		} - -		/* Frozen parent PE */ -		*pe = dev_pe; -		if (!(dev_pe->state & EEH_PE_ISOLATED)) -			phb->freeze_pe(phb, dev_pe->addr); - -		/* Next one */ -		dev_pe = dev_pe->parent; -	} - -	return 0; -} - -/** - * ioda_eeh_next_error - Retrieve next error for EEH core to handle - * @pe: The affected PE - * - * The function is expected to be called by EEH core while it gets - * special EEH event (without binding PE). The function calls to - * OPAL APIs for next error to handle. The informational error is - * handled internally by platform. However, the dead IOC, dead PHB, - * fenced PHB and frozen PE should be handled by EEH core eventually. - */ -static int ioda_eeh_next_error(struct eeh_pe **pe) -{ -	struct pci_controller *hose; -	struct pnv_phb *phb; -	struct eeh_pe *phb_pe, *parent_pe; -	__be64 frozen_pe_no; -	__be16 err_type, severity; -	int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); -	long rc; -	int state, ret = EEH_NEXT_ERR_NONE; - -	/* -	 * While running here, it's safe to purge the event queue. -	 * And we should keep the cached OPAL notifier event sychronized -	 * between the kernel and firmware. -	 */ -	eeh_remove_event(NULL, false); -	opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul); - -	list_for_each_entry(hose, &hose_list, list_node) { -		/* -		 * If the subordinate PCI buses of the PHB has been -		 * removed or is exactly under error recovery, we -		 * needn't take care of it any more. -		 */ -		phb = hose->private_data; -		phb_pe = eeh_phb_pe_get(hose); -		if (!phb_pe || (phb_pe->state & EEH_PE_ISOLATED)) -			continue; - -		rc = opal_pci_next_error(phb->opal_id, -				&frozen_pe_no, &err_type, &severity); - -		/* If OPAL API returns error, we needn't proceed */ -		if (rc != OPAL_SUCCESS) { -			pr_devel("%s: Invalid return value on " -				 "PHB#%x (0x%lx) from opal_pci_next_error", -				 __func__, hose->global_number, rc); -			continue; -		} - -		/* If the PHB doesn't have error, stop processing */ -		if (be16_to_cpu(err_type) == OPAL_EEH_NO_ERROR || -		    be16_to_cpu(severity) == OPAL_EEH_SEV_NO_ERROR) { -			pr_devel("%s: No error found on PHB#%x\n", -				 __func__, hose->global_number); -			continue; -		} - -		/* -		 * Processing the error. We're expecting the error with -		 * highest priority reported upon multiple errors on the -		 * specific PHB. -		 */ -		pr_devel("%s: Error (%d, %d, %llu) on PHB#%x\n", -			 __func__, be16_to_cpu(err_type), be16_to_cpu(severity), -			 be64_to_cpu(frozen_pe_no), hose->global_number); -		switch (be16_to_cpu(err_type)) { -		case OPAL_EEH_IOC_ERROR: -			if (be16_to_cpu(severity) == OPAL_EEH_SEV_IOC_DEAD) { -				pr_err("EEH: dead IOC detected\n"); -				ret = EEH_NEXT_ERR_DEAD_IOC; -			} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) { -				pr_info("EEH: IOC informative error " -					"detected\n"); -				ioda_eeh_hub_diag(hose); -				ret = EEH_NEXT_ERR_NONE; -			} - -			break; -		case OPAL_EEH_PHB_ERROR: -			if (be16_to_cpu(severity) == OPAL_EEH_SEV_PHB_DEAD) { -				*pe = phb_pe; -				pr_err("EEH: dead PHB#%x detected, " -				       "location: %s\n", -				       hose->global_number, -				       eeh_pe_loc_get(phb_pe)); -				ret = EEH_NEXT_ERR_DEAD_PHB; -			} else if (be16_to_cpu(severity) == -						OPAL_EEH_SEV_PHB_FENCED) { -				*pe = phb_pe; -				pr_err("EEH: Fenced PHB#%x detected, " -				       "location: %s\n", -				       hose->global_number, -				       eeh_pe_loc_get(phb_pe)); -				ret = EEH_NEXT_ERR_FENCED_PHB; -			} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) { -				pr_info("EEH: PHB#%x informative error " -					"detected, location: %s\n", -					hose->global_number, -					eeh_pe_loc_get(phb_pe)); -				ioda_eeh_phb_diag(phb_pe); -				pnv_pci_dump_phb_diag_data(hose, phb_pe->data); -				ret = EEH_NEXT_ERR_NONE; -			} - -			break; -		case OPAL_EEH_PE_ERROR: -			/* -			 * If we can't find the corresponding PE, we -			 * just try to unfreeze. -			 */ -			if (ioda_eeh_get_pe(hose, -					    be64_to_cpu(frozen_pe_no), pe)) { -				/* Try best to clear it */ -				pr_info("EEH: Clear non-existing PHB#%x-PE#%llx\n", -					hose->global_number, frozen_pe_no); -				pr_info("EEH: PHB location: %s\n", -					eeh_pe_loc_get(phb_pe)); -				opal_pci_eeh_freeze_clear(phb->opal_id, frozen_pe_no, -					OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); -				ret = EEH_NEXT_ERR_NONE; -			} else if ((*pe)->state & EEH_PE_ISOLATED || -				   eeh_pe_passed(*pe)) { -				ret = EEH_NEXT_ERR_NONE; -			} else { -				pr_err("EEH: Frozen PE#%x on PHB#%x detected\n", -					(*pe)->addr, (*pe)->phb->global_number); -				pr_err("EEH: PE location: %s, PHB location: %s\n", -					eeh_pe_loc_get(*pe), eeh_pe_loc_get(phb_pe)); -				ret = EEH_NEXT_ERR_FROZEN_PE; -			} - -			break; -		default: -			pr_warn("%s: Unexpected error type %d\n", -				__func__, be16_to_cpu(err_type)); -		} - -		/* -		 * EEH core will try recover from fenced PHB or -		 * frozen PE. In the time for frozen PE, EEH core -		 * enable IO path for that before collecting logs, -		 * but it ruins the site. So we have to dump the -		 * log in advance here. -		 */ -		if ((ret == EEH_NEXT_ERR_FROZEN_PE  || -		    ret == EEH_NEXT_ERR_FENCED_PHB) && -		    !((*pe)->state & EEH_PE_ISOLATED)) { -			eeh_pe_state_mark(*pe, EEH_PE_ISOLATED); -			ioda_eeh_phb_diag(*pe); - -			if (eeh_has_flag(EEH_EARLY_DUMP_LOG)) -				pnv_pci_dump_phb_diag_data((*pe)->phb, -							   (*pe)->data); -		} - -		/* -		 * We probably have the frozen parent PE out there and -		 * we need have to handle frozen parent PE firstly. -		 */ -		if (ret == EEH_NEXT_ERR_FROZEN_PE) { -			parent_pe = (*pe)->parent; -			while (parent_pe) { -				/* Hit the ceiling ? */ -				if (parent_pe->type & EEH_PE_PHB) -					break; - -				/* Frozen parent PE ? */ -				state = ioda_eeh_get_state(parent_pe); -				if (state > 0 && -				    (state & active_flags) != active_flags) -					*pe = parent_pe; - -				/* Next parent level */ -				parent_pe = parent_pe->parent; -			} - -			/* We possibly migrate to another PE */ -			eeh_pe_state_mark(*pe, EEH_PE_ISOLATED); -		} - -		/* -		 * If we have no errors on the specific PHB or only -		 * informative error there, we continue poking it. -		 * Otherwise, we need actions to be taken by upper -		 * layer. -		 */ -		if (ret > EEH_NEXT_ERR_INF) -			break; -	} - -	return ret; -} - -struct pnv_eeh_ops ioda_eeh_ops = { -	.post_init		= ioda_eeh_post_init, -	.set_option		= ioda_eeh_set_option, -	.get_state		= ioda_eeh_get_state, -	.reset			= ioda_eeh_reset, -	.get_log		= ioda_eeh_get_log, -	.configure_bridge	= ioda_eeh_configure_bridge, -	.err_inject		= ioda_eeh_err_inject, -	.next_error		= ioda_eeh_next_error -}; diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index e261869adc86..ce738ab3d5a9 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -12,6 +12,7 @@   */  #include <linux/atomic.h> +#include <linux/debugfs.h>  #include <linux/delay.h>  #include <linux/export.h>  #include <linux/init.h> @@ -38,12 +39,14 @@  #include "powernv.h"  #include "pci.h" +static bool pnv_eeh_nb_init = false; +  /** - * powernv_eeh_init - EEH platform dependent initialization + * pnv_eeh_init - EEH platform dependent initialization   *   * EEH platform dependent initialization on powernv   */ -static int powernv_eeh_init(void) +static int pnv_eeh_init(void)  {  	struct pci_controller *hose;  	struct pnv_phb *phb; @@ -85,37 +88,280 @@ static int powernv_eeh_init(void)  	return 0;  } +static int pnv_eeh_event(struct notifier_block *nb, +			 unsigned long events, void *change) +{ +	uint64_t changed_evts = (uint64_t)change; + +	/* +	 * We simply send special EEH event if EEH has +	 * been enabled, or clear pending events in +	 * case that we enable EEH soon +	 */ +	if (!(changed_evts & OPAL_EVENT_PCI_ERROR) || +	    !(events & OPAL_EVENT_PCI_ERROR)) +		return 0; + +	if (eeh_enabled()) +		eeh_send_failure_event(NULL); +	else +		opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul); + +	return 0; +} + +static struct notifier_block pnv_eeh_nb = { +	.notifier_call	= pnv_eeh_event, +	.next		= NULL, +	.priority	= 0 +}; + +#ifdef CONFIG_DEBUG_FS +static ssize_t pnv_eeh_ei_write(struct file *filp, +				const char __user *user_buf, +				size_t count, loff_t *ppos) +{ +	struct pci_controller *hose = filp->private_data; +	struct eeh_dev *edev; +	struct eeh_pe *pe; +	int pe_no, type, func; +	unsigned long addr, mask; +	char buf[50]; +	int ret; + +	if (!eeh_ops || !eeh_ops->err_inject) +		return -ENXIO; + +	/* Copy over argument buffer */ +	ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); +	if (!ret) +		return -EFAULT; + +	/* Retrieve parameters */ +	ret = sscanf(buf, "%x:%x:%x:%lx:%lx", +		     &pe_no, &type, &func, &addr, &mask); +	if (ret != 5) +		return -EINVAL; + +	/* Retrieve PE */ +	edev = kzalloc(sizeof(*edev), GFP_KERNEL); +	if (!edev) +		return -ENOMEM; +	edev->phb = hose; +	edev->pe_config_addr = pe_no; +	pe = eeh_pe_get(edev); +	kfree(edev); +	if (!pe) +		return -ENODEV; + +	/* Do error injection */ +	ret = eeh_ops->err_inject(pe, type, func, addr, mask); +	return ret < 0 ? ret : count; +} + +static const struct file_operations pnv_eeh_ei_fops = { +	.open	= simple_open, +	.llseek	= no_llseek, +	.write	= pnv_eeh_ei_write, +}; + +static int pnv_eeh_dbgfs_set(void *data, int offset, u64 val) +{ +	struct pci_controller *hose = data; +	struct pnv_phb *phb = hose->private_data; + +	out_be64(phb->regs + offset, val); +	return 0; +} + +static int pnv_eeh_dbgfs_get(void *data, int offset, u64 *val) +{ +	struct pci_controller *hose = data; +	struct pnv_phb *phb = hose->private_data; + +	*val = in_be64(phb->regs + offset); +	return 0; +} + +static int pnv_eeh_outb_dbgfs_set(void *data, u64 val) +{ +	return pnv_eeh_dbgfs_set(data, 0xD10, val); +} + +static int pnv_eeh_outb_dbgfs_get(void *data, u64 *val) +{ +	return pnv_eeh_dbgfs_get(data, 0xD10, val); +} + +static int pnv_eeh_inbA_dbgfs_set(void *data, u64 val) +{ +	return pnv_eeh_dbgfs_set(data, 0xD90, val); +} + +static int pnv_eeh_inbA_dbgfs_get(void *data, u64 *val) +{ +	return pnv_eeh_dbgfs_get(data, 0xD90, val); +} + +static int pnv_eeh_inbB_dbgfs_set(void *data, u64 val) +{ +	return pnv_eeh_dbgfs_set(data, 0xE10, val); +} + +static int pnv_eeh_inbB_dbgfs_get(void *data, u64 *val) +{ +	return pnv_eeh_dbgfs_get(data, 0xE10, val); +} + +DEFINE_SIMPLE_ATTRIBUTE(pnv_eeh_outb_dbgfs_ops, pnv_eeh_outb_dbgfs_get, +			pnv_eeh_outb_dbgfs_set, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(pnv_eeh_inbA_dbgfs_ops, pnv_eeh_inbA_dbgfs_get, +			pnv_eeh_inbA_dbgfs_set, "0x%llx\n"); +DEFINE_SIMPLE_ATTRIBUTE(pnv_eeh_inbB_dbgfs_ops, pnv_eeh_inbB_dbgfs_get, +			pnv_eeh_inbB_dbgfs_set, "0x%llx\n"); +#endif /* CONFIG_DEBUG_FS */ +  /** - * powernv_eeh_post_init - EEH platform dependent post initialization + * pnv_eeh_post_init - EEH platform dependent post initialization   *   * EEH platform dependent post initialization on powernv. When   * the function is called, the EEH PEs and devices should have   * been built. If the I/O cache staff has been built, EEH is   * ready to supply service.   */ -static int powernv_eeh_post_init(void) +static int pnv_eeh_post_init(void)  {  	struct pci_controller *hose;  	struct pnv_phb *phb;  	int ret = 0; +	/* Register OPAL event notifier */ +	if (!pnv_eeh_nb_init) { +		ret = opal_notifier_register(&pnv_eeh_nb); +		if (ret) { +			pr_warn("%s: Can't register OPAL event notifier (%d)\n", +				__func__, ret); +			return ret; +		} + +		pnv_eeh_nb_init = true; +	} +  	list_for_each_entry(hose, &hose_list, list_node) {  		phb = hose->private_data; -		if (phb->eeh_ops && phb->eeh_ops->post_init) { -			ret = phb->eeh_ops->post_init(hose); -			if (ret) -				break; -		} +		/* +		 * If EEH is enabled, we're going to rely on that. +		 * Otherwise, we restore to conventional mechanism +		 * to clear frozen PE during PCI config access. +		 */ +		if (eeh_enabled()) +			phb->flags |= PNV_PHB_FLAG_EEH; +		else +			phb->flags &= ~PNV_PHB_FLAG_EEH; + +		/* Create debugfs entries */ +#ifdef CONFIG_DEBUG_FS +		if (phb->has_dbgfs || !phb->dbgfs) +			continue; + +		phb->has_dbgfs = 1; +		debugfs_create_file("err_injct", 0200, +				    phb->dbgfs, hose, +				    &pnv_eeh_ei_fops); + +		debugfs_create_file("err_injct_outbound", 0600, +				    phb->dbgfs, hose, +				    &pnv_eeh_outb_dbgfs_ops); +		debugfs_create_file("err_injct_inboundA", 0600, +				    phb->dbgfs, hose, +				    &pnv_eeh_inbA_dbgfs_ops); +		debugfs_create_file("err_injct_inboundB", 0600, +				    phb->dbgfs, hose, +				    &pnv_eeh_inbB_dbgfs_ops); +#endif /* CONFIG_DEBUG_FS */  	} +  	return ret;  } +static int pnv_eeh_cap_start(struct pci_dn *pdn) +{ +	u32 status; + +	if (!pdn) +		return 0; + +	pnv_pci_cfg_read(pdn, PCI_STATUS, 2, &status); +	if (!(status & PCI_STATUS_CAP_LIST)) +		return 0; + +	return PCI_CAPABILITY_LIST; +} + +static int pnv_eeh_find_cap(struct pci_dn *pdn, int cap) +{ +	int pos = pnv_eeh_cap_start(pdn); +	int cnt = 48;   /* Maximal number of capabilities */ +	u32 id; + +	if (!pos) +		return 0; + +	while (cnt--) { +		pnv_pci_cfg_read(pdn, pos, 1, &pos); +		if (pos < 0x40) +			break; + +		pos &= ~3; +		pnv_pci_cfg_read(pdn, pos + PCI_CAP_LIST_ID, 1, &id); +		if (id == 0xff) +			break; + +		/* Found */ +		if (id == cap) +			return pos; + +		/* Next one */ +		pos += PCI_CAP_LIST_NEXT; +	} + +	return 0; +} + +static int pnv_eeh_find_ecap(struct pci_dn *pdn, int cap) +{ +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn); +	u32 header; +	int pos = 256, ttl = (4096 - 256) / 8; + +	if (!edev || !edev->pcie_cap) +		return 0; +	if (pnv_pci_cfg_read(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL) +		return 0; +	else if (!header) +		return 0; + +	while (ttl-- > 0) { +		if (PCI_EXT_CAP_ID(header) == cap && pos) +			return pos; + +		pos = PCI_EXT_CAP_NEXT(header); +		if (pos < 256) +			break; + +		if (pnv_pci_cfg_read(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL) +			break; +	} + +	return 0; +} +  /** - * powernv_eeh_dev_probe - Do probe on PCI device - * @dev: PCI device - * @flag: unused + * pnv_eeh_probe - Do probe on PCI device + * @pdn: PCI device node + * @data: unused   *   * When EEH module is installed during system boot, all PCI devices   * are checked one by one to see if it supports EEH. The function @@ -129,12 +375,12 @@ static int powernv_eeh_post_init(void)   * was possiblly triggered by EEH core, the binding between EEH device   * and the PCI device isn't built yet.   */ -static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag) +static void *pnv_eeh_probe(struct pci_dn *pdn, void *data)  { -	struct pci_controller *hose = pci_bus_to_host(dev->bus); +	struct pci_controller *hose = pdn->phb;  	struct pnv_phb *phb = hose->private_data; -	struct device_node *dn = pci_device_to_OF_node(dev); -	struct eeh_dev *edev = of_node_to_eeh_dev(dn); +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn); +	uint32_t pcie_flags;  	int ret;  	/* @@ -143,40 +389,42 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)  	 * the root bridge. So it's not reasonable to continue  	 * the probing.  	 */ -	if (!dn || !edev || edev->pe) -		return 0; +	if (!edev || edev->pe) +		return NULL;  	/* Skip for PCI-ISA bridge */ -	if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA) -		return 0; +	if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA) +		return NULL;  	/* Initialize eeh device */ -	edev->class_code = dev->class; +	edev->class_code = pdn->class_code;  	edev->mode	&= 0xFFFFFF00; -	if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) +	edev->pcix_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); +	edev->pcie_cap = pnv_eeh_find_cap(pdn, PCI_CAP_ID_EXP); +	edev->aer_cap  = pnv_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR); +	if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {  		edev->mode |= EEH_DEV_BRIDGE; -	edev->pcix_cap = pci_find_capability(dev, PCI_CAP_ID_PCIX); -	if (pci_is_pcie(dev)) { -		edev->pcie_cap = pci_pcie_cap(dev); - -		if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) -			edev->mode |= EEH_DEV_ROOT_PORT; -		else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) -			edev->mode |= EEH_DEV_DS_PORT; - -		edev->aer_cap = pci_find_ext_capability(dev, -							PCI_EXT_CAP_ID_ERR); +		if (edev->pcie_cap) { +			pnv_pci_cfg_read(pdn, edev->pcie_cap + PCI_EXP_FLAGS, +					 2, &pcie_flags); +			pcie_flags = (pcie_flags & PCI_EXP_FLAGS_TYPE) >> 4; +			if (pcie_flags == PCI_EXP_TYPE_ROOT_PORT) +				edev->mode |= EEH_DEV_ROOT_PORT; +			else if (pcie_flags == PCI_EXP_TYPE_DOWNSTREAM) +				edev->mode |= EEH_DEV_DS_PORT; +		}  	} -	edev->config_addr	= ((dev->bus->number << 8) | dev->devfn); -	edev->pe_config_addr	= phb->bdfn_to_pe(phb, dev->bus, dev->devfn & 0xff); +	edev->config_addr    = (pdn->busno << 8) | (pdn->devfn); +	edev->pe_config_addr = phb->ioda.pe_rmap[edev->config_addr];  	/* Create PE */  	ret = eeh_add_to_parent_pe(edev);  	if (ret) { -		pr_warn("%s: Can't add PCI dev %s to parent PE (%d)\n", -			__func__, pci_name(dev), ret); -		return ret; +		pr_warn("%s: Can't add PCI dev %04x:%02x:%02x.%01x to parent PE (%d)\n", +			__func__, hose->global_number, pdn->busno, +			PCI_SLOT(pdn->devfn), PCI_FUNC(pdn->devfn), ret); +		return NULL;  	}  	/* @@ -195,8 +443,10 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)  	 * Broadcom Austin 4-ports NICs (14e4:1657)  	 * Broadcom Shiner 2-ports 10G NICs (14e4:168e)  	 */ -	if ((dev->vendor == PCI_VENDOR_ID_BROADCOM && dev->device == 0x1657) || -	    (dev->vendor == PCI_VENDOR_ID_BROADCOM && dev->device == 0x168e)) +	if ((pdn->vendor_id == PCI_VENDOR_ID_BROADCOM && +	     pdn->device_id == 0x1657) || +	    (pdn->vendor_id == PCI_VENDOR_ID_BROADCOM && +	     pdn->device_id == 0x168e))  		edev->pe->state |= EEH_PE_CFG_RESTRICTED;  	/* @@ -206,7 +456,8 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)  	 * to PE reset.  	 */  	if (!edev->pe->bus) -		edev->pe->bus = dev->bus; +		edev->pe->bus = pci_find_bus(hose->global_number, +					     pdn->busno);  	/*  	 * Enable EEH explicitly so that we will do EEH check @@ -217,11 +468,11 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)  	/* Save memory bars */  	eeh_save_bars(edev); -	return 0; +	return NULL;  }  /** - * powernv_eeh_set_option - Initialize EEH or MMIO/DMA reenable + * pnv_eeh_set_option - Initialize EEH or MMIO/DMA reenable   * @pe: EEH PE   * @option: operation to be issued   * @@ -229,36 +480,236 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)   * Currently, following options are support according to PAPR:   * Enable EEH, Disable EEH, Enable MMIO and Enable DMA   */ -static int powernv_eeh_set_option(struct eeh_pe *pe, int option) +static int pnv_eeh_set_option(struct eeh_pe *pe, int option)  {  	struct pci_controller *hose = pe->phb;  	struct pnv_phb *phb = hose->private_data; -	int ret = -EEXIST; +	bool freeze_pe = false; +	int opt, ret = 0; +	s64 rc; + +	/* Sanity check on option */ +	switch (option) { +	case EEH_OPT_DISABLE: +		return -EPERM; +	case EEH_OPT_ENABLE: +		return 0; +	case EEH_OPT_THAW_MMIO: +		opt = OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO; +		break; +	case EEH_OPT_THAW_DMA: +		opt = OPAL_EEH_ACTION_CLEAR_FREEZE_DMA; +		break; +	case EEH_OPT_FREEZE_PE: +		freeze_pe = true; +		opt = OPAL_EEH_ACTION_SET_FREEZE_ALL; +		break; +	default: +		pr_warn("%s: Invalid option %d\n", __func__, option); +		return -EINVAL; +	} -	/* -	 * What we need do is pass it down for hardware -	 * implementation to handle it. -	 */ -	if (phb->eeh_ops && phb->eeh_ops->set_option) -		ret = phb->eeh_ops->set_option(pe, option); +	/* If PHB supports compound PE, to handle it */ +	if (freeze_pe) { +		if (phb->freeze_pe) { +			phb->freeze_pe(phb, pe->addr); +		} else { +			rc = opal_pci_eeh_freeze_set(phb->opal_id, +						     pe->addr, opt); +			if (rc != OPAL_SUCCESS) { +				pr_warn("%s: Failure %lld freezing " +					"PHB#%x-PE#%x\n", +					__func__, rc, +					phb->hose->global_number, pe->addr); +				ret = -EIO; +			} +		} +	} else { +		if (phb->unfreeze_pe) { +			ret = phb->unfreeze_pe(phb, pe->addr, opt); +		} else { +			rc = opal_pci_eeh_freeze_clear(phb->opal_id, +						       pe->addr, opt); +			if (rc != OPAL_SUCCESS) { +				pr_warn("%s: Failure %lld enable %d " +					"for PHB#%x-PE#%x\n", +					__func__, rc, option, +					phb->hose->global_number, pe->addr); +				ret = -EIO; +			} +		} +	}  	return ret;  }  /** - * powernv_eeh_get_pe_addr - Retrieve PE address + * pnv_eeh_get_pe_addr - Retrieve PE address   * @pe: EEH PE   *   * Retrieve the PE address according to the given tranditional   * PCI BDF (Bus/Device/Function) address.   */ -static int powernv_eeh_get_pe_addr(struct eeh_pe *pe) +static int pnv_eeh_get_pe_addr(struct eeh_pe *pe)  {  	return pe->addr;  } +static void pnv_eeh_get_phb_diag(struct eeh_pe *pe) +{ +	struct pnv_phb *phb = pe->phb->private_data; +	s64 rc; + +	rc = opal_pci_get_phb_diag_data2(phb->opal_id, pe->data, +					 PNV_PCI_DIAG_BUF_SIZE); +	if (rc != OPAL_SUCCESS) +		pr_warn("%s: Failure %lld getting PHB#%x diag-data\n", +			__func__, rc, pe->phb->global_number); +} + +static int pnv_eeh_get_phb_state(struct eeh_pe *pe) +{ +	struct pnv_phb *phb = pe->phb->private_data; +	u8 fstate; +	__be16 pcierr; +	s64 rc; +	int result = 0; + +	rc = opal_pci_eeh_freeze_status(phb->opal_id, +					pe->addr, +					&fstate, +					&pcierr, +					NULL); +	if (rc != OPAL_SUCCESS) { +		pr_warn("%s: Failure %lld getting PHB#%x state\n", +			__func__, rc, phb->hose->global_number); +		return EEH_STATE_NOT_SUPPORT; +	} + +	/* +	 * Check PHB state. If the PHB is frozen for the +	 * first time, to dump the PHB diag-data. +	 */ +	if (be16_to_cpu(pcierr) != OPAL_EEH_PHB_ERROR) { +		result = (EEH_STATE_MMIO_ACTIVE  | +			  EEH_STATE_DMA_ACTIVE   | +			  EEH_STATE_MMIO_ENABLED | +			  EEH_STATE_DMA_ENABLED); +	} else if (!(pe->state & EEH_PE_ISOLATED)) { +		eeh_pe_state_mark(pe, EEH_PE_ISOLATED); +		pnv_eeh_get_phb_diag(pe); + +		if (eeh_has_flag(EEH_EARLY_DUMP_LOG)) +			pnv_pci_dump_phb_diag_data(pe->phb, pe->data); +	} + +	return result; +} + +static int pnv_eeh_get_pe_state(struct eeh_pe *pe) +{ +	struct pnv_phb *phb = pe->phb->private_data; +	u8 fstate; +	__be16 pcierr; +	s64 rc; +	int result; + +	/* +	 * We don't clobber hardware frozen state until PE +	 * reset is completed. In order to keep EEH core +	 * moving forward, we have to return operational +	 * state during PE reset. +	 */ +	if (pe->state & EEH_PE_RESET) { +		result = (EEH_STATE_MMIO_ACTIVE  | +			  EEH_STATE_DMA_ACTIVE   | +			  EEH_STATE_MMIO_ENABLED | +			  EEH_STATE_DMA_ENABLED); +		return result; +	} + +	/* +	 * Fetch PE state from hardware. If the PHB +	 * supports compound PE, let it handle that. +	 */ +	if (phb->get_pe_state) { +		fstate = phb->get_pe_state(phb, pe->addr); +	} else { +		rc = opal_pci_eeh_freeze_status(phb->opal_id, +						pe->addr, +						&fstate, +						&pcierr, +						NULL); +		if (rc != OPAL_SUCCESS) { +			pr_warn("%s: Failure %lld getting PHB#%x-PE%x state\n", +				__func__, rc, phb->hose->global_number, +				pe->addr); +			return EEH_STATE_NOT_SUPPORT; +		} +	} + +	/* Figure out state */ +	switch (fstate) { +	case OPAL_EEH_STOPPED_NOT_FROZEN: +		result = (EEH_STATE_MMIO_ACTIVE  | +			  EEH_STATE_DMA_ACTIVE   | +			  EEH_STATE_MMIO_ENABLED | +			  EEH_STATE_DMA_ENABLED); +		break; +	case OPAL_EEH_STOPPED_MMIO_FREEZE: +		result = (EEH_STATE_DMA_ACTIVE | +			  EEH_STATE_DMA_ENABLED); +		break; +	case OPAL_EEH_STOPPED_DMA_FREEZE: +		result = (EEH_STATE_MMIO_ACTIVE | +			  EEH_STATE_MMIO_ENABLED); +		break; +	case OPAL_EEH_STOPPED_MMIO_DMA_FREEZE: +		result = 0; +		break; +	case OPAL_EEH_STOPPED_RESET: +		result = EEH_STATE_RESET_ACTIVE; +		break; +	case OPAL_EEH_STOPPED_TEMP_UNAVAIL: +		result = EEH_STATE_UNAVAILABLE; +		break; +	case OPAL_EEH_STOPPED_PERM_UNAVAIL: +		result = EEH_STATE_NOT_SUPPORT; +		break; +	default: +		result = EEH_STATE_NOT_SUPPORT; +		pr_warn("%s: Invalid PHB#%x-PE#%x state %x\n", +			__func__, phb->hose->global_number, +			pe->addr, fstate); +	} + +	/* +	 * If PHB supports compound PE, to freeze all +	 * slave PEs for consistency. +	 * +	 * If the PE is switching to frozen state for the +	 * first time, to dump the PHB diag-data. +	 */ +	if (!(result & EEH_STATE_NOT_SUPPORT) && +	    !(result & EEH_STATE_UNAVAILABLE) && +	    !(result & EEH_STATE_MMIO_ACTIVE) && +	    !(result & EEH_STATE_DMA_ACTIVE)  && +	    !(pe->state & EEH_PE_ISOLATED)) { +		if (phb->freeze_pe) +			phb->freeze_pe(phb, pe->addr); + +		eeh_pe_state_mark(pe, EEH_PE_ISOLATED); +		pnv_eeh_get_phb_diag(pe); + +		if (eeh_has_flag(EEH_EARLY_DUMP_LOG)) +			pnv_pci_dump_phb_diag_data(pe->phb, pe->data); +	} + +	return result; +} +  /** - * powernv_eeh_get_state - Retrieve PE state + * pnv_eeh_get_state - Retrieve PE state   * @pe: EEH PE   * @delay: delay while PE state is temporarily unavailable   * @@ -267,64 +718,279 @@ static int powernv_eeh_get_pe_addr(struct eeh_pe *pe)   * we prefer passing down to hardware implementation to handle   * it.   */ -static int powernv_eeh_get_state(struct eeh_pe *pe, int *delay) +static int pnv_eeh_get_state(struct eeh_pe *pe, int *delay) +{ +	int ret; + +	if (pe->type & EEH_PE_PHB) +		ret = pnv_eeh_get_phb_state(pe); +	else +		ret = pnv_eeh_get_pe_state(pe); + +	if (!delay) +		return ret; + +	/* +	 * If the PE state is temporarily unavailable, +	 * to inform the EEH core delay for default +	 * period (1 second) +	 */ +	*delay = 0; +	if (ret & EEH_STATE_UNAVAILABLE) +		*delay = 1000; + +	return ret; +} + +static s64 pnv_eeh_phb_poll(struct pnv_phb *phb) +{ +	s64 rc = OPAL_HARDWARE; + +	while (1) { +		rc = opal_pci_poll(phb->opal_id); +		if (rc <= 0) +			break; + +		if (system_state < SYSTEM_RUNNING) +			udelay(1000 * rc); +		else +			msleep(rc); +	} + +	return rc; +} + +int pnv_eeh_phb_reset(struct pci_controller *hose, int option)  { -	struct pci_controller *hose = pe->phb;  	struct pnv_phb *phb = hose->private_data; -	int ret = EEH_STATE_NOT_SUPPORT; +	s64 rc = OPAL_HARDWARE; + +	pr_debug("%s: Reset PHB#%x, option=%d\n", +		 __func__, hose->global_number, option); + +	/* Issue PHB complete reset request */ +	if (option == EEH_RESET_FUNDAMENTAL || +	    option == EEH_RESET_HOT) +		rc = opal_pci_reset(phb->opal_id, +				    OPAL_RESET_PHB_COMPLETE, +				    OPAL_ASSERT_RESET); +	else if (option == EEH_RESET_DEACTIVATE) +		rc = opal_pci_reset(phb->opal_id, +				    OPAL_RESET_PHB_COMPLETE, +				    OPAL_DEASSERT_RESET); +	if (rc < 0) +		goto out; -	if (phb->eeh_ops && phb->eeh_ops->get_state) { -		ret = phb->eeh_ops->get_state(pe); +	/* +	 * Poll state of the PHB until the request is done +	 * successfully. The PHB reset is usually PHB complete +	 * reset followed by hot reset on root bus. So we also +	 * need the PCI bus settlement delay. +	 */ +	rc = pnv_eeh_phb_poll(phb); +	if (option == EEH_RESET_DEACTIVATE) { +		if (system_state < SYSTEM_RUNNING) +			udelay(1000 * EEH_PE_RST_SETTLE_TIME); +		else +			msleep(EEH_PE_RST_SETTLE_TIME); +	} +out: +	if (rc != OPAL_SUCCESS) +		return -EIO; -		/* -		 * If the PE state is temporarily unavailable, -		 * to inform the EEH core delay for default -		 * period (1 second) -		 */ -		if (delay) { -			*delay = 0; -			if (ret & EEH_STATE_UNAVAILABLE) -				*delay = 1000; +	return 0; +} + +static int pnv_eeh_root_reset(struct pci_controller *hose, int option) +{ +	struct pnv_phb *phb = hose->private_data; +	s64 rc = OPAL_HARDWARE; + +	pr_debug("%s: Reset PHB#%x, option=%d\n", +		 __func__, hose->global_number, option); + +	/* +	 * During the reset deassert time, we needn't care +	 * the reset scope because the firmware does nothing +	 * for fundamental or hot reset during deassert phase. +	 */ +	if (option == EEH_RESET_FUNDAMENTAL) +		rc = opal_pci_reset(phb->opal_id, +				    OPAL_RESET_PCI_FUNDAMENTAL, +				    OPAL_ASSERT_RESET); +	else if (option == EEH_RESET_HOT) +		rc = opal_pci_reset(phb->opal_id, +				    OPAL_RESET_PCI_HOT, +				    OPAL_ASSERT_RESET); +	else if (option == EEH_RESET_DEACTIVATE) +		rc = opal_pci_reset(phb->opal_id, +				    OPAL_RESET_PCI_HOT, +				    OPAL_DEASSERT_RESET); +	if (rc < 0) +		goto out; + +	/* Poll state of the PHB until the request is done */ +	rc = pnv_eeh_phb_poll(phb); +	if (option == EEH_RESET_DEACTIVATE) +		msleep(EEH_PE_RST_SETTLE_TIME); +out: +	if (rc != OPAL_SUCCESS) +		return -EIO; + +	return 0; +} + +static int pnv_eeh_bridge_reset(struct pci_dev *dev, int option) +{ +	struct pci_dn *pdn = pci_get_pdn_by_devfn(dev->bus, dev->devfn); +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn); +	int aer = edev ? edev->aer_cap : 0; +	u32 ctrl; + +	pr_debug("%s: Reset PCI bus %04x:%02x with option %d\n", +		 __func__, pci_domain_nr(dev->bus), +		 dev->bus->number, option); + +	switch (option) { +	case EEH_RESET_FUNDAMENTAL: +	case EEH_RESET_HOT: +		/* Don't report linkDown event */ +		if (aer) { +			eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK, +					     4, &ctrl); +			ctrl |= PCI_ERR_UNC_SURPDN; +			eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK, +					      4, ctrl);  		} + +		eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl); +		ctrl |= PCI_BRIDGE_CTL_BUS_RESET; +		eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl); + +		msleep(EEH_PE_RST_HOLD_TIME); +		break; +	case EEH_RESET_DEACTIVATE: +		eeh_ops->read_config(pdn, PCI_BRIDGE_CONTROL, 2, &ctrl); +		ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; +		eeh_ops->write_config(pdn, PCI_BRIDGE_CONTROL, 2, ctrl); + +		msleep(EEH_PE_RST_SETTLE_TIME); + +		/* Continue reporting linkDown event */ +		if (aer) { +			eeh_ops->read_config(pdn, aer + PCI_ERR_UNCOR_MASK, +					     4, &ctrl); +			ctrl &= ~PCI_ERR_UNC_SURPDN; +			eeh_ops->write_config(pdn, aer + PCI_ERR_UNCOR_MASK, +					      4, ctrl); +		} + +		break;  	} -	return ret; +	return 0; +} + +void pnv_pci_reset_secondary_bus(struct pci_dev *dev) +{ +	struct pci_controller *hose; + +	if (pci_is_root_bus(dev->bus)) { +		hose = pci_bus_to_host(dev->bus); +		pnv_eeh_root_reset(hose, EEH_RESET_HOT); +		pnv_eeh_root_reset(hose, EEH_RESET_DEACTIVATE); +	} else { +		pnv_eeh_bridge_reset(dev, EEH_RESET_HOT); +		pnv_eeh_bridge_reset(dev, EEH_RESET_DEACTIVATE); +	}  }  /** - * powernv_eeh_reset - Reset the specified PE + * pnv_eeh_reset - Reset the specified PE   * @pe: EEH PE   * @option: reset option   * - * Reset the specified PE + * Do reset on the indicated PE. For PCI bus sensitive PE, + * we need to reset the parent p2p bridge. The PHB has to + * be reinitialized if the p2p bridge is root bridge. For + * PCI device sensitive PE, we will try to reset the device + * through FLR. For now, we don't have OPAL APIs to do HARD + * reset yet, so all reset would be SOFT (HOT) reset.   */ -static int powernv_eeh_reset(struct eeh_pe *pe, int option) +static int pnv_eeh_reset(struct eeh_pe *pe, int option)  {  	struct pci_controller *hose = pe->phb; -	struct pnv_phb *phb = hose->private_data; -	int ret = -EEXIST; +	struct pci_bus *bus; +	int ret; + +	/* +	 * For PHB reset, we always have complete reset. For those PEs whose +	 * primary bus derived from root complex (root bus) or root port +	 * (usually bus#1), we apply hot or fundamental reset on the root port. +	 * For other PEs, we always have hot reset on the PE primary bus. +	 * +	 * Here, we have different design to pHyp, which always clear the +	 * frozen state during PE reset. However, the good idea here from +	 * benh is to keep frozen state before we get PE reset done completely +	 * (until BAR restore). With the frozen state, HW drops illegal IO +	 * or MMIO access, which can incur recrusive frozen PE during PE +	 * reset. The side effect is that EEH core has to clear the frozen +	 * state explicitly after BAR restore. +	 */ +	if (pe->type & EEH_PE_PHB) { +		ret = pnv_eeh_phb_reset(hose, option); +	} else { +		struct pnv_phb *phb; +		s64 rc; -	if (phb->eeh_ops && phb->eeh_ops->reset) -		ret = phb->eeh_ops->reset(pe, option); +		/* +		 * The frozen PE might be caused by PAPR error injection +		 * registers, which are expected to be cleared after hitting +		 * frozen PE as stated in the hardware spec. Unfortunately, +		 * that's not true on P7IOC. So we have to clear it manually +		 * to avoid recursive EEH errors during recovery. +		 */ +		phb = hose->private_data; +		if (phb->model == PNV_PHB_MODEL_P7IOC && +		    (option == EEH_RESET_HOT || +		    option == EEH_RESET_FUNDAMENTAL)) { +			rc = opal_pci_reset(phb->opal_id, +					    OPAL_RESET_PHB_ERROR, +					    OPAL_ASSERT_RESET); +			if (rc != OPAL_SUCCESS) { +				pr_warn("%s: Failure %lld clearing " +					"error injection registers\n", +					__func__, rc); +				return -EIO; +			} +		} + +		bus = eeh_pe_bus_get(pe); +		if (pci_is_root_bus(bus) || +			pci_is_root_bus(bus->parent)) +			ret = pnv_eeh_root_reset(hose, option); +		else +			ret = pnv_eeh_bridge_reset(bus->self, option); +	}  	return ret;  }  /** - * powernv_eeh_wait_state - Wait for PE state + * pnv_eeh_wait_state - Wait for PE state   * @pe: EEH PE   * @max_wait: maximal period in microsecond   *   * Wait for the state of associated PE. It might take some time   * to retrieve the PE's state.   */ -static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait) +static int pnv_eeh_wait_state(struct eeh_pe *pe, int max_wait)  {  	int ret;  	int mwait;  	while (1) { -		ret = powernv_eeh_get_state(pe, &mwait); +		ret = pnv_eeh_get_state(pe, &mwait);  		/*  		 * If the PE's state is temporarily unavailable, @@ -348,7 +1014,7 @@ static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait)  }  /** - * powernv_eeh_get_log - Retrieve error log + * pnv_eeh_get_log - Retrieve error log   * @pe: EEH PE   * @severity: temporary or permanent error log   * @drv_log: driver log to be combined with retrieved error log @@ -356,41 +1022,30 @@ static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait)   *   * Retrieve the temporary or permanent error from the PE.   */ -static int powernv_eeh_get_log(struct eeh_pe *pe, int severity, -			       char *drv_log, unsigned long len) +static int pnv_eeh_get_log(struct eeh_pe *pe, int severity, +			   char *drv_log, unsigned long len)  { -	struct pci_controller *hose = pe->phb; -	struct pnv_phb *phb = hose->private_data; -	int ret = -EEXIST; +	if (!eeh_has_flag(EEH_EARLY_DUMP_LOG)) +		pnv_pci_dump_phb_diag_data(pe->phb, pe->data); -	if (phb->eeh_ops && phb->eeh_ops->get_log) -		ret = phb->eeh_ops->get_log(pe, severity, drv_log, len); - -	return ret; +	return 0;  }  /** - * powernv_eeh_configure_bridge - Configure PCI bridges in the indicated PE + * pnv_eeh_configure_bridge - Configure PCI bridges in the indicated PE   * @pe: EEH PE   *   * The function will be called to reconfigure the bridges included   * in the specified PE so that the mulfunctional PE would be recovered   * again.   */ -static int powernv_eeh_configure_bridge(struct eeh_pe *pe) +static int pnv_eeh_configure_bridge(struct eeh_pe *pe)  { -	struct pci_controller *hose = pe->phb; -	struct pnv_phb *phb = hose->private_data; -	int ret = 0; - -	if (phb->eeh_ops && phb->eeh_ops->configure_bridge) -		ret = phb->eeh_ops->configure_bridge(pe); - -	return ret; +	return 0;  }  /** - * powernv_pe_err_inject - Inject specified error to the indicated PE + * pnv_pe_err_inject - Inject specified error to the indicated PE   * @pe: the indicated PE   * @type: error type   * @func: specific error type @@ -401,22 +1056,52 @@ static int powernv_eeh_configure_bridge(struct eeh_pe *pe)   * determined by @type and @func, to the indicated PE for   * testing purpose.   */ -static int powernv_eeh_err_inject(struct eeh_pe *pe, int type, int func, -				  unsigned long addr, unsigned long mask) +static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func, +			      unsigned long addr, unsigned long mask)  {  	struct pci_controller *hose = pe->phb;  	struct pnv_phb *phb = hose->private_data; -	int ret = -EEXIST; +	s64 rc; + +	/* Sanity check on error type */ +	if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR && +	    type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) { +		pr_warn("%s: Invalid error type %d\n", +			__func__, type); +		return -ERANGE; +	} -	if (phb->eeh_ops && phb->eeh_ops->err_inject) -		ret = phb->eeh_ops->err_inject(pe, type, func, addr, mask); +	if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR || +	    func > OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET) { +		pr_warn("%s: Invalid error function %d\n", +			__func__, func); +		return -ERANGE; +	} -	return ret; +	/* Firmware supports error injection ? */ +	if (!opal_check_token(OPAL_PCI_ERR_INJECT)) { +		pr_warn("%s: Firmware doesn't support error injection\n", +			__func__); +		return -ENXIO; +	} + +	/* Do error injection */ +	rc = opal_pci_err_inject(phb->opal_id, pe->addr, +				 type, func, addr, mask); +	if (rc != OPAL_SUCCESS) { +		pr_warn("%s: Failure %lld injecting error " +			"%d-%d to PHB#%x-PE#%x\n", +			__func__, rc, type, func, +			hose->global_number, pe->addr); +		return -EIO; +	} + +	return 0;  } -static inline bool powernv_eeh_cfg_blocked(struct device_node *dn) +static inline bool pnv_eeh_cfg_blocked(struct pci_dn *pdn)  { -	struct eeh_dev *edev = of_node_to_eeh_dev(dn); +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn);  	if (!edev || !edev->pe)  		return false; @@ -427,51 +1112,377 @@ static inline bool powernv_eeh_cfg_blocked(struct device_node *dn)  	return false;  } -static int powernv_eeh_read_config(struct device_node *dn, -				   int where, int size, u32 *val) +static int pnv_eeh_read_config(struct pci_dn *pdn, +			       int where, int size, u32 *val)  { -	if (powernv_eeh_cfg_blocked(dn)) { +	if (!pdn) +		return PCIBIOS_DEVICE_NOT_FOUND; + +	if (pnv_eeh_cfg_blocked(pdn)) {  		*val = 0xFFFFFFFF;  		return PCIBIOS_SET_FAILED;  	} -	return pnv_pci_cfg_read(dn, where, size, val); +	return pnv_pci_cfg_read(pdn, where, size, val);  } -static int powernv_eeh_write_config(struct device_node *dn, -				    int where, int size, u32 val) +static int pnv_eeh_write_config(struct pci_dn *pdn, +				int where, int size, u32 val)  { -	if (powernv_eeh_cfg_blocked(dn)) +	if (!pdn) +		return PCIBIOS_DEVICE_NOT_FOUND; + +	if (pnv_eeh_cfg_blocked(pdn))  		return PCIBIOS_SET_FAILED; -	return pnv_pci_cfg_write(dn, where, size, val); +	return pnv_pci_cfg_write(pdn, where, size, val); +} + +static void pnv_eeh_dump_hub_diag_common(struct OpalIoP7IOCErrorData *data) +{ +	/* GEM */ +	if (data->gemXfir || data->gemRfir || +	    data->gemRirqfir || data->gemMask || data->gemRwof) +		pr_info("  GEM: %016llx %016llx %016llx %016llx %016llx\n", +			be64_to_cpu(data->gemXfir), +			be64_to_cpu(data->gemRfir), +			be64_to_cpu(data->gemRirqfir), +			be64_to_cpu(data->gemMask), +			be64_to_cpu(data->gemRwof)); + +	/* LEM */ +	if (data->lemFir || data->lemErrMask || +	    data->lemAction0 || data->lemAction1 || data->lemWof) +		pr_info("  LEM: %016llx %016llx %016llx %016llx %016llx\n", +			be64_to_cpu(data->lemFir), +			be64_to_cpu(data->lemErrMask), +			be64_to_cpu(data->lemAction0), +			be64_to_cpu(data->lemAction1), +			be64_to_cpu(data->lemWof)); +} + +static void pnv_eeh_get_and_dump_hub_diag(struct pci_controller *hose) +{ +	struct pnv_phb *phb = hose->private_data; +	struct OpalIoP7IOCErrorData *data = &phb->diag.hub_diag; +	long rc; + +	rc = opal_pci_get_hub_diag_data(phb->hub_id, data, sizeof(*data)); +	if (rc != OPAL_SUCCESS) { +		pr_warn("%s: Failed to get HUB#%llx diag-data (%ld)\n", +			__func__, phb->hub_id, rc); +		return; +	} + +	switch (data->type) { +	case OPAL_P7IOC_DIAG_TYPE_RGC: +		pr_info("P7IOC diag-data for RGC\n\n"); +		pnv_eeh_dump_hub_diag_common(data); +		if (data->rgc.rgcStatus || data->rgc.rgcLdcp) +			pr_info("  RGC: %016llx %016llx\n", +				be64_to_cpu(data->rgc.rgcStatus), +				be64_to_cpu(data->rgc.rgcLdcp)); +		break; +	case OPAL_P7IOC_DIAG_TYPE_BI: +		pr_info("P7IOC diag-data for BI %s\n\n", +			data->bi.biDownbound ? "Downbound" : "Upbound"); +		pnv_eeh_dump_hub_diag_common(data); +		if (data->bi.biLdcp0 || data->bi.biLdcp1 || +		    data->bi.biLdcp2 || data->bi.biFenceStatus) +			pr_info("  BI:  %016llx %016llx %016llx %016llx\n", +				be64_to_cpu(data->bi.biLdcp0), +				be64_to_cpu(data->bi.biLdcp1), +				be64_to_cpu(data->bi.biLdcp2), +				be64_to_cpu(data->bi.biFenceStatus)); +		break; +	case OPAL_P7IOC_DIAG_TYPE_CI: +		pr_info("P7IOC diag-data for CI Port %d\n\n", +			data->ci.ciPort); +		pnv_eeh_dump_hub_diag_common(data); +		if (data->ci.ciPortStatus || data->ci.ciPortLdcp) +			pr_info("  CI:  %016llx %016llx\n", +				be64_to_cpu(data->ci.ciPortStatus), +				be64_to_cpu(data->ci.ciPortLdcp)); +		break; +	case OPAL_P7IOC_DIAG_TYPE_MISC: +		pr_info("P7IOC diag-data for MISC\n\n"); +		pnv_eeh_dump_hub_diag_common(data); +		break; +	case OPAL_P7IOC_DIAG_TYPE_I2C: +		pr_info("P7IOC diag-data for I2C\n\n"); +		pnv_eeh_dump_hub_diag_common(data); +		break; +	default: +		pr_warn("%s: Invalid type of HUB#%llx diag-data (%d)\n", +			__func__, phb->hub_id, data->type); +	} +} + +static int pnv_eeh_get_pe(struct pci_controller *hose, +			  u16 pe_no, struct eeh_pe **pe) +{ +	struct pnv_phb *phb = hose->private_data; +	struct pnv_ioda_pe *pnv_pe; +	struct eeh_pe *dev_pe; +	struct eeh_dev edev; + +	/* +	 * If PHB supports compound PE, to fetch +	 * the master PE because slave PE is invisible +	 * to EEH core. +	 */ +	pnv_pe = &phb->ioda.pe_array[pe_no]; +	if (pnv_pe->flags & PNV_IODA_PE_SLAVE) { +		pnv_pe = pnv_pe->master; +		WARN_ON(!pnv_pe || +			!(pnv_pe->flags & PNV_IODA_PE_MASTER)); +		pe_no = pnv_pe->pe_number; +	} + +	/* Find the PE according to PE# */ +	memset(&edev, 0, sizeof(struct eeh_dev)); +	edev.phb = hose; +	edev.pe_config_addr = pe_no; +	dev_pe = eeh_pe_get(&edev); +	if (!dev_pe) +		return -EEXIST; + +	/* Freeze the (compound) PE */ +	*pe = dev_pe; +	if (!(dev_pe->state & EEH_PE_ISOLATED)) +		phb->freeze_pe(phb, pe_no); + +	/* +	 * At this point, we're sure the (compound) PE should +	 * have been frozen. However, we still need poke until +	 * hitting the frozen PE on top level. +	 */ +	dev_pe = dev_pe->parent; +	while (dev_pe && !(dev_pe->type & EEH_PE_PHB)) { +		int ret; +		int active_flags = (EEH_STATE_MMIO_ACTIVE | +				    EEH_STATE_DMA_ACTIVE); + +		ret = eeh_ops->get_state(dev_pe, NULL); +		if (ret <= 0 || (ret & active_flags) == active_flags) { +			dev_pe = dev_pe->parent; +			continue; +		} + +		/* Frozen parent PE */ +		*pe = dev_pe; +		if (!(dev_pe->state & EEH_PE_ISOLATED)) +			phb->freeze_pe(phb, dev_pe->addr); + +		/* Next one */ +		dev_pe = dev_pe->parent; +	} + +	return 0;  }  /** - * powernv_eeh_next_error - Retrieve next EEH error to handle + * pnv_eeh_next_error - Retrieve next EEH error to handle   * @pe: Affected PE   * - * Using OPAL API, to retrieve next EEH error for EEH core to handle + * The function is expected to be called by EEH core while it gets + * special EEH event (without binding PE). The function calls to + * OPAL APIs for next error to handle. The informational error is + * handled internally by platform. However, the dead IOC, dead PHB, + * fenced PHB and frozen PE should be handled by EEH core eventually.   */ -static int powernv_eeh_next_error(struct eeh_pe **pe) +static int pnv_eeh_next_error(struct eeh_pe **pe)  {  	struct pci_controller *hose; -	struct pnv_phb *phb = NULL; +	struct pnv_phb *phb; +	struct eeh_pe *phb_pe, *parent_pe; +	__be64 frozen_pe_no; +	__be16 err_type, severity; +	int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE); +	long rc; +	int state, ret = EEH_NEXT_ERR_NONE; + +	/* +	 * While running here, it's safe to purge the event queue. +	 * And we should keep the cached OPAL notifier event sychronized +	 * between the kernel and firmware. +	 */ +	eeh_remove_event(NULL, false); +	opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul);  	list_for_each_entry(hose, &hose_list, list_node) { +		/* +		 * If the subordinate PCI buses of the PHB has been +		 * removed or is exactly under error recovery, we +		 * needn't take care of it any more. +		 */  		phb = hose->private_data; -		break; -	} +		phb_pe = eeh_phb_pe_get(hose); +		if (!phb_pe || (phb_pe->state & EEH_PE_ISOLATED)) +			continue; + +		rc = opal_pci_next_error(phb->opal_id, +					 &frozen_pe_no, &err_type, &severity); +		if (rc != OPAL_SUCCESS) { +			pr_devel("%s: Invalid return value on " +				 "PHB#%x (0x%lx) from opal_pci_next_error", +				 __func__, hose->global_number, rc); +			continue; +		} + +		/* If the PHB doesn't have error, stop processing */ +		if (be16_to_cpu(err_type) == OPAL_EEH_NO_ERROR || +		    be16_to_cpu(severity) == OPAL_EEH_SEV_NO_ERROR) { +			pr_devel("%s: No error found on PHB#%x\n", +				 __func__, hose->global_number); +			continue; +		} + +		/* +		 * Processing the error. We're expecting the error with +		 * highest priority reported upon multiple errors on the +		 * specific PHB. +		 */ +		pr_devel("%s: Error (%d, %d, %llu) on PHB#%x\n", +			__func__, be16_to_cpu(err_type), +			be16_to_cpu(severity), be64_to_cpu(frozen_pe_no), +			hose->global_number); +		switch (be16_to_cpu(err_type)) { +		case OPAL_EEH_IOC_ERROR: +			if (be16_to_cpu(severity) == OPAL_EEH_SEV_IOC_DEAD) { +				pr_err("EEH: dead IOC detected\n"); +				ret = EEH_NEXT_ERR_DEAD_IOC; +			} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) { +				pr_info("EEH: IOC informative error " +					"detected\n"); +				pnv_eeh_get_and_dump_hub_diag(hose); +				ret = EEH_NEXT_ERR_NONE; +			} + +			break; +		case OPAL_EEH_PHB_ERROR: +			if (be16_to_cpu(severity) == OPAL_EEH_SEV_PHB_DEAD) { +				*pe = phb_pe; +				pr_err("EEH: dead PHB#%x detected, " +				       "location: %s\n", +					hose->global_number, +					eeh_pe_loc_get(phb_pe)); +				ret = EEH_NEXT_ERR_DEAD_PHB; +			} else if (be16_to_cpu(severity) == +				   OPAL_EEH_SEV_PHB_FENCED) { +				*pe = phb_pe; +				pr_err("EEH: Fenced PHB#%x detected, " +				       "location: %s\n", +					hose->global_number, +					eeh_pe_loc_get(phb_pe)); +				ret = EEH_NEXT_ERR_FENCED_PHB; +			} else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) { +				pr_info("EEH: PHB#%x informative error " +					"detected, location: %s\n", +					hose->global_number, +					eeh_pe_loc_get(phb_pe)); +				pnv_eeh_get_phb_diag(phb_pe); +				pnv_pci_dump_phb_diag_data(hose, phb_pe->data); +				ret = EEH_NEXT_ERR_NONE; +			} + +			break; +		case OPAL_EEH_PE_ERROR: +			/* +			 * If we can't find the corresponding PE, we +			 * just try to unfreeze. +			 */ +			if (pnv_eeh_get_pe(hose, +				be64_to_cpu(frozen_pe_no), pe)) { +				/* Try best to clear it */ +				pr_info("EEH: Clear non-existing PHB#%x-PE#%llx\n", +					hose->global_number, frozen_pe_no); +				pr_info("EEH: PHB location: %s\n", +					eeh_pe_loc_get(phb_pe)); +				opal_pci_eeh_freeze_clear(phb->opal_id, +					frozen_pe_no, +					OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); +				ret = EEH_NEXT_ERR_NONE; +			} else if ((*pe)->state & EEH_PE_ISOLATED || +				   eeh_pe_passed(*pe)) { +				ret = EEH_NEXT_ERR_NONE; +			} else { +				pr_err("EEH: Frozen PE#%x " +				       "on PHB#%x detected\n", +				       (*pe)->addr, +					(*pe)->phb->global_number); +				pr_err("EEH: PE location: %s, " +				       "PHB location: %s\n", +				       eeh_pe_loc_get(*pe), +				       eeh_pe_loc_get(phb_pe)); +				ret = EEH_NEXT_ERR_FROZEN_PE; +			} + +			break; +		default: +			pr_warn("%s: Unexpected error type %d\n", +				__func__, be16_to_cpu(err_type)); +		} -	if (phb && phb->eeh_ops->next_error) -		return phb->eeh_ops->next_error(pe); +		/* +		 * EEH core will try recover from fenced PHB or +		 * frozen PE. In the time for frozen PE, EEH core +		 * enable IO path for that before collecting logs, +		 * but it ruins the site. So we have to dump the +		 * log in advance here. +		 */ +		if ((ret == EEH_NEXT_ERR_FROZEN_PE  || +		    ret == EEH_NEXT_ERR_FENCED_PHB) && +		    !((*pe)->state & EEH_PE_ISOLATED)) { +			eeh_pe_state_mark(*pe, EEH_PE_ISOLATED); +			pnv_eeh_get_phb_diag(*pe); + +			if (eeh_has_flag(EEH_EARLY_DUMP_LOG)) +				pnv_pci_dump_phb_diag_data((*pe)->phb, +							   (*pe)->data); +		} -	return -EEXIST; +		/* +		 * We probably have the frozen parent PE out there and +		 * we need have to handle frozen parent PE firstly. +		 */ +		if (ret == EEH_NEXT_ERR_FROZEN_PE) { +			parent_pe = (*pe)->parent; +			while (parent_pe) { +				/* Hit the ceiling ? */ +				if (parent_pe->type & EEH_PE_PHB) +					break; + +				/* Frozen parent PE ? */ +				state = eeh_ops->get_state(parent_pe, NULL); +				if (state > 0 && +				    (state & active_flags) != active_flags) +					*pe = parent_pe; + +				/* Next parent level */ +				parent_pe = parent_pe->parent; +			} + +			/* We possibly migrate to another PE */ +			eeh_pe_state_mark(*pe, EEH_PE_ISOLATED); +		} + +		/* +		 * If we have no errors on the specific PHB or only +		 * informative error there, we continue poking it. +		 * Otherwise, we need actions to be taken by upper +		 * layer. +		 */ +		if (ret > EEH_NEXT_ERR_INF) +			break; +	} + +	return ret;  } -static int powernv_eeh_restore_config(struct device_node *dn) +static int pnv_eeh_restore_config(struct pci_dn *pdn)  { -	struct eeh_dev *edev = of_node_to_eeh_dev(dn); +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn);  	struct pnv_phb *phb;  	s64 ret; @@ -490,24 +1501,23 @@ static int powernv_eeh_restore_config(struct device_node *dn)  	return 0;  } -static struct eeh_ops powernv_eeh_ops = { +static struct eeh_ops pnv_eeh_ops = {  	.name                   = "powernv", -	.init                   = powernv_eeh_init, -	.post_init              = powernv_eeh_post_init, -	.of_probe               = NULL, -	.dev_probe              = powernv_eeh_dev_probe, -	.set_option             = powernv_eeh_set_option, -	.get_pe_addr            = powernv_eeh_get_pe_addr, -	.get_state              = powernv_eeh_get_state, -	.reset                  = powernv_eeh_reset, -	.wait_state             = powernv_eeh_wait_state, -	.get_log                = powernv_eeh_get_log, -	.configure_bridge       = powernv_eeh_configure_bridge, -	.err_inject		= powernv_eeh_err_inject, -	.read_config            = powernv_eeh_read_config, -	.write_config           = powernv_eeh_write_config, -	.next_error		= powernv_eeh_next_error, -	.restore_config		= powernv_eeh_restore_config +	.init                   = pnv_eeh_init, +	.post_init              = pnv_eeh_post_init, +	.probe			= pnv_eeh_probe, +	.set_option             = pnv_eeh_set_option, +	.get_pe_addr            = pnv_eeh_get_pe_addr, +	.get_state              = pnv_eeh_get_state, +	.reset                  = pnv_eeh_reset, +	.wait_state             = pnv_eeh_wait_state, +	.get_log                = pnv_eeh_get_log, +	.configure_bridge       = pnv_eeh_configure_bridge, +	.err_inject		= pnv_eeh_err_inject, +	.read_config            = pnv_eeh_read_config, +	.write_config           = pnv_eeh_write_config, +	.next_error		= pnv_eeh_next_error, +	.restore_config		= pnv_eeh_restore_config  };  /** @@ -521,7 +1531,7 @@ static int __init eeh_powernv_init(void)  	int ret = -EINVAL;  	eeh_set_pe_aux_size(PNV_PCI_DIAG_BUF_SIZE); -	ret = eeh_ops_register(&powernv_eeh_ops); +	ret = eeh_ops_register(&pnv_eeh_ops);  	if (!ret)  		pr_info("EEH: PowerNV platform initialized\n");  	else diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 6c9ff2b95119..76b344125cef 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1777,7 +1777,8 @@ static void pnv_ioda_setup_pe_seg(struct pci_controller *hose,  				region.start += phb->ioda.io_segsize;  				index++;  			} -		} else if (res->flags & IORESOURCE_MEM) { +		} else if ((res->flags & IORESOURCE_MEM) && +			   !pnv_pci_is_mem_pref_64(res->flags)) {  			region.start = res->start -  				       hose->mem_offset[0] -  				       phb->ioda.m32_pci_base; @@ -2078,9 +2079,6 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,  	phb->get_pe_state = pnv_ioda_get_pe_state;  	phb->freeze_pe = pnv_ioda_freeze_pe;  	phb->unfreeze_pe = pnv_ioda_unfreeze_pe; -#ifdef CONFIG_EEH -	phb->eeh_ops = &ioda_eeh_ops; -#endif  	/* Setup RID -> PE mapping function */  	phb->bdfn_to_pe = pnv_ioda_bdfn_to_pe; @@ -2121,8 +2119,8 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,  	 */  	if (is_kdump_kernel()) {  		pr_info("  Issue PHB reset ...\n"); -		ioda_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL); -		ioda_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE); +		pnv_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL); +		pnv_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE);  	}  	/* Remove M64 resource if we can't configure it successfully */ diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 54323d6b5166..946aa3d62c3c 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -366,9 +366,9 @@ static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no)  	spin_unlock_irqrestore(&phb->lock, flags);  } -static void pnv_pci_config_check_eeh(struct pnv_phb *phb, -				     struct device_node *dn) +static void pnv_pci_config_check_eeh(struct pci_dn *pdn)  { +	struct pnv_phb *phb = pdn->phb->private_data;  	u8	fstate;  	__be16	pcierr;  	int	pe_no; @@ -379,7 +379,7 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb,  	 * setup that yet. So all ER errors should be mapped to  	 * reserved PE.  	 */ -	pe_no = PCI_DN(dn)->pe_number; +	pe_no = pdn->pe_number;  	if (pe_no == IODA_INVALID_PE) {  		if (phb->type == PNV_PHB_P5IOC2)  			pe_no = 0; @@ -407,8 +407,7 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb,  	}  	cfg_dbg(" -> EEH check, bdfn=%04x PE#%d fstate=%x\n", -		(PCI_DN(dn)->busno << 8) | (PCI_DN(dn)->devfn), -		pe_no, fstate); +		(pdn->busno << 8) | (pdn->devfn), pe_no, fstate);  	/* Clear the frozen state if applicable */  	if (fstate == OPAL_EEH_STOPPED_MMIO_FREEZE || @@ -425,10 +424,9 @@ static void pnv_pci_config_check_eeh(struct pnv_phb *phb,  	}  } -int pnv_pci_cfg_read(struct device_node *dn, +int pnv_pci_cfg_read(struct pci_dn *pdn,  		     int where, int size, u32 *val)  { -	struct pci_dn *pdn = PCI_DN(dn);  	struct pnv_phb *phb = pdn->phb->private_data;  	u32 bdfn = (pdn->busno << 8) | pdn->devfn;  	s64 rc; @@ -462,10 +460,9 @@ int pnv_pci_cfg_read(struct device_node *dn,  	return PCIBIOS_SUCCESSFUL;  } -int pnv_pci_cfg_write(struct device_node *dn, +int pnv_pci_cfg_write(struct pci_dn *pdn,  		      int where, int size, u32 val)  { -	struct pci_dn *pdn = PCI_DN(dn);  	struct pnv_phb *phb = pdn->phb->private_data;  	u32 bdfn = (pdn->busno << 8) | pdn->devfn; @@ -489,18 +486,17 @@ int pnv_pci_cfg_write(struct device_node *dn,  }  #if CONFIG_EEH -static bool pnv_pci_cfg_check(struct pci_controller *hose, -			      struct device_node *dn) +static bool pnv_pci_cfg_check(struct pci_dn *pdn)  {  	struct eeh_dev *edev = NULL; -	struct pnv_phb *phb = hose->private_data; +	struct pnv_phb *phb = pdn->phb->private_data;  	/* EEH not enabled ? */  	if (!(phb->flags & PNV_PHB_FLAG_EEH))  		return true;  	/* PE reset or device removed ? */ -	edev = of_node_to_eeh_dev(dn); +	edev = pdn->edev;  	if (edev) {  		if (edev->pe &&  		    (edev->pe->state & EEH_PE_CFG_BLOCKED)) @@ -513,8 +509,7 @@ static bool pnv_pci_cfg_check(struct pci_controller *hose,  	return true;  }  #else -static inline pnv_pci_cfg_check(struct pci_controller *hose, -				struct device_node *dn) +static inline pnv_pci_cfg_check(struct pci_dn *pdn)  {  	return true;  } @@ -524,32 +519,26 @@ static int pnv_pci_read_config(struct pci_bus *bus,  			       unsigned int devfn,  			       int where, int size, u32 *val)  { -	struct device_node *dn, *busdn = pci_bus_to_OF_node(bus);  	struct pci_dn *pdn;  	struct pnv_phb *phb; -	bool found = false;  	int ret;  	*val = 0xFFFFFFFF; -	for (dn = busdn->child; dn; dn = dn->sibling) { -		pdn = PCI_DN(dn); -		if (pdn && pdn->devfn == devfn) { -			phb = pdn->phb->private_data; -			found = true; -			break; -		} -	} +	pdn = pci_get_pdn_by_devfn(bus, devfn); +	if (!pdn) +		return PCIBIOS_DEVICE_NOT_FOUND; -	if (!found || !pnv_pci_cfg_check(pdn->phb, dn)) +	if (!pnv_pci_cfg_check(pdn))  		return PCIBIOS_DEVICE_NOT_FOUND; -	ret = pnv_pci_cfg_read(dn, where, size, val); -	if (phb->flags & PNV_PHB_FLAG_EEH) { +	ret = pnv_pci_cfg_read(pdn, where, size, val); +	phb = pdn->phb->private_data; +	if (phb->flags & PNV_PHB_FLAG_EEH && pdn->edev) {  		if (*val == EEH_IO_ERROR_VALUE(size) && -		    eeh_dev_check_failure(of_node_to_eeh_dev(dn))) +		    eeh_dev_check_failure(pdn->edev))                          return PCIBIOS_DEVICE_NOT_FOUND;  	} else { -		pnv_pci_config_check_eeh(phb, dn); +		pnv_pci_config_check_eeh(pdn);  	}  	return ret; @@ -559,27 +548,21 @@ static int pnv_pci_write_config(struct pci_bus *bus,  				unsigned int devfn,  				int where, int size, u32 val)  { -	struct device_node *dn, *busdn = pci_bus_to_OF_node(bus);  	struct pci_dn *pdn;  	struct pnv_phb *phb; -	bool found = false;  	int ret; -	for (dn = busdn->child; dn; dn = dn->sibling) { -		pdn = PCI_DN(dn); -		if (pdn && pdn->devfn == devfn) { -			phb = pdn->phb->private_data; -			found = true; -			break; -		} -	} +	pdn = pci_get_pdn_by_devfn(bus, devfn); +	if (!pdn) +		return PCIBIOS_DEVICE_NOT_FOUND; -	if (!found || !pnv_pci_cfg_check(pdn->phb, dn)) +	if (!pnv_pci_cfg_check(pdn))  		return PCIBIOS_DEVICE_NOT_FOUND; -	ret = pnv_pci_cfg_write(dn, where, size, val); +	ret = pnv_pci_cfg_write(pdn, where, size, val); +	phb = pdn->phb->private_data;  	if (!(phb->flags & PNV_PHB_FLAG_EEH)) -		pnv_pci_config_check_eeh(phb, dn); +		pnv_pci_config_check_eeh(pdn);  	return ret;  } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 6c02ff8dd69f..1f0cb66133a1 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -75,22 +75,6 @@ struct pnv_ioda_pe {  	struct list_head	list;  }; -/* IOC dependent EEH operations */ -#ifdef CONFIG_EEH -struct pnv_eeh_ops { -	int (*post_init)(struct pci_controller *hose); -	int (*set_option)(struct eeh_pe *pe, int option); -	int (*get_state)(struct eeh_pe *pe); -	int (*reset)(struct eeh_pe *pe, int option); -	int (*get_log)(struct eeh_pe *pe, int severity, -		       char *drv_log, unsigned long len); -	int (*configure_bridge)(struct eeh_pe *pe); -	int (*err_inject)(struct eeh_pe *pe, int type, int func, -			  unsigned long addr, unsigned long mask); -	int (*next_error)(struct eeh_pe **pe); -}; -#endif /* CONFIG_EEH */ -  #define PNV_PHB_FLAG_EEH	(1 << 0)  struct pnv_phb { @@ -104,10 +88,6 @@ struct pnv_phb {  	int			initialized;  	spinlock_t		lock; -#ifdef CONFIG_EEH -	struct pnv_eeh_ops	*eeh_ops; -#endif -  #ifdef CONFIG_DEBUG_FS  	int			has_dbgfs;  	struct dentry		*dbgfs; @@ -213,15 +193,12 @@ struct pnv_phb {  };  extern struct pci_ops pnv_pci_ops; -#ifdef CONFIG_EEH -extern struct pnv_eeh_ops ioda_eeh_ops; -#endif  void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,  				unsigned char *log_buff); -int pnv_pci_cfg_read(struct device_node *dn, +int pnv_pci_cfg_read(struct pci_dn *pdn,  		     int where, int size, u32 *val); -int pnv_pci_cfg_write(struct device_node *dn, +int pnv_pci_cfg_write(struct pci_dn *pdn,  		      int where, int size, u32 val);  extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,  				      void *tce_mem, u64 tce_size, @@ -232,6 +209,6 @@ extern void pnv_pci_init_ioda2_phb(struct device_node *np);  extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl,  					__be64 *startp, __be64 *endp, bool rm);  extern void pnv_pci_reset_secondary_bus(struct pci_dev *dev); -extern int ioda_eeh_phb_reset(struct pci_controller *hose, int option); +extern int pnv_eeh_phb_reset(struct pci_controller *hose, int option);  #endif /* __POWERNV_PCI_H */ diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index a6c7e19f5eb3..2039397cc75d 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -118,9 +118,8 @@ static int pseries_eeh_init(void)  	return 0;  } -static int pseries_eeh_cap_start(struct device_node *dn) +static int pseries_eeh_cap_start(struct pci_dn *pdn)  { -	struct pci_dn *pdn = PCI_DN(dn);  	u32 status;  	if (!pdn) @@ -134,10 +133,9 @@ static int pseries_eeh_cap_start(struct device_node *dn)  } -static int pseries_eeh_find_cap(struct device_node *dn, int cap) +static int pseries_eeh_find_cap(struct pci_dn *pdn, int cap)  { -	struct pci_dn *pdn = PCI_DN(dn); -	int pos = pseries_eeh_cap_start(dn); +	int pos = pseries_eeh_cap_start(pdn);  	int cnt = 48;	/* Maximal number of capabilities */  	u32 id; @@ -160,10 +158,9 @@ static int pseries_eeh_find_cap(struct device_node *dn, int cap)  	return 0;  } -static int pseries_eeh_find_ecap(struct device_node *dn, int cap) +static int pseries_eeh_find_ecap(struct pci_dn *pdn, int cap)  { -	struct pci_dn *pdn = PCI_DN(dn); -	struct eeh_dev *edev = of_node_to_eeh_dev(dn); +	struct eeh_dev *edev = pdn_to_eeh_dev(pdn);  	u32 header;  	int pos = 256;  	int ttl = (4096 - 256) / 8; @@ -191,53 +188,44 @@ static int pseries_eeh_find_ecap(struct device_node *dn, int cap)  }  /** - * pseries_eeh_of_probe - EEH probe on the given device - * @dn: OF node - * @flag: Unused + * pseries_eeh_probe - EEH probe on the given device + * @pdn: PCI device node + * @data: Unused   *   * When EEH module is installed during system boot, all PCI devices   * are checked one by one to see if it supports EEH. The function   * is introduced for the purpose.   */ -static void *pseries_eeh_of_probe(struct device_node *dn, void *flag) +static void *pseries_eeh_probe(struct pci_dn *pdn, void *data)  {  	struct eeh_dev *edev;  	struct eeh_pe pe; -	struct pci_dn *pdn = PCI_DN(dn); -	const __be32 *classp, *vendorp, *devicep; -	u32 class_code; -	const __be32 *regs;  	u32 pcie_flags;  	int enable = 0;  	int ret;  	/* Retrieve OF node and eeh device */ -	edev = of_node_to_eeh_dev(dn); -	if (edev->pe || !of_device_is_available(dn)) +	edev = pdn_to_eeh_dev(pdn); +	if (!edev || edev->pe)  		return NULL; -	/* Retrieve class/vendor/device IDs */ -	classp = of_get_property(dn, "class-code", NULL); -	vendorp = of_get_property(dn, "vendor-id", NULL); -	devicep = of_get_property(dn, "device-id", NULL); - -	/* Skip for bad OF node or PCI-ISA bridge */ -	if (!classp || !vendorp || !devicep) -		return NULL; -	if (dn->type && !strcmp(dn->type, "isa")) +	/* Check class/vendor/device IDs */ +	if (!pdn->vendor_id || !pdn->device_id || !pdn->class_code)  		return NULL; -	class_code = of_read_number(classp, 1); +	/* Skip for PCI-ISA bridge */ +        if ((pdn->class_code >> 8) == PCI_CLASS_BRIDGE_ISA) +		return NULL;  	/*  	 * Update class code and mode of eeh device. We need  	 * correctly reflects that current device is root port  	 * or PCIe switch downstream port.  	 */ -	edev->class_code = class_code; -	edev->pcix_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_PCIX); -	edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP); -	edev->aer_cap = pseries_eeh_find_ecap(dn, PCI_EXT_CAP_ID_ERR); +	edev->class_code = pdn->class_code; +	edev->pcix_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_PCIX); +	edev->pcie_cap = pseries_eeh_find_cap(pdn, PCI_CAP_ID_EXP); +	edev->aer_cap = pseries_eeh_find_ecap(pdn, PCI_EXT_CAP_ID_ERR);  	edev->mode &= 0xFFFFFF00;  	if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {  		edev->mode |= EEH_DEV_BRIDGE; @@ -252,24 +240,16 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)  		}  	} -	/* Retrieve the device address */ -	regs = of_get_property(dn, "reg", NULL); -	if (!regs) { -		pr_warn("%s: OF node property %s::reg not found\n", -			__func__, dn->full_name); -		return NULL; -	} -  	/* Initialize the fake PE */  	memset(&pe, 0, sizeof(struct eeh_pe));  	pe.phb = edev->phb; -	pe.config_addr = of_read_number(regs, 1); +	pe.config_addr = (pdn->busno << 16) | (pdn->devfn << 8);  	/* Enable EEH on the device */  	ret = eeh_ops->set_option(&pe, EEH_OPT_ENABLE);  	if (!ret) { -		edev->config_addr = of_read_number(regs, 1);  		/* Retrieve PE address */ +		edev->config_addr = (pdn->busno << 16) | (pdn->devfn << 8);  		edev->pe_config_addr = eeh_ops->get_pe_addr(&pe);  		pe.addr = edev->pe_config_addr; @@ -285,16 +265,17 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)  			eeh_add_flag(EEH_ENABLED);  			eeh_add_to_parent_pe(edev); -			pr_debug("%s: EEH enabled on %s PHB#%d-PE#%x, config addr#%x\n", -				__func__, dn->full_name, pe.phb->global_number, -				pe.addr, pe.config_addr); -		} else if (dn->parent && of_node_to_eeh_dev(dn->parent) && -			   (of_node_to_eeh_dev(dn->parent))->pe) { +			pr_debug("%s: EEH enabled on %02x:%02x.%01x PHB#%d-PE#%x\n", +				__func__, pdn->busno, PCI_SLOT(pdn->devfn), +				PCI_FUNC(pdn->devfn), pe.phb->global_number, +				pe.addr); +		} else if (pdn->parent && pdn_to_eeh_dev(pdn->parent) && +			   (pdn_to_eeh_dev(pdn->parent))->pe) {  			/* This device doesn't support EEH, but it may have an  			 * EEH parent, in which case we mark it as supported.  			 */ -			edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr; -			edev->pe_config_addr = of_node_to_eeh_dev(dn->parent)->pe_config_addr; +			edev->config_addr = pdn_to_eeh_dev(pdn->parent)->config_addr; +			edev->pe_config_addr = pdn_to_eeh_dev(pdn->parent)->pe_config_addr;  			eeh_add_to_parent_pe(edev);  		}  	} @@ -670,45 +651,36 @@ static int pseries_eeh_configure_bridge(struct eeh_pe *pe)  /**   * pseries_eeh_read_config - Read PCI config space - * @dn: device node + * @pdn: PCI device node   * @where: PCI address   * @size: size to read   * @val: return value   *   * Read config space from the speicifed device   */ -static int pseries_eeh_read_config(struct device_node *dn, int where, int size, u32 *val) +static int pseries_eeh_read_config(struct pci_dn *pdn, int where, int size, u32 *val)  { -	struct pci_dn *pdn; - -	pdn = PCI_DN(dn); -  	return rtas_read_config(pdn, where, size, val);  }  /**   * pseries_eeh_write_config - Write PCI config space - * @dn: device node + * @pdn: PCI device node   * @where: PCI address   * @size: size to write   * @val: value to be written   *   * Write config space to the specified device   */ -static int pseries_eeh_write_config(struct device_node *dn, int where, int size, u32 val) +static int pseries_eeh_write_config(struct pci_dn *pdn, int where, int size, u32 val)  { -	struct pci_dn *pdn; - -	pdn = PCI_DN(dn); -  	return rtas_write_config(pdn, where, size, val);  }  static struct eeh_ops pseries_eeh_ops = {  	.name			= "pseries",  	.init			= pseries_eeh_init, -	.of_probe		= pseries_eeh_of_probe, -	.dev_probe		= NULL, +	.probe			= pseries_eeh_probe,  	.set_option		= pseries_eeh_set_option,  	.get_pe_addr		= pseries_eeh_get_pe_addr,  	.get_state		= pseries_eeh_get_state, diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index 691a154c286d..c8d24f9a6948 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -195,6 +195,7 @@ static struct device_node *find_pe_total_msi(struct pci_dev *dev, int *total)  static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)  {  	struct device_node *dn; +	struct pci_dn *pdn;  	struct eeh_dev *edev;  	/* Found our PE and assume 8 at that point. */ @@ -204,10 +205,11 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total)  		return NULL;  	/* Get the top level device in the PE */ -	edev = of_node_to_eeh_dev(dn); +	edev = pdn_to_eeh_dev(PCI_DN(dn));  	if (edev->pe)  		edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list); -	dn = eeh_dev_to_of_node(edev); +	pdn = eeh_dev_to_pdn(edev); +	dn = pdn ? pdn->node : NULL;  	if (!dn)  		return NULL; diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 89e23811199c..f735f4fee48c 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -82,7 +82,7 @@ struct pci_controller *init_phb_dynamic(struct device_node *dn)  	eeh_dev_phb_init_dynamic(phb);  	if (dn->child) -		eeh_add_device_tree_early(dn); +		eeh_add_device_tree_early(PCI_DN(dn));  	pcibios_scan_phb(phb);  	pcibios_finish_adding_to_bus(phb->bus); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index e445b6701f50..70304070a260 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -265,7 +265,7 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act  			update_dn_pci_info(np, pci->phb);  			/* Create EEH device for the OF node */ -			eeh_dev_init(np, pci->phb); +			eeh_dev_init(PCI_DN(np), pci->phb);  		}  		break;  	default: | 
