diff options
Diffstat (limited to 'arch/mips/pci/pci-xlp.c')
-rw-r--r-- | arch/mips/pci/pci-xlp.c | 124 |
1 files changed, 80 insertions, 44 deletions
diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index ad55f2cfeec1..653d2db9e0c5 100644 --- a/arch/mips/pci/pci-xlp.c +++ b/arch/mips/pci/pci-xlp.c | |||
@@ -46,6 +46,7 @@ | |||
46 | 46 | ||
47 | #include <asm/netlogic/interrupt.h> | 47 | #include <asm/netlogic/interrupt.h> |
48 | #include <asm/netlogic/haldefs.h> | 48 | #include <asm/netlogic/haldefs.h> |
49 | #include <asm/netlogic/common.h> | ||
49 | 50 | ||
50 | #include <asm/netlogic/xlp-hal/iomap.h> | 51 | #include <asm/netlogic/xlp-hal/iomap.h> |
51 | #include <asm/netlogic/xlp-hal/pic.h> | 52 | #include <asm/netlogic/xlp-hal/pic.h> |
@@ -64,8 +65,12 @@ static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn, | |||
64 | u32 data; | 65 | u32 data; |
65 | u32 *cfgaddr; | 66 | u32 *cfgaddr; |
66 | 67 | ||
68 | where &= ~3; | ||
69 | if (bus->number == 0 && PCI_SLOT(devfn) == 1 && where == 0x954) | ||
70 | return 0xffffffff; | ||
71 | |||
67 | cfgaddr = (u32 *)(pci_config_base + | 72 | cfgaddr = (u32 *)(pci_config_base + |
68 | pci_cfg_addr(bus->number, devfn, where & ~3)); | 73 | pci_cfg_addr(bus->number, devfn, where)); |
69 | data = *cfgaddr; | 74 | data = *cfgaddr; |
70 | return data; | 75 | return data; |
71 | } | 76 | } |
@@ -157,32 +162,38 @@ struct pci_controller nlm_pci_controller = { | |||
157 | .io_offset = 0x00000000UL, | 162 | .io_offset = 0x00000000UL, |
158 | }; | 163 | }; |
159 | 164 | ||
160 | static int get_irq_vector(const struct pci_dev *dev) | 165 | static struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev) |
161 | { | 166 | { |
162 | /* | 167 | struct pci_bus *bus, *p; |
163 | * For XLP PCIe, there is an IRQ per Link, find out which | ||
164 | * link the device is on to assign interrupts | ||
165 | */ | ||
166 | if (dev->bus->self == NULL) | ||
167 | return 0; | ||
168 | 168 | ||
169 | switch (dev->bus->self->devfn) { | 169 | /* Find the bridge on bus 0 */ |
170 | case 0x8: | 170 | bus = dev->bus; |
171 | return PIC_PCIE_LINK_0_IRQ; | 171 | for (p = bus->parent; p && p->number != 0; p = p->parent) |
172 | case 0x9: | 172 | bus = p; |
173 | return PIC_PCIE_LINK_1_IRQ; | 173 | |
174 | case 0xa: | 174 | return p ? bus->self : NULL; |
175 | return PIC_PCIE_LINK_2_IRQ; | 175 | } |
176 | case 0xb: | 176 | |
177 | return PIC_PCIE_LINK_3_IRQ; | 177 | static inline int nlm_pci_link_to_irq(int link) |
178 | } | 178 | { |
179 | WARN(1, "Unexpected devfn %d\n", dev->bus->self->devfn); | 179 | return PIC_PCIE_LINK_0_IRQ + link; |
180 | return 0; | ||
181 | } | 180 | } |
182 | 181 | ||
183 | int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) | 182 | int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) |
184 | { | 183 | { |
185 | return get_irq_vector(dev); | 184 | struct pci_dev *lnkdev; |
185 | int lnkslot, lnkfunc; | ||
186 | |||
187 | /* | ||
188 | * For XLP PCIe, there is an IRQ per Link, find out which | ||
189 | * link the device is on to assign interrupts | ||
190 | */ | ||
191 | lnkdev = xlp_get_pcie_link(dev); | ||
192 | if (lnkdev == NULL) | ||
193 | return 0; | ||
194 | lnkfunc = PCI_FUNC(lnkdev->devfn); | ||
195 | lnkslot = PCI_SLOT(lnkdev->devfn); | ||
196 | return nlm_irq_to_xirq(lnkslot / 8, nlm_pci_link_to_irq(lnkfunc)); | ||
186 | } | 197 | } |
187 | 198 | ||
188 | /* Do platform specific device initialization at pci_enable_device() time */ | 199 | /* Do platform specific device initialization at pci_enable_device() time */ |
@@ -191,42 +202,48 @@ int pcibios_plat_dev_init(struct pci_dev *dev) | |||
191 | return 0; | 202 | return 0; |
192 | } | 203 | } |
193 | 204 | ||
194 | static int xlp_enable_pci_bswap(void) | 205 | /* |
206 | * If big-endian, enable hardware byteswap on the PCIe bridges. | ||
207 | * This will make both the SoC and PCIe devices behave consistently with | ||
208 | * readl/writel. | ||
209 | */ | ||
210 | #ifdef __BIG_ENDIAN | ||
211 | static void xlp_config_pci_bswap(int node, int link) | ||
195 | { | 212 | { |
196 | uint64_t pciebase, sysbase; | 213 | uint64_t nbubase, lnkbase; |
197 | int node, i; | ||
198 | u32 reg; | 214 | u32 reg; |
199 | 215 | ||
200 | /* Chip-0 so node set to 0 */ | 216 | nbubase = nlm_get_bridge_regbase(node); |
201 | node = 0; | 217 | lnkbase = nlm_get_pcie_base(node, link); |
202 | sysbase = nlm_get_bridge_regbase(node); | 218 | |
203 | /* | 219 | /* |
204 | * Enable byte swap in hardware. Program each link's PCIe SWAP regions | 220 | * Enable byte swap in hardware. Program each link's PCIe SWAP regions |
205 | * from the link's address ranges. | 221 | * from the link's address ranges. |
206 | */ | 222 | */ |
207 | for (i = 0; i < 4; i++) { | 223 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link); |
208 | pciebase = nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node, i)); | 224 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg); |
209 | if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) | ||
210 | continue; | ||
211 | 225 | ||
212 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_BASE0 + i); | 226 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_LIMIT0 + link); |
213 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_BASE, reg); | 227 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff); |
214 | 228 | ||
215 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_LIMIT0 + i); | 229 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link); |
216 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_LIM, | 230 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg); |
217 | reg | 0xfff); | ||
218 | 231 | ||
219 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_BASE0 + i); | 232 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link); |
220 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_BASE, reg); | 233 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); |
221 | |||
222 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_LIMIT0 + i); | ||
223 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); | ||
224 | } | ||
225 | return 0; | ||
226 | } | 234 | } |
235 | #else | ||
236 | /* Swap configuration not needed in little-endian mode */ | ||
237 | static inline void xlp_config_pci_bswap(int node, int link) {} | ||
238 | #endif /* __BIG_ENDIAN */ | ||
227 | 239 | ||
228 | static int __init pcibios_init(void) | 240 | static int __init pcibios_init(void) |
229 | { | 241 | { |
242 | struct nlm_soc_info *nodep; | ||
243 | uint64_t pciebase; | ||
244 | int link, n; | ||
245 | u32 reg; | ||
246 | |||
230 | /* Firmware assigns PCI resources */ | 247 | /* Firmware assigns PCI resources */ |
231 | pci_set_flags(PCI_PROBE_ONLY); | 248 | pci_set_flags(PCI_PROBE_ONLY); |
232 | pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20); | 249 | pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20); |
@@ -235,7 +252,26 @@ static int __init pcibios_init(void) | |||
235 | ioport_resource.start = 0; | 252 | ioport_resource.start = 0; |
236 | ioport_resource.end = ~0; | 253 | ioport_resource.end = ~0; |
237 | 254 | ||
238 | xlp_enable_pci_bswap(); | 255 | for (n = 0; n < NLM_NR_NODES; n++) { |
256 | nodep = nlm_get_node(n); | ||
257 | if (!nodep->coremask) | ||
258 | continue; /* node does not exist */ | ||
259 | |||
260 | for (link = 0; link < 4; link++) { | ||
261 | pciebase = nlm_get_pcie_base(n, link); | ||
262 | if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) | ||
263 | continue; | ||
264 | xlp_config_pci_bswap(n, link); | ||
265 | |||
266 | /* put in intpin and irq - u-boot does not */ | ||
267 | reg = nlm_read_pci_reg(pciebase, 0xf); | ||
268 | reg &= ~0x1fu; | ||
269 | reg |= (1 << 8) | nlm_pci_link_to_irq(link); | ||
270 | nlm_write_pci_reg(pciebase, 0xf, reg); | ||
271 | pr_info("XLP PCIe: Link %d-%d initialized.\n", n, link); | ||
272 | } | ||
273 | } | ||
274 | |||
239 | set_io_port_base(CKSEG1); | 275 | set_io_port_base(CKSEG1); |
240 | nlm_pci_controller.io_map_base = CKSEG1; | 276 | nlm_pci_controller.io_map_base = CKSEG1; |
241 | 277 | ||