aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-19 13:45:05 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-20 02:10:04 -0400
commit61305a96fad622ae0f0e78cb06f67ad721d378f9 (patch)
tree210ea46ff7cdec39440aeececbf0175e51d0659d
parented79ba9e15f84cef05aba5cbfe6e93f9b43c31f4 (diff)
powerpc/powernv: Add support for p5ioc2 PCI-X and PCIe
This adds support for PCI-X and PCIe on the p5ioc2 IO hub using OPAL. This includes allocating & setting up TCE tables and config space access routines. This also supports fallbacks via RTAS when OPAL is absent, using legacy TCE format pre-allocated via the device-tree (BML style) Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/platforms/powernv/Makefile1
-rw-r--r--arch/powerpc/platforms/powernv/pci-p5ioc2.c185
-rw-r--r--arch/powerpc/platforms/powernv/pci.c286
-rw-r--r--arch/powerpc/platforms/powernv/pci.h38
-rw-r--r--arch/powerpc/platforms/powernv/powernv.h6
-rw-r--r--arch/powerpc/platforms/powernv/setup.c3
6 files changed, 518 insertions, 1 deletions
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 618ad836f28b..31853008b418 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -2,3 +2,4 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
2obj-y += opal-rtc.o opal-nvram.o 2obj-y += opal-rtc.o opal-nvram.o
3 3
4obj-$(CONFIG_SMP) += smp.o 4obj-$(CONFIG_SMP) += smp.o
5obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o
diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
new file mode 100644
index 000000000000..afabc2bcae45
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
@@ -0,0 +1,185 @@
1/*
2 * Support PCI/PCIe on PowerNV platforms
3 *
4 * Currently supports only P5IOC2
5 *
6 * Copyright 2011 Benjamin Herrenschmidt, IBM Corp.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14#include <linux/kernel.h>
15#include <linux/pci.h>
16#include <linux/delay.h>
17#include <linux/string.h>
18#include <linux/init.h>
19#include <linux/bootmem.h>
20#include <linux/irq.h>
21#include <linux/io.h>
22
23#include <asm/sections.h>
24#include <asm/io.h>
25#include <asm/prom.h>
26#include <asm/pci-bridge.h>
27#include <asm/machdep.h>
28#include <asm/ppc-pci.h>
29#include <asm/opal.h>
30#include <asm/iommu.h>
31#include <asm/tce.h>
32#include <asm/abs_addr.h>
33
34#include "powernv.h"
35#include "pci.h"
36
37/* For now, use a fixed amount of TCE memory for each p5ioc2
38 * hub, 16M will do
39 */
40#define P5IOC2_TCE_MEMORY 0x01000000
41
42static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb,
43 struct pci_dev *pdev)
44{
45 if (phb->p5ioc2.iommu_table.it_map == NULL)
46 iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node);
47
48 set_iommu_table_base(&pdev->dev, &phb->p5ioc2.iommu_table);
49}
50
51static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np,
52 void *tce_mem, u64 tce_size)
53{
54 struct pnv_phb *phb;
55 const u64 *prop64;
56 u64 phb_id;
57 int64_t rc;
58 static int primary = 1;
59
60 pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name);
61
62 prop64 = of_get_property(np, "ibm,opal-phbid", NULL);
63 if (!prop64) {
64 pr_err(" Missing \"ibm,opal-phbid\" property !\n");
65 return;
66 }
67 phb_id = be64_to_cpup(prop64);
68 pr_devel(" PHB-ID : 0x%016llx\n", phb_id);
69 pr_devel(" TCE AT : 0x%016lx\n", __pa(tce_mem));
70 pr_devel(" TCE SZ : 0x%016llx\n", tce_size);
71
72 rc = opal_pci_set_phb_tce_memory(phb_id, __pa(tce_mem), tce_size);
73 if (rc != OPAL_SUCCESS) {
74 pr_err(" Failed to set TCE memory, OPAL error %lld\n", rc);
75 return;
76 }
77
78 phb = alloc_bootmem(sizeof(struct pnv_phb));
79 if (phb) {
80 memset(phb, 0, sizeof(struct pnv_phb));
81 phb->hose = pcibios_alloc_controller(np);
82 }
83 if (!phb || !phb->hose) {
84 pr_err(" Failed to allocate PCI controller\n");
85 return;
86 }
87
88 spin_lock_init(&phb->lock);
89 phb->hose->first_busno = 0;
90 phb->hose->last_busno = 0xff;
91 phb->hose->private_data = phb;
92 phb->opal_id = phb_id;
93 phb->type = PNV_PHB_P5IOC2;
94
95 phb->regs = of_iomap(np, 0);
96
97 if (phb->regs == NULL)
98 pr_err(" Failed to map registers !\n");
99 else {
100 pr_devel(" P_BUID = 0x%08x\n", in_be32(phb->regs + 0x100));
101 pr_devel(" P_IOSZ = 0x%08x\n", in_be32(phb->regs + 0x1b0));
102 pr_devel(" P_IO_ST = 0x%08x\n", in_be32(phb->regs + 0x1e0));
103 pr_devel(" P_MEM1_H = 0x%08x\n", in_be32(phb->regs + 0x1a0));
104 pr_devel(" P_MEM1_L = 0x%08x\n", in_be32(phb->regs + 0x190));
105 pr_devel(" P_MSZ1_L = 0x%08x\n", in_be32(phb->regs + 0x1c0));
106 pr_devel(" P_MEM_ST = 0x%08x\n", in_be32(phb->regs + 0x1d0));
107 pr_devel(" P_MEM2_H = 0x%08x\n", in_be32(phb->regs + 0x2c0));
108 pr_devel(" P_MEM2_L = 0x%08x\n", in_be32(phb->regs + 0x2b0));
109 pr_devel(" P_MSZ2_H = 0x%08x\n", in_be32(phb->regs + 0x2d0));
110 pr_devel(" P_MSZ2_L = 0x%08x\n", in_be32(phb->regs + 0x2e0));
111 }
112
113 /* Interpret the "ranges" property */
114 /* This also maps the I/O region and sets isa_io/mem_base */
115 pci_process_bridge_OF_ranges(phb->hose, np, primary);
116 primary = 0;
117
118 phb->hose->ops = &pnv_pci_ops;
119
120 /* Setup TCEs */
121 phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup;
122 pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table,
123 tce_mem, tce_size, 0);
124}
125
126void __init pnv_pci_init_p5ioc2_hub(struct device_node *np)
127{
128 struct device_node *phbn;
129 const u64 *prop64;
130 u64 hub_id;
131 void *tce_mem;
132 uint64_t tce_per_phb;
133 int64_t rc;
134 int phb_count = 0;
135
136 pr_info("Probing p5ioc2 IO-Hub %s\n", np->full_name);
137
138 prop64 = of_get_property(np, "ibm,opal-hubid", NULL);
139 if (!prop64) {
140 pr_err(" Missing \"ibm,opal-hubid\" property !\n");
141 return;
142 }
143 hub_id = be64_to_cpup(prop64);
144 pr_info(" HUB-ID : 0x%016llx\n", hub_id);
145
146 /* Currently allocate 16M of TCE memory for every Hub
147 *
148 * XXX TODO: Make it chip local if possible
149 */
150 tce_mem = __alloc_bootmem(P5IOC2_TCE_MEMORY, P5IOC2_TCE_MEMORY,
151 __pa(MAX_DMA_ADDRESS));
152 if (!tce_mem) {
153 pr_err(" Failed to allocate TCE Memory !\n");
154 return;
155 }
156 pr_debug(" TCE : 0x%016lx..0x%016lx\n",
157 __pa(tce_mem), __pa(tce_mem) + P5IOC2_TCE_MEMORY - 1);
158 rc = opal_pci_set_hub_tce_memory(hub_id, __pa(tce_mem),
159 P5IOC2_TCE_MEMORY);
160 if (rc != OPAL_SUCCESS) {
161 pr_err(" Failed to allocate TCE memory, OPAL error %lld\n", rc);
162 return;
163 }
164
165 /* Count child PHBs */
166 for_each_child_of_node(np, phbn) {
167 if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") ||
168 of_device_is_compatible(phbn, "ibm,p5ioc2-pciex"))
169 phb_count++;
170 }
171
172 /* Calculate how much TCE space we can give per PHB */
173 tce_per_phb = __rounddown_pow_of_two(P5IOC2_TCE_MEMORY / phb_count);
174 pr_info(" Allocating %lld MB of TCE memory per PHB\n",
175 tce_per_phb >> 20);
176
177 /* Initialize PHBs */
178 for_each_child_of_node(np, phbn) {
179 if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") ||
180 of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) {
181 pnv_pci_init_p5ioc2_phb(phbn, tce_mem, tce_per_phb);
182 tce_mem += tce_per_phb;
183 }
184 }
185}
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
new file mode 100644
index 000000000000..746ce5e51208
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -0,0 +1,286 @@
1/*
2 * Support PCI/PCIe on PowerNV platforms
3 *
4 * Currently supports only P5IOC2
5 *
6 * Copyright 2011 Benjamin Herrenschmidt, IBM Corp.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14#include <linux/kernel.h>
15#include <linux/pci.h>
16#include <linux/delay.h>
17#include <linux/string.h>
18#include <linux/init.h>
19#include <linux/bootmem.h>
20#include <linux/irq.h>
21#include <linux/io.h>
22
23#include <asm/sections.h>
24#include <asm/io.h>
25#include <asm/prom.h>
26#include <asm/pci-bridge.h>
27#include <asm/machdep.h>
28#include <asm/ppc-pci.h>
29#include <asm/opal.h>
30#include <asm/iommu.h>
31#include <asm/tce.h>
32#include <asm/abs_addr.h>
33
34#include "powernv.h"
35#include "pci.h"
36
37
38#define cfg_dbg(fmt...) do { } while(0)
39//#define cfg_dbg(fmt...) printk(fmt)
40
41
42static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus,
43 u32 bdfn)
44{
45 s64 rc;
46 u8 fstate;
47 u16 pcierr;
48 u32 pe_no;
49
50 /* Get PE# if we support IODA */
51 pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0;
52
53 /* Read freeze status */
54 rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr,
55 NULL);
56 if (rc) {
57 pr_warning("PCI %d: Failed to read EEH status for PE#%d,"
58 " err %lld\n", phb->hose->global_number, pe_no, rc);
59 return;
60 }
61 cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n",
62 bdfn, pe_no, fstate);
63 if (fstate != 0) {
64 rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no,
65 OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
66 if (rc) {
67 pr_warning("PCI %d: Failed to clear EEH freeze state"
68 " for PE#%d, err %lld\n",
69 phb->hose->global_number, pe_no, rc);
70 }
71 }
72}
73
74static int pnv_pci_read_config(struct pci_bus *bus,
75 unsigned int devfn,
76 int where, int size, u32 *val)
77{
78 struct pci_controller *hose = pci_bus_to_host(bus);
79 struct pnv_phb *phb = hose->private_data;
80 u32 bdfn = (((uint64_t)bus->number) << 8) | devfn;
81 s64 rc;
82
83 if (hose == NULL)
84 return PCIBIOS_DEVICE_NOT_FOUND;
85
86 switch (size) {
87 case 1: {
88 u8 v8;
89 rc = opal_pci_config_read_byte(phb->opal_id, bdfn, where, &v8);
90 *val = (rc == OPAL_SUCCESS) ? v8 : 0xff;
91 break;
92 }
93 case 2: {
94 u16 v16;
95 rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where,
96 &v16);
97 *val = (rc == OPAL_SUCCESS) ? v16 : 0xffff;
98 break;
99 }
100 case 4: {
101 u32 v32;
102 rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32);
103 *val = (rc == OPAL_SUCCESS) ? v32 : 0xffffffff;
104 break;
105 }
106 default:
107 return PCIBIOS_FUNC_NOT_SUPPORTED;
108 }
109 cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n",
110 bus->number, devfn, where, size, *val);
111
112 /* Check if the PHB got frozen due to an error (no response) */
113 pnv_pci_config_check_eeh(phb, bus, bdfn);
114
115 return PCIBIOS_SUCCESSFUL;
116}
117
118static int pnv_pci_write_config(struct pci_bus *bus,
119 unsigned int devfn,
120 int where, int size, u32 val)
121{
122 struct pci_controller *hose = pci_bus_to_host(bus);
123 struct pnv_phb *phb = hose->private_data;
124 u32 bdfn = (((uint64_t)bus->number) << 8) | devfn;
125
126 if (hose == NULL)
127 return PCIBIOS_DEVICE_NOT_FOUND;
128
129 cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n",
130 bus->number, devfn, where, size, val);
131 switch (size) {
132 case 1:
133 opal_pci_config_write_byte(phb->opal_id, bdfn, where, val);
134 break;
135 case 2:
136 opal_pci_config_write_half_word(phb->opal_id, bdfn, where, val);
137 break;
138 case 4:
139 opal_pci_config_write_word(phb->opal_id, bdfn, where, val);
140 break;
141 default:
142 return PCIBIOS_FUNC_NOT_SUPPORTED;
143 }
144 /* Check if the PHB got frozen due to an error (no response) */
145 pnv_pci_config_check_eeh(phb, bus, bdfn);
146
147 return PCIBIOS_SUCCESSFUL;
148}
149
150struct pci_ops pnv_pci_ops = {
151 .read = pnv_pci_read_config,
152 .write = pnv_pci_write_config,
153};
154
155static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
156 unsigned long uaddr, enum dma_data_direction direction,
157 struct dma_attrs *attrs)
158{
159 u64 proto_tce;
160 u64 *tcep;
161 u64 rpn;
162
163 proto_tce = TCE_PCI_READ; // Read allowed
164
165 if (direction != DMA_TO_DEVICE)
166 proto_tce |= TCE_PCI_WRITE;
167
168 tcep = ((u64 *)tbl->it_base) + index;
169
170 while (npages--) {
171 /* can't move this out since we might cross LMB boundary */
172 rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
173 *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
174
175 uaddr += TCE_PAGE_SIZE;
176 tcep++;
177 }
178 return 0;
179}
180
181static void pnv_tce_free(struct iommu_table *tbl, long index, long npages)
182{
183 u64 *tcep = ((u64 *)tbl->it_base) + index;
184
185 while (npages--)
186 *(tcep++) = 0;
187}
188
189void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
190 void *tce_mem, u64 tce_size,
191 u64 dma_offset)
192{
193 tbl->it_blocksize = 16;
194 tbl->it_base = (unsigned long)tce_mem;
195 tbl->it_offset = dma_offset >> IOMMU_PAGE_SHIFT;
196 tbl->it_index = 0;
197 tbl->it_size = tce_size >> 3;
198 tbl->it_busno = 0;
199 tbl->it_type = TCE_PCI;
200}
201
202static struct iommu_table * __devinit
203pnv_pci_setup_bml_iommu(struct pci_controller *hose)
204{
205 struct iommu_table *tbl;
206 const __be64 *basep;
207 const __be32 *sizep;
208
209 basep = of_get_property(hose->dn, "linux,tce-base", NULL);
210 sizep = of_get_property(hose->dn, "linux,tce-size", NULL);
211 if (basep == NULL || sizep == NULL) {
212 pr_err("PCI: %s has missing tce entries !\n", hose->dn->full_name);
213 return NULL;
214 }
215 tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node);
216 if (WARN_ON(!tbl))
217 return NULL;
218 pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)),
219 be32_to_cpup(sizep), 0);
220 iommu_init_table(tbl, hose->node);
221 return tbl;
222}
223
224static void __devinit pnv_pci_dma_fallback_setup(struct pci_controller *hose,
225 struct pci_dev *pdev)
226{
227 struct device_node *np = pci_bus_to_OF_node(hose->bus);
228 struct pci_dn *pdn;
229
230 if (np == NULL)
231 return;
232 pdn = PCI_DN(np);
233 if (!pdn->iommu_table)
234 pdn->iommu_table = pnv_pci_setup_bml_iommu(hose);
235 if (!pdn->iommu_table)
236 return;
237 set_iommu_table_base(&pdev->dev, pdn->iommu_table);
238}
239
240static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev)
241{
242 struct pci_controller *hose = pci_bus_to_host(pdev->bus);
243 struct pnv_phb *phb = hose->private_data;
244
245 /* If we have no phb structure, try to setup a fallback based on
246 * the device-tree (RTAS PCI for example)
247 */
248 if (phb && phb->dma_dev_setup)
249 phb->dma_dev_setup(phb, pdev);
250 else
251 pnv_pci_dma_fallback_setup(hose, pdev);
252}
253
254void __init pnv_pci_init(void)
255{
256 struct device_node *np;
257
258 pci_set_flags(PCI_CAN_SKIP_ISA_ALIGN);
259
260 /* We do not want to just probe */
261 pci_probe_only = 0;
262
263 /* OPAL absent, try POPAL first then RTAS detection of PHBs */
264 if (!firmware_has_feature(FW_FEATURE_OPAL)) {
265#ifdef CONFIG_PPC_POWERNV_RTAS
266 init_pci_config_tokens();
267 find_and_init_phbs();
268#endif /* CONFIG_PPC_POWERNV_RTAS */
269 } else {
270 /* OPAL is here, do our normal stuff */
271
272 /* Look for p5ioc2 IO-Hubs */
273 for_each_compatible_node(np, NULL, "ibm,p5ioc2")
274 pnv_pci_init_p5ioc2_hub(np);
275 }
276
277 /* Setup the linkage between OF nodes and PHBs */
278 pci_devs_phb_init();
279
280 /* Configure IOMMU DMA hooks */
281 ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
282 ppc_md.tce_build = pnv_tce_build;
283 ppc_md.tce_free = pnv_tce_free;
284 set_pci_dma_ops(&dma_iommu_ops);
285
286}
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
new file mode 100644
index 000000000000..6730a10d7369
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -0,0 +1,38 @@
1#ifndef __POWERNV_PCI_H
2#define __POWERNV_PCI_H
3
4struct pci_dn;
5
6enum pnv_phb_type {
7 PNV_PHB_P5IOC2,
8 PNV_PHB_IODA1,
9 PNV_PHB_IODA2,
10};
11
12struct pnv_phb {
13 struct pci_controller *hose;
14 enum pnv_phb_type type;
15 u64 opal_id;
16 void __iomem *regs;
17 spinlock_t lock;
18
19 void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
20 void (*fixup_phb)(struct pci_controller *hose);
21 u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
22
23 union {
24 struct {
25 struct iommu_table iommu_table;
26 } p5ioc2;
27 };
28};
29
30extern struct pci_ops pnv_pci_ops;
31
32extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
33 void *tce_mem, u64 tce_size,
34 u64 dma_offset);
35extern void pnv_pci_init_p5ioc2_hub(struct device_node *np);
36
37
38#endif /* __POWERNV_PCI_H */
diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
index 35b716008cd3..8a9df7f9667e 100644
--- a/arch/powerpc/platforms/powernv/powernv.h
+++ b/arch/powerpc/platforms/powernv/powernv.h
@@ -7,4 +7,10 @@ extern void pnv_smp_init(void);
7static inline void pnv_smp_init(void) { } 7static inline void pnv_smp_init(void) { }
8#endif 8#endif
9 9
10#ifdef CONFIG_PCI
11extern void pnv_pci_init(void);
12#else
13static inline void pnv_pci_init(void) { }
14#endif
15
10#endif /* _POWERNV_H */ 16#endif /* _POWERNV_H */
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index f0242f3fd3e6..467bd4ac6824 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -40,7 +40,8 @@ static void __init pnv_setup_arch(void)
40 /* Initialize SMP */ 40 /* Initialize SMP */
41 pnv_smp_init(); 41 pnv_smp_init();
42 42
43 /* XXX PCI */ 43 /* Setup PCI */
44 pnv_pci_init();
44 45
45 /* Setup RTC and NVRAM callbacks */ 46 /* Setup RTC and NVRAM callbacks */
46 if (firmware_has_feature(FW_FEATURE_OPAL)) 47 if (firmware_has_feature(FW_FEATURE_OPAL))