diff options
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh.c | 115 | ||||
-rw-r--r-- | include/asm-powerpc/ppc-pci.h | 23 |
2 files changed, 137 insertions, 1 deletions
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index a06c91a4852b..b760836bb9d1 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -77,6 +77,9 @@ | |||
77 | */ | 77 | */ |
78 | #define EEH_MAX_FAILS 100000 | 78 | #define EEH_MAX_FAILS 100000 |
79 | 79 | ||
80 | /* Misc forward declaraions */ | ||
81 | static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn); | ||
82 | |||
80 | /* RTAS tokens */ | 83 | /* RTAS tokens */ |
81 | static int ibm_set_eeh_option; | 84 | static int ibm_set_eeh_option; |
82 | static int ibm_set_slot_reset; | 85 | static int ibm_set_slot_reset; |
@@ -366,6 +369,7 @@ static void pci_addr_cache_remove_device(struct pci_dev *dev) | |||
366 | */ | 369 | */ |
367 | void __init pci_addr_cache_build(void) | 370 | void __init pci_addr_cache_build(void) |
368 | { | 371 | { |
372 | struct device_node *dn; | ||
369 | struct pci_dev *dev = NULL; | 373 | struct pci_dev *dev = NULL; |
370 | 374 | ||
371 | if (!eeh_subsystem_enabled) | 375 | if (!eeh_subsystem_enabled) |
@@ -379,6 +383,10 @@ void __init pci_addr_cache_build(void) | |||
379 | continue; | 383 | continue; |
380 | } | 384 | } |
381 | pci_addr_cache_insert_device(dev); | 385 | pci_addr_cache_insert_device(dev); |
386 | |||
387 | /* Save the BAR's; firmware doesn't restore these after EEH reset */ | ||
388 | dn = pci_device_to_OF_node(dev); | ||
389 | eeh_save_bars(dev, PCI_DN(dn)); | ||
382 | } | 390 | } |
383 | 391 | ||
384 | #ifdef DEBUG | 392 | #ifdef DEBUG |
@@ -775,6 +783,108 @@ rtas_set_slot_reset(struct pci_dn *pdn) | |||
775 | } | 783 | } |
776 | } | 784 | } |
777 | 785 | ||
786 | /* ------------------------------------------------------- */ | ||
787 | /** Save and restore of PCI BARs | ||
788 | * | ||
789 | * Although firmware will set up BARs during boot, it doesn't | ||
790 | * set up device BAR's after a device reset, although it will, | ||
791 | * if requested, set up bridge configuration. Thus, we need to | ||
792 | * configure the PCI devices ourselves. | ||
793 | */ | ||
794 | |||
795 | /** | ||
796 | * __restore_bars - Restore the Base Address Registers | ||
797 | * Loads the PCI configuration space base address registers, | ||
798 | * the expansion ROM base address, the latency timer, and etc. | ||
799 | * from the saved values in the device node. | ||
800 | */ | ||
801 | static inline void __restore_bars (struct pci_dn *pdn) | ||
802 | { | ||
803 | int i; | ||
804 | |||
805 | if (NULL==pdn->phb) return; | ||
806 | for (i=4; i<10; i++) { | ||
807 | rtas_write_config(pdn, i*4, 4, pdn->config_space[i]); | ||
808 | } | ||
809 | |||
810 | /* 12 == Expansion ROM Address */ | ||
811 | rtas_write_config(pdn, 12*4, 4, pdn->config_space[12]); | ||
812 | |||
813 | #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) | ||
814 | #define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)]) | ||
815 | |||
816 | rtas_write_config (pdn, PCI_CACHE_LINE_SIZE, 1, | ||
817 | SAVED_BYTE(PCI_CACHE_LINE_SIZE)); | ||
818 | |||
819 | rtas_write_config (pdn, PCI_LATENCY_TIMER, 1, | ||
820 | SAVED_BYTE(PCI_LATENCY_TIMER)); | ||
821 | |||
822 | /* max latency, min grant, interrupt pin and line */ | ||
823 | rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]); | ||
824 | } | ||
825 | |||
826 | /** | ||
827 | * eeh_restore_bars - restore the PCI config space info | ||
828 | * | ||
829 | * This routine performs a recursive walk to the children | ||
830 | * of this device as well. | ||
831 | */ | ||
832 | void eeh_restore_bars(struct pci_dn *pdn) | ||
833 | { | ||
834 | struct device_node *dn; | ||
835 | if (!pdn) | ||
836 | return; | ||
837 | |||
838 | if (! pdn->eeh_is_bridge) | ||
839 | __restore_bars (pdn); | ||
840 | |||
841 | dn = pdn->node->child; | ||
842 | while (dn) { | ||
843 | eeh_restore_bars (PCI_DN(dn)); | ||
844 | dn = dn->sibling; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | /** | ||
849 | * eeh_save_bars - save device bars | ||
850 | * | ||
851 | * Save the values of the device bars. Unlike the restore | ||
852 | * routine, this routine is *not* recursive. This is because | ||
853 | * PCI devices are added individuallly; but, for the restore, | ||
854 | * an entire slot is reset at a time. | ||
855 | */ | ||
856 | static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn) | ||
857 | { | ||
858 | int i; | ||
859 | |||
860 | if (!pdev || !pdn ) | ||
861 | return; | ||
862 | |||
863 | for (i = 0; i < 16; i++) | ||
864 | pci_read_config_dword(pdev, i * 4, &pdn->config_space[i]); | ||
865 | |||
866 | if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) | ||
867 | pdn->eeh_is_bridge = 1; | ||
868 | } | ||
869 | |||
870 | void | ||
871 | rtas_configure_bridge(struct pci_dn *pdn) | ||
872 | { | ||
873 | int token = rtas_token ("ibm,configure-bridge"); | ||
874 | int rc; | ||
875 | |||
876 | if (token == RTAS_UNKNOWN_SERVICE) | ||
877 | return; | ||
878 | rc = rtas_call(token,3,1, NULL, | ||
879 | pdn->eeh_config_addr, | ||
880 | BUID_HI(pdn->phb->buid), | ||
881 | BUID_LO(pdn->phb->buid)); | ||
882 | if (rc) { | ||
883 | printk (KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n", | ||
884 | rc, pdn->node->full_name); | ||
885 | } | ||
886 | } | ||
887 | |||
778 | /* ------------------------------------------------------------- */ | 888 | /* ------------------------------------------------------------- */ |
779 | /* The code below deals with enabling EEH for devices during the | 889 | /* The code below deals with enabling EEH for devices during the |
780 | * early boot sequence. EEH must be enabled before any PCI probing | 890 | * early boot sequence. EEH must be enabled before any PCI probing |
@@ -977,6 +1087,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_early); | |||
977 | void eeh_add_device_late(struct pci_dev *dev) | 1087 | void eeh_add_device_late(struct pci_dev *dev) |
978 | { | 1088 | { |
979 | struct device_node *dn; | 1089 | struct device_node *dn; |
1090 | struct pci_dn *pdn; | ||
980 | 1091 | ||
981 | if (!dev || !eeh_subsystem_enabled) | 1092 | if (!dev || !eeh_subsystem_enabled) |
982 | return; | 1093 | return; |
@@ -987,9 +1098,11 @@ void eeh_add_device_late(struct pci_dev *dev) | |||
987 | 1098 | ||
988 | pci_dev_get (dev); | 1099 | pci_dev_get (dev); |
989 | dn = pci_device_to_OF_node(dev); | 1100 | dn = pci_device_to_OF_node(dev); |
990 | PCI_DN(dn)->pcidev = dev; | 1101 | pdn = PCI_DN(dn); |
1102 | pdn->pcidev = dev; | ||
991 | 1103 | ||
992 | pci_addr_cache_insert_device (dev); | 1104 | pci_addr_cache_insert_device (dev); |
1105 | eeh_save_bars(dev, pdn); | ||
993 | } | 1106 | } |
994 | EXPORT_SYMBOL_GPL(eeh_add_device_late); | 1107 | EXPORT_SYMBOL_GPL(eeh_add_device_late); |
995 | 1108 | ||
diff --git a/include/asm-powerpc/ppc-pci.h b/include/asm-powerpc/ppc-pci.h index 930a606b4e35..d86c47872bea 100644 --- a/include/asm-powerpc/ppc-pci.h +++ b/include/asm-powerpc/ppc-pci.h | |||
@@ -64,6 +64,29 @@ extern int pci_read_irq_line(struct pci_dev *pci_dev); | |||
64 | */ | 64 | */ |
65 | void rtas_set_slot_reset (struct pci_dn *); | 65 | void rtas_set_slot_reset (struct pci_dn *); |
66 | 66 | ||
67 | /** | ||
68 | * eeh_restore_bars - Restore device configuration info. | ||
69 | * | ||
70 | * A reset of a PCI device will clear out its config space. | ||
71 | * This routines will restore the config space for this | ||
72 | * device, and is children, to values previously obtained | ||
73 | * from the firmware. | ||
74 | */ | ||
75 | void eeh_restore_bars(struct pci_dn *); | ||
76 | |||
77 | /** | ||
78 | * rtas_configure_bridge -- firmware initialization of pci bridge | ||
79 | * | ||
80 | * Ask the firmware to configure all PCI bridges devices | ||
81 | * located behind the indicated node. Required after a | ||
82 | * pci device reset. Does essentially the same hing as | ||
83 | * eeh_restore_bars, but for brdges, and lets firmware | ||
84 | * do the work. | ||
85 | */ | ||
86 | void rtas_configure_bridge(struct pci_dn *); | ||
87 | |||
88 | int rtas_write_config(struct pci_dn *, int where, int size, u32 val); | ||
89 | |||
67 | #endif | 90 | #endif |
68 | 91 | ||
69 | #endif /* _ASM_POWERPC_PPC_PCI_H */ | 92 | #endif /* _ASM_POWERPC_PPC_PCI_H */ |