diff options
author | Jayachandran C <jchandra@broadcom.com> | 2013-01-16 06:12:41 -0500 |
---|---|---|
committer | John Crispin <blogic@openwrt.org> | 2013-02-16 18:15:22 -0500 |
commit | 7b53eb4d40d702a7458588dcfcddaf4498dbbb36 (patch) | |
tree | a5039562ce54e0560b9235c915f4d5b0df11e060 /arch/mips/pci | |
parent | cba3b643039b9d38284a5fd5143558622b9b64d9 (diff) |
MIPS: PCI: Multi-node PCI support for Netlogic XLP
On a multi-chip XLP board, each node can have 4 PCIe links. Update
XLP PCI code to initialize PCIe on all the nodes.
Signed-off-by: Jayachandran C <jchandra@broadcom.com>
Patchwork: http://patchwork.linux-mips.org/patch/4803/
Signed-off-by: John Crispin <blogic@openwrt.org>
Diffstat (limited to 'arch/mips/pci')
-rw-r--r-- | arch/mips/pci/pci-xlp.c | 109 |
1 files changed, 66 insertions, 43 deletions
diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index fbf001a068a4..dd2d3eb3ad31 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> |
@@ -161,32 +162,38 @@ struct pci_controller nlm_pci_controller = { | |||
161 | .io_offset = 0x00000000UL, | 162 | .io_offset = 0x00000000UL, |
162 | }; | 163 | }; |
163 | 164 | ||
164 | static int get_irq_vector(const struct pci_dev *dev) | 165 | static struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev) |
165 | { | 166 | { |
166 | /* | 167 | struct pci_bus *bus, *p; |
167 | * For XLP PCIe, there is an IRQ per Link, find out which | ||
168 | * link the device is on to assign interrupts | ||
169 | */ | ||
170 | if (dev->bus->self == NULL) | ||
171 | return 0; | ||
172 | 168 | ||
173 | switch (dev->bus->self->devfn) { | 169 | /* Find the bridge on bus 0 */ |
174 | case 0x8: | 170 | bus = dev->bus; |
175 | return PIC_PCIE_LINK_0_IRQ; | 171 | for (p = bus->parent; p && p->number != 0; p = p->parent) |
176 | case 0x9: | 172 | bus = p; |
177 | return PIC_PCIE_LINK_1_IRQ; | 173 | |
178 | case 0xa: | 174 | return p ? bus->self : NULL; |
179 | return PIC_PCIE_LINK_2_IRQ; | 175 | } |
180 | case 0xb: | 176 | |
181 | return PIC_PCIE_LINK_3_IRQ; | 177 | static inline int nlm_pci_link_to_irq(int link) |
182 | } | 178 | { |
183 | WARN(1, "Unexpected devfn %d\n", dev->bus->self->devfn); | 179 | return PIC_PCIE_LINK_0_IRQ + link; |
184 | return 0; | ||
185 | } | 180 | } |
186 | 181 | ||
187 | 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) |
188 | { | 183 | { |
189 | 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)); | ||
190 | } | 197 | } |
191 | 198 | ||
192 | /* Do platform specific device initialization at pci_enable_device() time */ | 199 | /* Do platform specific device initialization at pci_enable_device() time */ |
@@ -201,45 +208,42 @@ int pcibios_plat_dev_init(struct pci_dev *dev) | |||
201 | * readl/writel. | 208 | * readl/writel. |
202 | */ | 209 | */ |
203 | #ifdef __BIG_ENDIAN | 210 | #ifdef __BIG_ENDIAN |
204 | static void xlp_config_pci_bswap(void) | 211 | static void xlp_config_pci_bswap(int node, int link) |
205 | { | 212 | { |
206 | uint64_t pciebase, sysbase; | 213 | uint64_t nbubase, lnkbase; |
207 | int node, i; | ||
208 | u32 reg; | 214 | u32 reg; |
209 | 215 | ||
210 | /* Chip-0 so node set to 0 */ | 216 | nbubase = nlm_get_bridge_regbase(node); |
211 | node = 0; | 217 | lnkbase = nlm_get_pcie_base(node, link); |
212 | sysbase = nlm_get_bridge_regbase(node); | 218 | |
213 | /* | 219 | /* |
214 | * 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 |
215 | * from the link's address ranges. | 221 | * from the link's address ranges. |
216 | */ | 222 | */ |
217 | for (i = 0; i < 4; i++) { | 223 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link); |
218 | pciebase = nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node, i)); | 224 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg); |
219 | if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) | ||
220 | continue; | ||
221 | 225 | ||
222 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_BASE0 + i); | 226 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_LIMIT0 + link); |
223 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_BASE, reg); | 227 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff); |
224 | 228 | ||
225 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_LIMIT0 + i); | 229 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link); |
226 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_LIM, | 230 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg); |
227 | reg | 0xfff); | ||
228 | 231 | ||
229 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_BASE0 + i); | 232 | reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link); |
230 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_BASE, reg); | 233 | nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); |
231 | |||
232 | reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_LIMIT0 + i); | ||
233 | nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); | ||
234 | } | ||
235 | } | 234 | } |
236 | #else | 235 | #else |
237 | /* Swap configuration not needed in little-endian mode */ | 236 | /* Swap configuration not needed in little-endian mode */ |
238 | static inline void xlp_config_pci_bswap(void) {} | 237 | static inline void xlp_config_pci_bswap(int node, int link) {} |
239 | #endif /* __BIG_ENDIAN */ | 238 | #endif /* __BIG_ENDIAN */ |
240 | 239 | ||
241 | static int __init pcibios_init(void) | 240 | static int __init pcibios_init(void) |
242 | { | 241 | { |
242 | struct nlm_soc_info *nodep; | ||
243 | uint64_t pciebase; | ||
244 | int link, n; | ||
245 | u32 reg; | ||
246 | |||
243 | /* Firmware assigns PCI resources */ | 247 | /* Firmware assigns PCI resources */ |
244 | pci_set_flags(PCI_PROBE_ONLY); | 248 | pci_set_flags(PCI_PROBE_ONLY); |
245 | pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20); | 249 | pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20); |
@@ -248,7 +252,26 @@ static int __init pcibios_init(void) | |||
248 | ioport_resource.start = 0; | 252 | ioport_resource.start = 0; |
249 | ioport_resource.end = ~0; | 253 | ioport_resource.end = ~0; |
250 | 254 | ||
251 | xlp_config_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 | |||
252 | set_io_port_base(CKSEG1); | 275 | set_io_port_base(CKSEG1); |
253 | nlm_pci_controller.io_map_base = CKSEG1; | 276 | nlm_pci_controller.io_map_base = CKSEG1; |
254 | 277 | ||