diff options
Diffstat (limited to 'arch/powerpc/platforms/pasemi/pci.c')
-rw-r--r-- | arch/powerpc/platforms/pasemi/pci.c | 74 |
1 files changed, 69 insertions, 5 deletions
diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index ab1f5f62bcd8..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 | /* |
@@ -107,23 +165,20 @@ static int pa_pxp_write_config(struct pci_bus *bus, unsigned int devfn, | |||
107 | switch (len) { | 165 | switch (len) { |
108 | case 1: | 166 | case 1: |
109 | out_8(addr, val); | 167 | out_8(addr, val); |
110 | (void) in_8(addr); | ||
111 | break; | 168 | break; |
112 | case 2: | 169 | case 2: |
113 | out_le16(addr, val); | 170 | out_le16(addr, val); |
114 | (void) in_le16(addr); | ||
115 | break; | 171 | break; |
116 | default: | 172 | default: |
117 | out_le32(addr, val); | 173 | out_le32(addr, val); |
118 | (void) in_le32(addr); | ||
119 | break; | 174 | break; |
120 | } | 175 | } |
121 | return PCIBIOS_SUCCESSFUL; | 176 | return PCIBIOS_SUCCESSFUL; |
122 | } | 177 | } |
123 | 178 | ||
124 | static struct pci_ops pa_pxp_ops = { | 179 | static struct pci_ops pa_pxp_ops = { |
125 | pa_pxp_read_config, | 180 | .read = pa_pxp_read_config, |
126 | pa_pxp_write_config, | 181 | .write = pa_pxp_write_config, |
127 | }; | 182 | }; |
128 | 183 | ||
129 | static void __init setup_pa_pxp(struct pci_controller *hose) | 184 | static void __init setup_pa_pxp(struct pci_controller *hose) |
@@ -178,3 +233,12 @@ void __init pas_pci_init(void) | |||
178 | /* Use the common resource allocation mechanism */ | 233 | /* Use the common resource allocation mechanism */ |
179 | pci_probe_only = 1; | 234 | pci_probe_only = 1; |
180 | } | 235 | } |
236 | |||
237 | void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset) | ||
238 | { | ||
239 | struct pci_controller *hose; | ||
240 | |||
241 | hose = pci_bus_to_host(dev->bus); | ||
242 | |||
243 | return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset); | ||
244 | } | ||