diff options
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 99 | ||||
-rw-r--r-- | include/asm-powerpc/ppc-pci.h | 14 |
2 files changed, 113 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 1fec99d53311..a06c91a4852b 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -17,6 +17,7 @@ | |||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/delay.h> | ||
20 | #include <linux/init.h> | 21 | #include <linux/init.h> |
21 | #include <linux/list.h> | 22 | #include <linux/list.h> |
22 | #include <linux/pci.h> | 23 | #include <linux/pci.h> |
@@ -677,6 +678,104 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon | |||
677 | EXPORT_SYMBOL(eeh_check_failure); | 678 | EXPORT_SYMBOL(eeh_check_failure); |
678 | 679 | ||
679 | /* ------------------------------------------------------------- */ | 680 | /* ------------------------------------------------------------- */ |
681 | /* The code below deals with error recovery */ | ||
682 | |||
683 | /** Return negative value if a permanent error, else return | ||
684 | * a number of milliseconds to wait until the PCI slot is | ||
685 | * ready to be used. | ||
686 | */ | ||
687 | static int | ||
688 | eeh_slot_availability(struct pci_dn *pdn) | ||
689 | { | ||
690 | int rc; | ||
691 | int rets[3]; | ||
692 | |||
693 | rc = read_slot_reset_state(pdn, rets); | ||
694 | |||
695 | if (rc) return rc; | ||
696 | |||
697 | if (rets[1] == 0) return -1; /* EEH is not supported */ | ||
698 | if (rets[0] == 0) return 0; /* Oll Korrect */ | ||
699 | if (rets[0] == 5) { | ||
700 | if (rets[2] == 0) return -1; /* permanently unavailable */ | ||
701 | return rets[2]; /* number of millisecs to wait */ | ||
702 | } | ||
703 | return -1; | ||
704 | } | ||
705 | |||
706 | /** rtas_pci_slot_reset raises/lowers the pci #RST line | ||
707 | * state: 1/0 to raise/lower the #RST | ||
708 | * | ||
709 | * Clear the EEH-frozen condition on a slot. This routine | ||
710 | * asserts the PCI #RST line if the 'state' argument is '1', | ||
711 | * and drops the #RST line if 'state is '0'. This routine is | ||
712 | * safe to call in an interrupt context. | ||
713 | * | ||
714 | */ | ||
715 | |||
716 | static void | ||
717 | rtas_pci_slot_reset(struct pci_dn *pdn, int state) | ||
718 | { | ||
719 | int rc; | ||
720 | |||
721 | BUG_ON (pdn==NULL); | ||
722 | |||
723 | if (!pdn->phb) { | ||
724 | printk (KERN_WARNING "EEH: in slot reset, device node %s has no phb\n", | ||
725 | pdn->node->full_name); | ||
726 | return; | ||
727 | } | ||
728 | |||
729 | rc = rtas_call(ibm_set_slot_reset,4,1, NULL, | ||
730 | pdn->eeh_config_addr, | ||
731 | BUID_HI(pdn->phb->buid), | ||
732 | BUID_LO(pdn->phb->buid), | ||
733 | state); | ||
734 | if (rc) { | ||
735 | printk (KERN_WARNING "EEH: Unable to reset the failed slot, (%d) #RST=%d dn=%s\n", | ||
736 | rc, state, pdn->node->full_name); | ||
737 | return; | ||
738 | } | ||
739 | |||
740 | if (state == 0) | ||
741 | eeh_clear_slot (pdn->node->parent->child); | ||
742 | } | ||
743 | |||
744 | /** rtas_set_slot_reset -- assert the pci #RST line for 1/4 second | ||
745 | * dn -- device node to be reset. | ||
746 | */ | ||
747 | |||
748 | void | ||
749 | rtas_set_slot_reset(struct pci_dn *pdn) | ||
750 | { | ||
751 | int i, rc; | ||
752 | |||
753 | rtas_pci_slot_reset (pdn, 1); | ||
754 | |||
755 | /* The PCI bus requires that the reset be held high for at least | ||
756 | * a 100 milliseconds. We wait a bit longer 'just in case'. */ | ||
757 | |||
758 | #define PCI_BUS_RST_HOLD_TIME_MSEC 250 | ||
759 | msleep (PCI_BUS_RST_HOLD_TIME_MSEC); | ||
760 | rtas_pci_slot_reset (pdn, 0); | ||
761 | |||
762 | /* After a PCI slot has been reset, the PCI Express spec requires | ||
763 | * a 1.5 second idle time for the bus to stabilize, before starting | ||
764 | * up traffic. */ | ||
765 | #define PCI_BUS_SETTLE_TIME_MSEC 1800 | ||
766 | msleep (PCI_BUS_SETTLE_TIME_MSEC); | ||
767 | |||
768 | /* Now double check with the firmware to make sure the device is | ||
769 | * ready to be used; if not, wait for recovery. */ | ||
770 | for (i=0; i<10; i++) { | ||
771 | rc = eeh_slot_availability (pdn); | ||
772 | if (rc <= 0) break; | ||
773 | |||
774 | msleep (rc+100); | ||
775 | } | ||
776 | } | ||
777 | |||
778 | /* ------------------------------------------------------------- */ | ||
680 | /* The code below deals with enabling EEH for devices during the | 779 | /* The code below deals with enabling EEH for devices during the |
681 | * early boot sequence. EEH must be enabled before any PCI probing | 780 | * early boot sequence. EEH must be enabled before any PCI probing |
682 | * can be done. | 781 | * can be done. |
diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h index c47146f74a7b..930a606b4e35 100644 --- a/include/asm-powerpc/ppc-pci.h +++ b/include/asm-powerpc/ppc-pci.h | |||
@@ -52,4 +52,18 @@ extern unsigned long pci_probe_only; | |||
52 | extern unsigned long pci_assign_all_buses; | 52 | extern unsigned long pci_assign_all_buses; |
53 | extern int pci_read_irq_line(struct pci_dev *pci_dev); | 53 | extern int pci_read_irq_line(struct pci_dev *pci_dev); |
54 | 54 | ||
55 | /* ---- EEH internal-use-only related routines ---- */ | ||
56 | #ifdef CONFIG_EEH | ||
57 | /** | ||
58 | * rtas_set_slot_reset -- unfreeze a frozen slot | ||
59 | * | ||
60 | * Clear the EEH-frozen condition on a slot. This routine | ||
61 | * does this by asserting the PCI #RST line for 1/8th of | ||
62 | * a second; this routine will sleep while the adapter is | ||
63 | * being reset. | ||
64 | */ | ||
65 | void rtas_set_slot_reset (struct pci_dn *); | ||
66 | |||
67 | #endif | ||
68 | |||
55 | #endif /* _ASM_POWERPC_PPC_PCI_H */ | 69 | #endif /* _ASM_POWERPC_PPC_PCI_H */ |