aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinas Vepstas <linas@linas.org>2005-11-03 19:50:17 -0500
committerPaul Mackerras <paulus@samba.org>2005-11-09 19:38:14 -0500
commit8b553f32db3bf5d0ec0819c595932eb21cd45945 (patch)
treec147ee62bdb580a17d0eec827740c1d97dd29967
parent6dee3fb94004c43ce09f6bf5e7c0b778ec5b8cc8 (diff)
[PATCH] ppc64: Save & restore of PCI device BARS
14-eeh-device-bar-save.patch After a PCI device has been resest, the device BAR's and other config space info must be restored to the same state as they were in when the firmware first handed us this device. This will allow the PCI device driver, when restarted, to correctly recognize and set up the device. Tis patch saves the device config space as early as reasonable after the firmware has handed over the device. Te state resore funcion is inteded for use by the EEH recovery routines. Signed-off-by: Linas Vepstas <linas@austin.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c115
-rw-r--r--include/asm-powerpc/ppc-pci.h23
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 */
81static void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn);
82
80/* RTAS tokens */ 83/* RTAS tokens */
81static int ibm_set_eeh_option; 84static int ibm_set_eeh_option;
82static int ibm_set_slot_reset; 85static int ibm_set_slot_reset;
@@ -366,6 +369,7 @@ static void pci_addr_cache_remove_device(struct pci_dev *dev)
366 */ 369 */
367void __init pci_addr_cache_build(void) 370void __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 */
801static 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 */
832void 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 */
856static 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
870void
871rtas_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);
977void eeh_add_device_late(struct pci_dev *dev) 1087void 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}
994EXPORT_SYMBOL_GPL(eeh_add_device_late); 1107EXPORT_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 */
65void rtas_set_slot_reset (struct pci_dn *); 65void 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 */
75void 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 */
86void rtas_configure_bridge(struct pci_dn *);
87
88int 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 */