Commit | Line | Data |
---|---|---|
61305a96 BH |
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> | |
c1a2562a | 22 | #include <linux/msi.h> |
61305a96 BH |
23 | |
24 | #include <asm/sections.h> | |
25 | #include <asm/io.h> | |
26 | #include <asm/prom.h> | |
27 | #include <asm/pci-bridge.h> | |
28 | #include <asm/machdep.h> | |
fb1b55d6 | 29 | #include <asm/msi_bitmap.h> |
61305a96 BH |
30 | #include <asm/ppc-pci.h> |
31 | #include <asm/opal.h> | |
32 | #include <asm/iommu.h> | |
33 | #include <asm/tce.h> | |
61305a96 BH |
34 | |
35 | #include "powernv.h" | |
36 | #include "pci.h" | |
37 | ||
38 | /* For now, use a fixed amount of TCE memory for each p5ioc2 | |
39 | * hub, 16M will do | |
40 | */ | |
41 | #define P5IOC2_TCE_MEMORY 0x01000000 | |
42 | ||
c1a2562a BH |
43 | #ifdef CONFIG_PCI_MSI |
44 | static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, | |
137436c9 GS |
45 | unsigned int hwirq, unsigned int virq, |
46 | unsigned int is_64, struct msi_msg *msg) | |
c1a2562a BH |
47 | { |
48 | if (WARN_ON(!is_64)) | |
49 | return -ENXIO; | |
50 | msg->data = hwirq - phb->msi_base; | |
51 | msg->address_hi = 0x10000000; | |
52 | msg->address_lo = 0; | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) | |
58 | { | |
fb1b55d6 | 59 | unsigned int count; |
c1a2562a BH |
60 | const __be32 *prop = of_get_property(phb->hose->dn, |
61 | "ibm,opal-msi-ranges", NULL); | |
62 | if (!prop) | |
63 | return; | |
64 | ||
65 | /* Don't do MSI's on p5ioc2 PCI-X are they are not properly | |
66 | * verified in HW | |
67 | */ | |
68 | if (of_device_is_compatible(phb->hose->dn, "ibm,p5ioc2-pcix")) | |
69 | return; | |
70 | phb->msi_base = be32_to_cpup(prop); | |
fb1b55d6 GS |
71 | count = be32_to_cpup(prop + 1); |
72 | if (msi_bitmap_alloc(&phb->msi_bmp, count, phb->hose->dn)) { | |
c1a2562a BH |
73 | pr_err("PCI %d: Failed to allocate MSI bitmap !\n", |
74 | phb->hose->global_number); | |
75 | return; | |
76 | } | |
77 | phb->msi_setup = pnv_pci_p5ioc2_msi_setup; | |
78 | phb->msi32_support = 0; | |
79 | pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n", | |
fb1b55d6 | 80 | count, phb->msi_base); |
c1a2562a BH |
81 | } |
82 | #else | |
83 | static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) { } | |
84 | #endif /* CONFIG_PCI_MSI */ | |
85 | ||
cad5cef6 GKH |
86 | static void pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb, |
87 | struct pci_dev *pdev) | |
61305a96 | 88 | { |
4e13c1ac | 89 | if (phb->p5ioc2.iommu_table.it_map == NULL) { |
61305a96 | 90 | iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node); |
4e13c1ac AK |
91 | iommu_register_group(&phb->p5ioc2.iommu_table, |
92 | pci_domain_nr(phb->hose->bus), phb->opal_id); | |
93 | } | |
61305a96 | 94 | |
d905c5df | 95 | set_iommu_table_base_and_group(&pdev->dev, &phb->p5ioc2.iommu_table); |
61305a96 BH |
96 | } |
97 | ||
e9cc17d4 | 98 | static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id, |
61305a96 BH |
99 | void *tce_mem, u64 tce_size) |
100 | { | |
101 | struct pnv_phb *phb; | |
8c5fcc83 | 102 | const __be64 *prop64; |
61305a96 BH |
103 | u64 phb_id; |
104 | int64_t rc; | |
105 | static int primary = 1; | |
106 | ||
107 | pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name); | |
108 | ||
109 | prop64 = of_get_property(np, "ibm,opal-phbid", NULL); | |
110 | if (!prop64) { | |
111 | pr_err(" Missing \"ibm,opal-phbid\" property !\n"); | |
112 | return; | |
113 | } | |
114 | phb_id = be64_to_cpup(prop64); | |
115 | pr_devel(" PHB-ID : 0x%016llx\n", phb_id); | |
116 | pr_devel(" TCE AT : 0x%016lx\n", __pa(tce_mem)); | |
117 | pr_devel(" TCE SZ : 0x%016llx\n", tce_size); | |
118 | ||
119 | rc = opal_pci_set_phb_tce_memory(phb_id, __pa(tce_mem), tce_size); | |
120 | if (rc != OPAL_SUCCESS) { | |
121 | pr_err(" Failed to set TCE memory, OPAL error %lld\n", rc); | |
122 | return; | |
123 | } | |
124 | ||
e39f223f ME |
125 | phb = memblock_virt_alloc(sizeof(struct pnv_phb), 0); |
126 | phb->hose = pcibios_alloc_controller(np); | |
127 | if (!phb->hose) { | |
61305a96 BH |
128 | pr_err(" Failed to allocate PCI controller\n"); |
129 | return; | |
130 | } | |
131 | ||
132 | spin_lock_init(&phb->lock); | |
133 | phb->hose->first_busno = 0; | |
134 | phb->hose->last_busno = 0xff; | |
135 | phb->hose->private_data = phb; | |
65ebf4b6 | 136 | phb->hose->controller_ops = pnv_pci_controller_ops; |
e9cc17d4 | 137 | phb->hub_id = hub_id; |
61305a96 BH |
138 | phb->opal_id = phb_id; |
139 | phb->type = PNV_PHB_P5IOC2; | |
cee72d5b | 140 | phb->model = PNV_PHB_MODEL_P5IOC2; |
61305a96 BH |
141 | |
142 | phb->regs = of_iomap(np, 0); | |
143 | ||
144 | if (phb->regs == NULL) | |
145 | pr_err(" Failed to map registers !\n"); | |
146 | else { | |
147 | pr_devel(" P_BUID = 0x%08x\n", in_be32(phb->regs + 0x100)); | |
148 | pr_devel(" P_IOSZ = 0x%08x\n", in_be32(phb->regs + 0x1b0)); | |
149 | pr_devel(" P_IO_ST = 0x%08x\n", in_be32(phb->regs + 0x1e0)); | |
150 | pr_devel(" P_MEM1_H = 0x%08x\n", in_be32(phb->regs + 0x1a0)); | |
151 | pr_devel(" P_MEM1_L = 0x%08x\n", in_be32(phb->regs + 0x190)); | |
152 | pr_devel(" P_MSZ1_L = 0x%08x\n", in_be32(phb->regs + 0x1c0)); | |
153 | pr_devel(" P_MEM_ST = 0x%08x\n", in_be32(phb->regs + 0x1d0)); | |
154 | pr_devel(" P_MEM2_H = 0x%08x\n", in_be32(phb->regs + 0x2c0)); | |
155 | pr_devel(" P_MEM2_L = 0x%08x\n", in_be32(phb->regs + 0x2b0)); | |
156 | pr_devel(" P_MSZ2_H = 0x%08x\n", in_be32(phb->regs + 0x2d0)); | |
157 | pr_devel(" P_MSZ2_L = 0x%08x\n", in_be32(phb->regs + 0x2e0)); | |
158 | } | |
159 | ||
160 | /* Interpret the "ranges" property */ | |
161 | /* This also maps the I/O region and sets isa_io/mem_base */ | |
162 | pci_process_bridge_OF_ranges(phb->hose, np, primary); | |
163 | primary = 0; | |
164 | ||
165 | phb->hose->ops = &pnv_pci_ops; | |
166 | ||
c1a2562a BH |
167 | /* Setup MSI support */ |
168 | pnv_pci_init_p5ioc2_msis(phb); | |
169 | ||
61305a96 BH |
170 | /* Setup TCEs */ |
171 | phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup; | |
172 | pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table, | |
8fa5d454 AK |
173 | tce_mem, tce_size, 0, |
174 | IOMMU_PAGE_SHIFT_4K); | |
61305a96 BH |
175 | } |
176 | ||
177 | void __init pnv_pci_init_p5ioc2_hub(struct device_node *np) | |
178 | { | |
179 | struct device_node *phbn; | |
8c5fcc83 | 180 | const __be64 *prop64; |
61305a96 BH |
181 | u64 hub_id; |
182 | void *tce_mem; | |
183 | uint64_t tce_per_phb; | |
184 | int64_t rc; | |
185 | int phb_count = 0; | |
186 | ||
187 | pr_info("Probing p5ioc2 IO-Hub %s\n", np->full_name); | |
188 | ||
189 | prop64 = of_get_property(np, "ibm,opal-hubid", NULL); | |
190 | if (!prop64) { | |
191 | pr_err(" Missing \"ibm,opal-hubid\" property !\n"); | |
192 | return; | |
193 | } | |
194 | hub_id = be64_to_cpup(prop64); | |
195 | pr_info(" HUB-ID : 0x%016llx\n", hub_id); | |
196 | ||
cf2b1e0e GS |
197 | /* Count child PHBs and calculate TCE space per PHB */ |
198 | for_each_child_of_node(np, phbn) { | |
199 | if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") || | |
200 | of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) | |
201 | phb_count++; | |
202 | } | |
203 | ||
204 | if (phb_count <= 0) { | |
205 | pr_info(" No PHBs for Hub %s\n", np->full_name); | |
206 | return; | |
207 | } | |
208 | ||
209 | tce_per_phb = __rounddown_pow_of_two(P5IOC2_TCE_MEMORY / phb_count); | |
210 | pr_info(" Allocating %lld MB of TCE memory per PHB\n", | |
211 | tce_per_phb >> 20); | |
212 | ||
61305a96 BH |
213 | /* Currently allocate 16M of TCE memory for every Hub |
214 | * | |
215 | * XXX TODO: Make it chip local if possible | |
216 | */ | |
e39f223f | 217 | tce_mem = memblock_virt_alloc(P5IOC2_TCE_MEMORY, P5IOC2_TCE_MEMORY); |
61305a96 BH |
218 | pr_debug(" TCE : 0x%016lx..0x%016lx\n", |
219 | __pa(tce_mem), __pa(tce_mem) + P5IOC2_TCE_MEMORY - 1); | |
220 | rc = opal_pci_set_hub_tce_memory(hub_id, __pa(tce_mem), | |
221 | P5IOC2_TCE_MEMORY); | |
222 | if (rc != OPAL_SUCCESS) { | |
223 | pr_err(" Failed to allocate TCE memory, OPAL error %lld\n", rc); | |
224 | return; | |
225 | } | |
226 | ||
61305a96 BH |
227 | /* Initialize PHBs */ |
228 | for_each_child_of_node(np, phbn) { | |
229 | if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") || | |
230 | of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) { | |
e9cc17d4 GS |
231 | pnv_pci_init_p5ioc2_phb(phbn, hub_id, |
232 | tce_mem, tce_per_phb); | |
61305a96 BH |
233 | tce_mem += tce_per_phb; |
234 | } | |
235 | } | |
236 | } |