diff options
Diffstat (limited to 'arch/powerpc/kernel/eeh_pe.c')
-rw-r--r-- | arch/powerpc/kernel/eeh_pe.c | 58 |
1 files changed, 25 insertions, 33 deletions
diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 016588a6f5ed..f9450537e335 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c | |||
@@ -149,8 +149,8 @@ static struct eeh_pe *eeh_pe_next(struct eeh_pe *pe, | |||
149 | * callback returns something other than NULL, or no more PEs | 149 | * callback returns something other than NULL, or no more PEs |
150 | * to be traversed. | 150 | * to be traversed. |
151 | */ | 151 | */ |
152 | static void *eeh_pe_traverse(struct eeh_pe *root, | 152 | void *eeh_pe_traverse(struct eeh_pe *root, |
153 | eeh_traverse_func fn, void *flag) | 153 | eeh_traverse_func fn, void *flag) |
154 | { | 154 | { |
155 | struct eeh_pe *pe; | 155 | struct eeh_pe *pe; |
156 | void *ret; | 156 | void *ret; |
@@ -176,7 +176,7 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root, | |||
176 | eeh_traverse_func fn, void *flag) | 176 | eeh_traverse_func fn, void *flag) |
177 | { | 177 | { |
178 | struct eeh_pe *pe; | 178 | struct eeh_pe *pe; |
179 | struct eeh_dev *edev; | 179 | struct eeh_dev *edev, *tmp; |
180 | void *ret; | 180 | void *ret; |
181 | 181 | ||
182 | if (!root) { | 182 | if (!root) { |
@@ -186,7 +186,7 @@ void *eeh_pe_dev_traverse(struct eeh_pe *root, | |||
186 | 186 | ||
187 | /* Traverse root PE */ | 187 | /* Traverse root PE */ |
188 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { | 188 | for (pe = root; pe; pe = eeh_pe_next(pe, root)) { |
189 | eeh_pe_for_each_dev(pe, edev) { | 189 | eeh_pe_for_each_dev(pe, edev, tmp) { |
190 | ret = fn(edev, flag); | 190 | ret = fn(edev, flag); |
191 | if (ret) | 191 | if (ret) |
192 | return ret; | 192 | return ret; |
@@ -333,7 +333,7 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
333 | while (parent) { | 333 | while (parent) { |
334 | if (!(parent->type & EEH_PE_INVALID)) | 334 | if (!(parent->type & EEH_PE_INVALID)) |
335 | break; | 335 | break; |
336 | parent->type &= ~EEH_PE_INVALID; | 336 | parent->type &= ~(EEH_PE_INVALID | EEH_PE_KEEP); |
337 | parent = parent->parent; | 337 | parent = parent->parent; |
338 | } | 338 | } |
339 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", | 339 | pr_debug("EEH: Add %s to Device PE#%x, Parent PE#%x\n", |
@@ -397,21 +397,20 @@ int eeh_add_to_parent_pe(struct eeh_dev *edev) | |||
397 | /** | 397 | /** |
398 | * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE | 398 | * eeh_rmv_from_parent_pe - Remove one EEH device from the associated PE |
399 | * @edev: EEH device | 399 | * @edev: EEH device |
400 | * @purge_pe: remove PE or not | ||
401 | * | 400 | * |
402 | * The PE hierarchy tree might be changed when doing PCI hotplug. | 401 | * The PE hierarchy tree might be changed when doing PCI hotplug. |
403 | * Also, the PCI devices or buses could be removed from the system | 402 | * Also, the PCI devices or buses could be removed from the system |
404 | * during EEH recovery. So we have to call the function remove the | 403 | * during EEH recovery. So we have to call the function remove the |
405 | * corresponding PE accordingly if necessary. | 404 | * corresponding PE accordingly if necessary. |
406 | */ | 405 | */ |
407 | int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) | 406 | int eeh_rmv_from_parent_pe(struct eeh_dev *edev) |
408 | { | 407 | { |
409 | struct eeh_pe *pe, *parent, *child; | 408 | struct eeh_pe *pe, *parent, *child; |
410 | int cnt; | 409 | int cnt; |
411 | 410 | ||
412 | if (!edev->pe) { | 411 | if (!edev->pe) { |
413 | pr_warning("%s: No PE found for EEH device %s\n", | 412 | pr_debug("%s: No PE found for EEH device %s\n", |
414 | __func__, edev->dn->full_name); | 413 | __func__, edev->dn->full_name); |
415 | return -EEXIST; | 414 | return -EEXIST; |
416 | } | 415 | } |
417 | 416 | ||
@@ -431,7 +430,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) | |||
431 | if (pe->type & EEH_PE_PHB) | 430 | if (pe->type & EEH_PE_PHB) |
432 | break; | 431 | break; |
433 | 432 | ||
434 | if (purge_pe) { | 433 | if (!(pe->state & EEH_PE_KEEP)) { |
435 | if (list_empty(&pe->edevs) && | 434 | if (list_empty(&pe->edevs) && |
436 | list_empty(&pe->child_list)) { | 435 | list_empty(&pe->child_list)) { |
437 | list_del(&pe->child); | 436 | list_del(&pe->child); |
@@ -502,7 +501,7 @@ static void *__eeh_pe_state_mark(void *data, void *flag) | |||
502 | { | 501 | { |
503 | struct eeh_pe *pe = (struct eeh_pe *)data; | 502 | struct eeh_pe *pe = (struct eeh_pe *)data; |
504 | int state = *((int *)flag); | 503 | int state = *((int *)flag); |
505 | struct eeh_dev *tmp; | 504 | struct eeh_dev *edev, *tmp; |
506 | struct pci_dev *pdev; | 505 | struct pci_dev *pdev; |
507 | 506 | ||
508 | /* | 507 | /* |
@@ -512,8 +511,8 @@ static void *__eeh_pe_state_mark(void *data, void *flag) | |||
512 | * the PCI device driver. | 511 | * the PCI device driver. |
513 | */ | 512 | */ |
514 | pe->state |= state; | 513 | pe->state |= state; |
515 | eeh_pe_for_each_dev(pe, tmp) { | 514 | eeh_pe_for_each_dev(pe, edev, tmp) { |
516 | pdev = eeh_dev_to_pci_dev(tmp); | 515 | pdev = eeh_dev_to_pci_dev(edev); |
517 | if (pdev) | 516 | if (pdev) |
518 | pdev->error_state = pci_channel_io_frozen; | 517 | pdev->error_state = pci_channel_io_frozen; |
519 | } | 518 | } |
@@ -579,7 +578,7 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state) | |||
579 | * blocked on normal path during the stage. So we need utilize | 578 | * blocked on normal path during the stage. So we need utilize |
580 | * eeh operations, which is always permitted. | 579 | * eeh operations, which is always permitted. |
581 | */ | 580 | */ |
582 | static void eeh_bridge_check_link(struct pci_dev *pdev, | 581 | static void eeh_bridge_check_link(struct eeh_dev *edev, |
583 | struct device_node *dn) | 582 | struct device_node *dn) |
584 | { | 583 | { |
585 | int cap; | 584 | int cap; |
@@ -590,16 +589,17 @@ static void eeh_bridge_check_link(struct pci_dev *pdev, | |||
590 | * We only check root port and downstream ports of | 589 | * We only check root port and downstream ports of |
591 | * PCIe switches | 590 | * PCIe switches |
592 | */ | 591 | */ |
593 | if (!pci_is_pcie(pdev) || | 592 | if (!(edev->mode & (EEH_DEV_ROOT_PORT | EEH_DEV_DS_PORT))) |
594 | (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT && | ||
595 | pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)) | ||
596 | return; | 593 | return; |
597 | 594 | ||
598 | pr_debug("%s: Check PCIe link for %s ...\n", | 595 | pr_debug("%s: Check PCIe link for %04x:%02x:%02x.%01x ...\n", |
599 | __func__, pci_name(pdev)); | 596 | __func__, edev->phb->global_number, |
597 | edev->config_addr >> 8, | ||
598 | PCI_SLOT(edev->config_addr & 0xFF), | ||
599 | PCI_FUNC(edev->config_addr & 0xFF)); | ||
600 | 600 | ||
601 | /* Check slot status */ | 601 | /* Check slot status */ |
602 | cap = pdev->pcie_cap; | 602 | cap = edev->pcie_cap; |
603 | eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val); | 603 | eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val); |
604 | if (!(val & PCI_EXP_SLTSTA_PDS)) { | 604 | if (!(val & PCI_EXP_SLTSTA_PDS)) { |
605 | pr_debug(" No card in the slot (0x%04x) !\n", val); | 605 | pr_debug(" No card in the slot (0x%04x) !\n", val); |
@@ -653,8 +653,7 @@ static void eeh_bridge_check_link(struct pci_dev *pdev, | |||
653 | #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) | 653 | #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) |
654 | #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) | 654 | #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) |
655 | 655 | ||
656 | static void eeh_restore_bridge_bars(struct pci_dev *pdev, | 656 | static void eeh_restore_bridge_bars(struct eeh_dev *edev, |
657 | struct eeh_dev *edev, | ||
658 | struct device_node *dn) | 657 | struct device_node *dn) |
659 | { | 658 | { |
660 | int i; | 659 | int i; |
@@ -680,7 +679,7 @@ static void eeh_restore_bridge_bars(struct pci_dev *pdev, | |||
680 | eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]); | 679 | eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]); |
681 | 680 | ||
682 | /* Check the PCIe link is ready */ | 681 | /* Check the PCIe link is ready */ |
683 | eeh_bridge_check_link(pdev, dn); | 682 | eeh_bridge_check_link(edev, dn); |
684 | } | 683 | } |
685 | 684 | ||
686 | static void eeh_restore_device_bars(struct eeh_dev *edev, | 685 | static void eeh_restore_device_bars(struct eeh_dev *edev, |
@@ -729,19 +728,12 @@ static void eeh_restore_device_bars(struct eeh_dev *edev, | |||
729 | */ | 728 | */ |
730 | static void *eeh_restore_one_device_bars(void *data, void *flag) | 729 | static void *eeh_restore_one_device_bars(void *data, void *flag) |
731 | { | 730 | { |
732 | struct pci_dev *pdev = NULL; | ||
733 | struct eeh_dev *edev = (struct eeh_dev *)data; | 731 | struct eeh_dev *edev = (struct eeh_dev *)data; |
734 | struct device_node *dn = eeh_dev_to_of_node(edev); | 732 | struct device_node *dn = eeh_dev_to_of_node(edev); |
735 | 733 | ||
736 | /* Trace the PCI bridge */ | 734 | /* Do special restore for bridges */ |
737 | if (eeh_probe_mode_dev()) { | 735 | if (edev->mode & EEH_DEV_BRIDGE) |
738 | pdev = eeh_dev_to_pci_dev(edev); | 736 | eeh_restore_bridge_bars(edev, dn); |
739 | if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) | ||
740 | pdev = NULL; | ||
741 | } | ||
742 | |||
743 | if (pdev) | ||
744 | eeh_restore_bridge_bars(pdev, edev, dn); | ||
745 | else | 737 | else |
746 | eeh_restore_device_bars(edev, dn); | 738 | eeh_restore_device_bars(edev, dn); |
747 | 739 | ||