diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/eeh-ioda.c')
| -rw-r--r-- | arch/powerpc/platforms/powernv/eeh-ioda.c | 128 |
1 files changed, 49 insertions, 79 deletions
diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index e1e71618b70c..253fefe3d1a0 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c | |||
| @@ -44,7 +44,8 @@ static int ioda_eeh_event(struct notifier_block *nb, | |||
| 44 | 44 | ||
| 45 | /* We simply send special EEH event */ | 45 | /* We simply send special EEH event */ |
| 46 | if ((changed_evts & OPAL_EVENT_PCI_ERROR) && | 46 | if ((changed_evts & OPAL_EVENT_PCI_ERROR) && |
| 47 | (events & OPAL_EVENT_PCI_ERROR)) | 47 | (events & OPAL_EVENT_PCI_ERROR) && |
| 48 | eeh_enabled()) | ||
| 48 | eeh_send_failure_event(NULL); | 49 | eeh_send_failure_event(NULL); |
| 49 | 50 | ||
| 50 | return 0; | 51 | return 0; |
| @@ -113,6 +114,7 @@ DEFINE_SIMPLE_ATTRIBUTE(ioda_eeh_inbB_dbgfs_ops, ioda_eeh_inbB_dbgfs_get, | |||
| 113 | ioda_eeh_inbB_dbgfs_set, "0x%llx\n"); | 114 | ioda_eeh_inbB_dbgfs_set, "0x%llx\n"); |
| 114 | #endif /* CONFIG_DEBUG_FS */ | 115 | #endif /* CONFIG_DEBUG_FS */ |
| 115 | 116 | ||
| 117 | |||
| 116 | /** | 118 | /** |
| 117 | * ioda_eeh_post_init - Chip dependent post initialization | 119 | * ioda_eeh_post_init - Chip dependent post initialization |
| 118 | * @hose: PCI controller | 120 | * @hose: PCI controller |
| @@ -220,6 +222,22 @@ static int ioda_eeh_set_option(struct eeh_pe *pe, int option) | |||
| 220 | return ret; | 222 | return ret; |
| 221 | } | 223 | } |
| 222 | 224 | ||
| 225 | static void ioda_eeh_phb_diag(struct pci_controller *hose) | ||
| 226 | { | ||
| 227 | struct pnv_phb *phb = hose->private_data; | ||
| 228 | long rc; | ||
| 229 | |||
| 230 | rc = opal_pci_get_phb_diag_data2(phb->opal_id, phb->diag.blob, | ||
| 231 | PNV_PCI_DIAG_BUF_SIZE); | ||
| 232 | if (rc != OPAL_SUCCESS) { | ||
| 233 | pr_warning("%s: Failed to get diag-data for PHB#%x (%ld)\n", | ||
| 234 | __func__, hose->global_number, rc); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | |||
| 238 | pnv_pci_dump_phb_diag_data(hose, phb->diag.blob); | ||
| 239 | } | ||
| 240 | |||
| 223 | /** | 241 | /** |
| 224 | * ioda_eeh_get_state - Retrieve the state of PE | 242 | * ioda_eeh_get_state - Retrieve the state of PE |
| 225 | * @pe: EEH PE | 243 | * @pe: EEH PE |
| @@ -271,6 +289,9 @@ static int ioda_eeh_get_state(struct eeh_pe *pe) | |||
| 271 | result |= EEH_STATE_DMA_ACTIVE; | 289 | result |= EEH_STATE_DMA_ACTIVE; |
| 272 | result |= EEH_STATE_MMIO_ENABLED; | 290 | result |= EEH_STATE_MMIO_ENABLED; |
| 273 | result |= EEH_STATE_DMA_ENABLED; | 291 | result |= EEH_STATE_DMA_ENABLED; |
| 292 | } else if (!(pe->state & EEH_PE_ISOLATED)) { | ||
| 293 | eeh_pe_state_mark(pe, EEH_PE_ISOLATED); | ||
| 294 | ioda_eeh_phb_diag(hose); | ||
| 274 | } | 295 | } |
| 275 | 296 | ||
| 276 | return result; | 297 | return result; |
| @@ -314,6 +335,15 @@ static int ioda_eeh_get_state(struct eeh_pe *pe) | |||
| 314 | __func__, fstate, hose->global_number, pe_no); | 335 | __func__, fstate, hose->global_number, pe_no); |
| 315 | } | 336 | } |
| 316 | 337 | ||
| 338 | /* Dump PHB diag-data for frozen PE */ | ||
| 339 | if (result != EEH_STATE_NOT_SUPPORT && | ||
| 340 | (result & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) != | ||
| 341 | (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE) && | ||
| 342 | !(pe->state & EEH_PE_ISOLATED)) { | ||
| 343 | eeh_pe_state_mark(pe, EEH_PE_ISOLATED); | ||
| 344 | ioda_eeh_phb_diag(hose); | ||
| 345 | } | ||
| 346 | |||
| 317 | return result; | 347 | return result; |
| 318 | } | 348 | } |
| 319 | 349 | ||
| @@ -489,8 +519,7 @@ static int ioda_eeh_bridge_reset(struct pci_controller *hose, | |||
| 489 | static int ioda_eeh_reset(struct eeh_pe *pe, int option) | 519 | static int ioda_eeh_reset(struct eeh_pe *pe, int option) |
| 490 | { | 520 | { |
| 491 | struct pci_controller *hose = pe->phb; | 521 | struct pci_controller *hose = pe->phb; |
| 492 | struct eeh_dev *edev; | 522 | struct pci_bus *bus; |
| 493 | struct pci_dev *dev; | ||
| 494 | int ret; | 523 | int ret; |
| 495 | 524 | ||
| 496 | /* | 525 | /* |
| @@ -519,73 +548,17 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option) | |||
| 519 | if (pe->type & EEH_PE_PHB) { | 548 | if (pe->type & EEH_PE_PHB) { |
| 520 | ret = ioda_eeh_phb_reset(hose, option); | 549 | ret = ioda_eeh_phb_reset(hose, option); |
| 521 | } else { | 550 | } else { |
| 522 | if (pe->type & EEH_PE_DEVICE) { | 551 | bus = eeh_pe_bus_get(pe); |
| 523 | /* | 552 | if (pci_is_root_bus(bus)) |
| 524 | * If it's device PE, we didn't refer to the parent | ||
| 525 | * PCI bus yet. So we have to figure it out indirectly. | ||
| 526 | */ | ||
| 527 | edev = list_first_entry(&pe->edevs, | ||
| 528 | struct eeh_dev, list); | ||
| 529 | dev = eeh_dev_to_pci_dev(edev); | ||
| 530 | dev = dev->bus->self; | ||
| 531 | } else { | ||
| 532 | /* | ||
| 533 | * If it's bus PE, the parent PCI bus is already there | ||
| 534 | * and just pick it up. | ||
| 535 | */ | ||
| 536 | dev = pe->bus->self; | ||
| 537 | } | ||
| 538 | |||
| 539 | /* | ||
| 540 | * Do reset based on the fact that the direct upstream bridge | ||
| 541 | * is root bridge (port) or not. | ||
| 542 | */ | ||
| 543 | if (dev->bus->number == 0) | ||
| 544 | ret = ioda_eeh_root_reset(hose, option); | 553 | ret = ioda_eeh_root_reset(hose, option); |
| 545 | else | 554 | else |
| 546 | ret = ioda_eeh_bridge_reset(hose, dev, option); | 555 | ret = ioda_eeh_bridge_reset(hose, bus->self, option); |
| 547 | } | 556 | } |
| 548 | 557 | ||
| 549 | return ret; | 558 | return ret; |
| 550 | } | 559 | } |
| 551 | 560 | ||
| 552 | /** | 561 | /** |
| 553 | * ioda_eeh_get_log - Retrieve error log | ||
| 554 | * @pe: EEH PE | ||
| 555 | * @severity: Severity level of the log | ||
| 556 | * @drv_log: buffer to store the log | ||
| 557 | * @len: space of the log buffer | ||
| 558 | * | ||
| 559 | * The function is used to retrieve error log from P7IOC. | ||
| 560 | */ | ||
| 561 | static int ioda_eeh_get_log(struct eeh_pe *pe, int severity, | ||
| 562 | char *drv_log, unsigned long len) | ||
| 563 | { | ||
| 564 | s64 ret; | ||
| 565 | unsigned long flags; | ||
| 566 | struct pci_controller *hose = pe->phb; | ||
| 567 | struct pnv_phb *phb = hose->private_data; | ||
| 568 | |||
| 569 | spin_lock_irqsave(&phb->lock, flags); | ||
| 570 | |||
| 571 | ret = opal_pci_get_phb_diag_data2(phb->opal_id, | ||
| 572 | phb->diag.blob, PNV_PCI_DIAG_BUF_SIZE); | ||
| 573 | if (ret) { | ||
| 574 | spin_unlock_irqrestore(&phb->lock, flags); | ||
| 575 | pr_warning("%s: Can't get log for PHB#%x-PE#%x (%lld)\n", | ||
| 576 | __func__, hose->global_number, pe->addr, ret); | ||
| 577 | return -EIO; | ||
| 578 | } | ||
| 579 | |||
| 580 | /* The PHB diag-data is always indicative */ | ||
| 581 | pnv_pci_dump_phb_diag_data(hose, phb->diag.blob); | ||
| 582 | |||
| 583 | spin_unlock_irqrestore(&phb->lock, flags); | ||
| 584 | |||
| 585 | return 0; | ||
| 586 | } | ||
| 587 | |||
| 588 | /** | ||
| 589 | * ioda_eeh_configure_bridge - Configure the PCI bridges for the indicated PE | 562 | * ioda_eeh_configure_bridge - Configure the PCI bridges for the indicated PE |
| 590 | * @pe: EEH PE | 563 | * @pe: EEH PE |
| 591 | * | 564 | * |
| @@ -666,22 +639,6 @@ static void ioda_eeh_hub_diag(struct pci_controller *hose) | |||
| 666 | } | 639 | } |
| 667 | } | 640 | } |
| 668 | 641 | ||
| 669 | static void ioda_eeh_phb_diag(struct pci_controller *hose) | ||
| 670 | { | ||
| 671 | struct pnv_phb *phb = hose->private_data; | ||
| 672 | long rc; | ||
| 673 | |||
| 674 | rc = opal_pci_get_phb_diag_data2(phb->opal_id, phb->diag.blob, | ||
| 675 | PNV_PCI_DIAG_BUF_SIZE); | ||
| 676 | if (rc != OPAL_SUCCESS) { | ||
| 677 | pr_warning("%s: Failed to get diag-data for PHB#%x (%ld)\n", | ||
| 678 | __func__, hose->global_number, rc); | ||
| 679 | return; | ||
| 680 | } | ||
| 681 | |||
| 682 | pnv_pci_dump_phb_diag_data(hose, phb->diag.blob); | ||
| 683 | } | ||
| 684 | |||
| 685 | static int ioda_eeh_get_phb_pe(struct pci_controller *hose, | 642 | static int ioda_eeh_get_phb_pe(struct pci_controller *hose, |
| 686 | struct eeh_pe **pe) | 643 | struct eeh_pe **pe) |
| 687 | { | 644 | { |
| @@ -855,6 +812,20 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) | |||
| 855 | } | 812 | } |
| 856 | 813 | ||
| 857 | /* | 814 | /* |
| 815 | * EEH core will try recover from fenced PHB or | ||
| 816 | * frozen PE. In the time for frozen PE, EEH core | ||
| 817 | * enable IO path for that before collecting logs, | ||
| 818 | * but it ruins the site. So we have to dump the | ||
| 819 | * log in advance here. | ||
| 820 | */ | ||
| 821 | if ((ret == EEH_NEXT_ERR_FROZEN_PE || | ||
| 822 | ret == EEH_NEXT_ERR_FENCED_PHB) && | ||
| 823 | !((*pe)->state & EEH_PE_ISOLATED)) { | ||
| 824 | eeh_pe_state_mark(*pe, EEH_PE_ISOLATED); | ||
| 825 | ioda_eeh_phb_diag(hose); | ||
| 826 | } | ||
| 827 | |||
| 828 | /* | ||
| 858 | * If we have no errors on the specific PHB or only | 829 | * If we have no errors on the specific PHB or only |
| 859 | * informative error there, we continue poking it. | 830 | * informative error there, we continue poking it. |
| 860 | * Otherwise, we need actions to be taken by upper | 831 | * Otherwise, we need actions to be taken by upper |
| @@ -872,7 +843,6 @@ struct pnv_eeh_ops ioda_eeh_ops = { | |||
| 872 | .set_option = ioda_eeh_set_option, | 843 | .set_option = ioda_eeh_set_option, |
| 873 | .get_state = ioda_eeh_get_state, | 844 | .get_state = ioda_eeh_get_state, |
| 874 | .reset = ioda_eeh_reset, | 845 | .reset = ioda_eeh_reset, |
| 875 | .get_log = ioda_eeh_get_log, | ||
| 876 | .configure_bridge = ioda_eeh_configure_bridge, | 846 | .configure_bridge = ioda_eeh_configure_bridge, |
| 877 | .next_error = ioda_eeh_next_error | 847 | .next_error = ioda_eeh_next_error |
| 878 | }; | 848 | }; |
