diff options
author | Olof Johansson <olof@lixom.net> | 2007-09-04 22:08:50 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-09-13 11:33:21 -0400 |
commit | 4d442331e57b7bbc28b5a20f7d069bc12e9c503e (patch) | |
tree | e9feaf54400dc32c78add422c2f3d4c0277c404e /arch/powerpc/platforms | |
parent | 68c8404c742fdda2151b4bf6bd98419bf8118481 (diff) |
[POWERPC] pasemi: Add workaround for erratum 5945
Erratum 5945 causes some of the registers on the PCIe root ports to
not read correctly. Do a small dance to avoid this: Write an unused
register, read the value and write it back. Thankfully this is not in
a hot code path.
Signed-off-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/pasemi/pci.c | 62 |
1 files changed, 60 insertions, 2 deletions
diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index fcfb3df3f1a9..b6a0ec45c695 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c | |||
@@ -51,6 +51,61 @@ static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose, | |||
51 | return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset); | 51 | return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset); |
52 | } | 52 | } |
53 | 53 | ||
54 | static inline int is_root_port(int busno, int devfn) | ||
55 | { | ||
56 | return ((busno == 0) && (PCI_FUNC(devfn) < 4) && | ||
57 | ((PCI_SLOT(devfn) == 16) || (PCI_SLOT(devfn) == 17))); | ||
58 | } | ||
59 | |||
60 | static inline int is_5945_reg(int reg) | ||
61 | { | ||
62 | return (((reg >= 0x18) && (reg < 0x34)) || | ||
63 | ((reg >= 0x158) && (reg < 0x178))); | ||
64 | } | ||
65 | |||
66 | static int workaround_5945(struct pci_bus *bus, unsigned int devfn, | ||
67 | int offset, int len, u32 *val) | ||
68 | { | ||
69 | struct pci_controller *hose; | ||
70 | void volatile __iomem *addr, *dummy; | ||
71 | int byte; | ||
72 | u32 tmp; | ||
73 | |||
74 | if (!is_root_port(bus->number, devfn) || !is_5945_reg(offset)) | ||
75 | return 0; | ||
76 | |||
77 | hose = pci_bus_to_host(bus); | ||
78 | |||
79 | addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset & ~0x3); | ||
80 | byte = offset & 0x3; | ||
81 | |||
82 | /* Workaround bug 5945: write 0 to a dummy register before reading, | ||
83 | * and write back what we read. We must read/write the full 32-bit | ||
84 | * contents so we need to shift and mask by hand. | ||
85 | */ | ||
86 | dummy = pa_pxp_cfg_addr(hose, bus->number, devfn, 0x10); | ||
87 | out_le32(dummy, 0); | ||
88 | tmp = in_le32(addr); | ||
89 | out_le32(addr, tmp); | ||
90 | |||
91 | switch (len) { | ||
92 | case 1: | ||
93 | *val = (tmp >> (8*byte)) & 0xff; | ||
94 | break; | ||
95 | case 2: | ||
96 | if (byte == 0) | ||
97 | *val = tmp & 0xffff; | ||
98 | else | ||
99 | *val = (tmp >> 16) & 0xffff; | ||
100 | break; | ||
101 | default: | ||
102 | *val = tmp; | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | return 1; | ||
107 | } | ||
108 | |||
54 | static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn, | 109 | static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn, |
55 | int offset, int len, u32 *val) | 110 | int offset, int len, u32 *val) |
56 | { | 111 | { |
@@ -64,6 +119,9 @@ static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn, | |||
64 | if (!pa_pxp_offset_valid(bus->number, devfn, offset)) | 119 | if (!pa_pxp_offset_valid(bus->number, devfn, offset)) |
65 | return PCIBIOS_BAD_REGISTER_NUMBER; | 120 | return PCIBIOS_BAD_REGISTER_NUMBER; |
66 | 121 | ||
122 | if (workaround_5945(bus, devfn, offset, len, val)) | ||
123 | return PCIBIOS_SUCCESSFUL; | ||
124 | |||
67 | addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset); | 125 | addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset); |
68 | 126 | ||
69 | /* | 127 | /* |
@@ -180,7 +238,7 @@ void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset) | |||
180 | { | 238 | { |
181 | struct pci_controller *hose; | 239 | struct pci_controller *hose; |
182 | 240 | ||
183 | hose = pci_bus_to_host(bus); | 241 | hose = pci_bus_to_host(dev->bus); |
184 | 242 | ||
185 | return pa_pxp_cfg_addr(hose, hose->number, dev->devfn, int offset) | 243 | return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset); |
186 | } | 244 | } |