diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 13:45:05 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-20 02:10:04 -0400 |
commit | 61305a96fad622ae0f0e78cb06f67ad721d378f9 (patch) | |
tree | 210ea46ff7cdec39440aeececbf0175e51d0659d | |
parent | ed79ba9e15f84cef05aba5cbfe6e93f9b43c31f4 (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/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-p5ioc2.c | 185 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.c | 286 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci.h | 38 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/powernv.h | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 3 |
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 | |||
2 | obj-y += opal-rtc.o opal-nvram.o | 2 | obj-y += opal-rtc.o opal-nvram.o |
3 | 3 | ||
4 | obj-$(CONFIG_SMP) += smp.o | 4 | obj-$(CONFIG_SMP) += smp.o |
5 | obj-$(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 | |||
42 | static 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 | |||
51 | static 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 | |||
126 | void __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 | |||
42 | static 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 | |||
74 | static 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 | |||
118 | static 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 | |||
150 | struct pci_ops pnv_pci_ops = { | ||
151 | .read = pnv_pci_read_config, | ||
152 | .write = pnv_pci_write_config, | ||
153 | }; | ||
154 | |||
155 | static 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 | |||
181 | static 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 | |||
189 | void 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 | |||
202 | static struct iommu_table * __devinit | ||
203 | pnv_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 | |||
224 | static 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 | |||
240 | static 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 | |||
254 | void __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 | |||
4 | struct pci_dn; | ||
5 | |||
6 | enum pnv_phb_type { | ||
7 | PNV_PHB_P5IOC2, | ||
8 | PNV_PHB_IODA1, | ||
9 | PNV_PHB_IODA2, | ||
10 | }; | ||
11 | |||
12 | struct 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 | |||
30 | extern struct pci_ops pnv_pci_ops; | ||
31 | |||
32 | extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl, | ||
33 | void *tce_mem, u64 tce_size, | ||
34 | u64 dma_offset); | ||
35 | extern 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); | |||
7 | static inline void pnv_smp_init(void) { } | 7 | static inline void pnv_smp_init(void) { } |
8 | #endif | 8 | #endif |
9 | 9 | ||
10 | #ifdef CONFIG_PCI | ||
11 | extern void pnv_pci_init(void); | ||
12 | #else | ||
13 | static 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)) |