For PowerPC PowerNV platform, running on top of skiboot, all PE level reset should be routed to firmware, which exported PCI slot reset capability with device-node property "ibm,reset-by-firmware". Otherwise, the kernel still has to simulate hot reset on PCI bridge's secondary bus. So the code doesn't depend on if the firmware has corresponding feature supported.
Signed-off-by: Gavin Shan <gws...@linux.vnet.ibm.com> --- arch/powerpc/include/asm/eeh.h | 1 + arch/powerpc/include/asm/opal.h | 9 +- arch/powerpc/platforms/powernv/eeh-ioda.c | 163 ++++++++++++++++-------------- 3 files changed, 89 insertions(+), 84 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 9c11d1e..5847721 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -192,6 +192,7 @@ enum { #define EEH_RESET_DEACTIVATE 0 /* Deactivate the PE reset */ #define EEH_RESET_HOT 1 /* Hot reset */ #define EEH_RESET_FUNDAMENTAL 3 /* Fundamental reset */ +#define EEH_RESET_COMPLETE 4 /* PHB complete reset */ #define EEH_LOG_TEMP 1 /* EEH temporary error log */ #define EEH_LOG_PERM 2 /* EEH permanent error log */ diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 9124b0e..edd1993 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -405,11 +405,6 @@ enum OpalPciResetState { OPAL_ASSERT_RESET = 1 }; -enum OpalPciMaskAction { - OPAL_UNMASK_ERROR_TYPE = 0, - OPAL_MASK_ERROR_TYPE = 1 -}; - enum OpalSlotLedType { OPAL_SLOT_LED_ID_TYPE = 0, OPAL_SLOT_LED_FAULT_TYPE = 1 @@ -906,7 +901,7 @@ int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id, uint16_t pe_number, uint16_t dma_window_number, uint64_t pci_start_addr, uint64_t pci_mem_size); -int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope, uint8_t assert_state); +int64_t opal_pci_reset(uint64_t id, uint8_t reset_scope, uint8_t assert_state); int64_t opal_pci_get_hub_diag_data(uint64_t hub_id, void *diag_buffer, uint64_t diag_buffer_len); @@ -922,7 +917,7 @@ int64_t opal_get_epow_status(__be64 *status); int64_t opal_set_system_attention_led(uint8_t led_action); int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe, __be16 *pci_error_type, __be16 *severity); -int64_t opal_pci_poll(uint64_t phb_id); +int64_t opal_pci_poll(uint64_t id, uint8_t *val); int64_t opal_return_cpu(void); int64_t opal_check_token(uint64_t token); int64_t opal_reinit_cpus(uint64_t flags); diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index 43aba2d..825da60 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -490,12 +490,12 @@ static int ioda_eeh_get_state(struct eeh_pe *pe) return ioda_eeh_get_pe_state(pe); } -static s64 ioda_eeh_phb_poll(struct pnv_phb *phb) +static s64 ioda_eeh_poll(uint64_t id) { s64 rc = OPAL_HARDWARE; while (1) { - rc = opal_pci_poll(phb->opal_id); + rc = opal_pci_poll(id, NULL); if (rc <= 0) break; @@ -511,84 +511,38 @@ static s64 ioda_eeh_phb_poll(struct pnv_phb *phb) int ioda_eeh_phb_reset(struct pci_controller *hose, int option) { struct pnv_phb *phb = hose->private_data; + uint8_t scope; 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); + switch (option) { + case EEH_RESET_HOT: + scope = OPAL_RESET_PCI_HOT; + break; + case EEH_RESET_FUNDAMENTAL: + scope = OPAL_RESET_PCI_FUNDAMENTAL; + break; + case EEH_RESET_COMPLETE: + scope = OPAL_RESET_PHB_COMPLETE; + break; + case EEH_RESET_DEACTIVATE: + return 0; + default: + pr_warn("%s: Unsupported option %d\n", + __func__, option); + return -EINVAL; } -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); + /* Issue reset and poll until it's completed */ + rc = opal_pci_reset(phb->opal_id, scope, OPAL_ASSERT_RESET); + if (rc > 0) + rc = ioda_eeh_poll(phb->opal_id); - /* - * 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; + return (rc == OPAL_SUCCESS) ? 0 : -EIO; } -static int ioda_eeh_bridge_reset(struct pci_dev *dev, int option) +static int __ioda_eeh_bridge_reset(struct pci_dev *dev, int option) { struct device_node *dn = pci_device_to_OF_node(dev); @@ -639,14 +593,57 @@ static int ioda_eeh_bridge_reset(struct pci_dev *dev, int option) return 0; } +static int ioda_eeh_bridge_reset(struct pci_dev *dev, int option) +{ + struct pci_controller *hose; + struct pnv_phb *phb; + struct device_node *dn = dev ? pci_device_to_OF_node(dev) : NULL; + uint64_t id = (0x1ul << 60); + uint8_t scope; + s64 rc; + + /* + * If the firmware can't handle it, we still need simulate a hot + * reset on the secondary bus. It should be the rare case. + */ + if (!of_get_property(dn, "ibm,reset-by-firmware", NULL)) + return __ioda_eeh_bridge_reset(dev, option); + + /* The firmware can handle the request */ + switch (option) { + case EEH_RESET_HOT: + scope = OPAL_RESET_PCI_HOT; + break; + case EEH_RESET_FUNDAMENTAL: + scope = OPAL_RESET_PCI_FUNDAMENTAL; + break; + case EEH_RESET_DEACTIVATE: + return 0; + case EEH_RESET_COMPLETE: + default: + pr_warn("%s: Unsupported option %d on device %s\n", + __func__, option, pci_name(dev)); + return -EINVAL; + } + + hose = pci_bus_to_host(dev->bus); + phb = hose->private_data; + id |= (dev->bus->number << 24) | (dev->devfn << 16) | phb->opal_id; + rc = opal_pci_reset(id, scope, OPAL_ASSERT_RESET); + if (rc > 0) + ioda_eeh_poll(id); + + return (rc == OPAL_SUCCESS) ? 0 : -EIO; +} + 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); + ioda_eeh_phb_reset(hose, EEH_RESET_HOT); + ioda_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE); } else { ioda_eeh_bridge_reset(dev, EEH_RESET_HOT); ioda_eeh_bridge_reset(dev, EEH_RESET_DEACTIVATE); @@ -686,7 +683,20 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option) * state explicitly after BAR restore. */ if (pe->type & EEH_PE_PHB) { - ret = ioda_eeh_phb_reset(hose, option); + switch (option) { + case EEH_RESET_HOT: + case EEH_RESET_FUNDAMENTAL: + case EEH_RESET_COMPLETE: + ret = ioda_eeh_phb_reset(hose, EEH_RESET_COMPLETE); + break; + case EEH_RESET_DEACTIVATE: + ret = 0; + break; + default: + ret = -EINVAL; + } + + return ret; } else { struct pnv_phb *phb; s64 rc; @@ -714,9 +724,8 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option) } 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); + if (pci_is_root_bus(bus)) + ret = ioda_eeh_phb_reset(hose, option); else ret = ioda_eeh_bridge_reset(bus->self, option); } -- 1.8.3.2 _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev