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