Commit | Line | Data |
---|---|---|
8f6a93a1 DM |
1 | /* pci_sun4v.c: SUN4V specific PCI controller support. |
2 | * | |
3 | * Copyright (C) 2006 David S. Miller (davem@davemloft.net) | |
4 | */ | |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/types.h> | |
8 | #include <linux/pci.h> | |
9 | #include <linux/init.h> | |
10 | #include <linux/slab.h> | |
11 | #include <linux/interrupt.h> | |
12 | ||
13 | #include <asm/pbm.h> | |
14 | #include <asm/iommu.h> | |
15 | #include <asm/irq.h> | |
16 | #include <asm/upa.h> | |
17 | #include <asm/pstate.h> | |
18 | #include <asm/oplib.h> | |
19 | #include <asm/hypervisor.h> | |
20 | ||
21 | #include "pci_impl.h" | |
22 | #include "iommu_common.h" | |
23 | ||
bade5622 DM |
24 | #include "pci_sun4v.h" |
25 | ||
8f6a93a1 DM |
26 | static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) |
27 | { | |
28 | return NULL; | |
29 | } | |
30 | ||
31 | static void pci_4v_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma) | |
32 | { | |
33 | } | |
34 | ||
35 | static dma_addr_t pci_4v_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction) | |
36 | { | |
37 | return 0; | |
38 | } | |
39 | ||
40 | static void pci_4v_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) | |
41 | { | |
42 | } | |
43 | ||
44 | static int pci_4v_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) | |
45 | { | |
46 | return nelems; | |
47 | } | |
48 | ||
49 | static void pci_4v_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) | |
50 | { | |
51 | } | |
52 | ||
53 | static void pci_4v_dma_sync_single_for_cpu(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) | |
54 | { | |
55 | } | |
56 | ||
57 | static void pci_4v_dma_sync_sg_for_cpu(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) | |
58 | { | |
59 | } | |
60 | ||
61 | struct pci_iommu_ops pci_sun4v_iommu_ops = { | |
62 | .alloc_consistent = pci_4v_alloc_consistent, | |
63 | .free_consistent = pci_4v_free_consistent, | |
64 | .map_single = pci_4v_map_single, | |
65 | .unmap_single = pci_4v_unmap_single, | |
66 | .map_sg = pci_4v_map_sg, | |
67 | .unmap_sg = pci_4v_unmap_sg, | |
68 | .dma_sync_single_for_cpu = pci_4v_dma_sync_single_for_cpu, | |
69 | .dma_sync_sg_for_cpu = pci_4v_dma_sync_sg_for_cpu, | |
70 | }; | |
71 | ||
bade5622 DM |
72 | /* SUN4V PCI configuration space accessors. */ |
73 | ||
74 | static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | |
75 | int where, int size, u32 *value) | |
76 | { | |
77 | /* XXX Implement me! XXX */ | |
78 | return 0; | |
79 | } | |
80 | ||
81 | static int pci_sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, | |
82 | int where, int size, u32 value) | |
83 | { | |
84 | /* XXX Implement me! XXX */ | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static struct pci_ops pci_sun4v_ops = { | |
89 | .read = pci_sun4v_read_pci_cfg, | |
90 | .write = pci_sun4v_write_pci_cfg, | |
91 | }; | |
92 | ||
93 | ||
94 | static void pci_sun4v_scan_bus(struct pci_controller_info *p) | |
95 | { | |
96 | /* XXX Implement me! XXX */ | |
97 | } | |
98 | ||
99 | static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm, | |
100 | struct pci_dev *pdev, | |
101 | unsigned int ino) | |
102 | { | |
103 | /* XXX Implement me! XXX */ | |
104 | return 0; | |
105 | } | |
106 | ||
107 | /* XXX correct? XXX */ | |
108 | static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource) | |
109 | { | |
110 | struct pcidev_cookie *pcp = pdev->sysdata; | |
111 | struct pci_pbm_info *pbm = pcp->pbm; | |
112 | struct resource *res, *root; | |
113 | u32 reg; | |
114 | int where, size, is_64bit; | |
115 | ||
116 | res = &pdev->resource[resource]; | |
117 | if (resource < 6) { | |
118 | where = PCI_BASE_ADDRESS_0 + (resource * 4); | |
119 | } else if (resource == PCI_ROM_RESOURCE) { | |
120 | where = pdev->rom_base_reg; | |
121 | } else { | |
122 | /* Somebody might have asked allocation of a non-standard resource */ | |
123 | return; | |
124 | } | |
125 | ||
126 | is_64bit = 0; | |
127 | if (res->flags & IORESOURCE_IO) | |
128 | root = &pbm->io_space; | |
129 | else { | |
130 | root = &pbm->mem_space; | |
131 | if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) | |
132 | == PCI_BASE_ADDRESS_MEM_TYPE_64) | |
133 | is_64bit = 1; | |
134 | } | |
135 | ||
136 | size = res->end - res->start; | |
137 | pci_read_config_dword(pdev, where, ®); | |
138 | reg = ((reg & size) | | |
139 | (((u32)(res->start - root->start)) & ~size)); | |
140 | if (resource == PCI_ROM_RESOURCE) { | |
141 | reg |= PCI_ROM_ADDRESS_ENABLE; | |
142 | res->flags |= IORESOURCE_ROM_ENABLE; | |
143 | } | |
144 | pci_write_config_dword(pdev, where, reg); | |
145 | ||
146 | /* This knows that the upper 32-bits of the address | |
147 | * must be zero. Our PCI common layer enforces this. | |
148 | */ | |
149 | if (is_64bit) | |
150 | pci_write_config_dword(pdev, where + 4, 0); | |
151 | } | |
152 | ||
153 | /* XXX correct? XXX */ | |
154 | static void pci_sun4v_resource_adjust(struct pci_dev *pdev, | |
155 | struct resource *res, | |
156 | struct resource *root) | |
157 | { | |
158 | res->start += root->start; | |
159 | res->end += root->start; | |
160 | } | |
161 | ||
162 | /* Use ranges property to determine where PCI MEM, I/O, and Config | |
163 | * space are for this PCI bus module. | |
164 | */ | |
165 | static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm) | |
166 | { | |
167 | int i, saw_cfg, saw_mem, saw_io; | |
168 | ||
169 | saw_cfg = saw_mem = saw_io = 0; | |
170 | for (i = 0; i < pbm->num_pbm_ranges; i++) { | |
171 | struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; | |
172 | unsigned long a; | |
173 | int type; | |
174 | ||
175 | type = (pr->child_phys_hi >> 24) & 0x3; | |
176 | a = (((unsigned long)pr->parent_phys_hi << 32UL) | | |
177 | ((unsigned long)pr->parent_phys_lo << 0UL)); | |
178 | ||
179 | switch (type) { | |
180 | case 0: | |
181 | /* PCI config space, 16MB */ | |
182 | pbm->config_space = a; | |
183 | saw_cfg = 1; | |
184 | break; | |
185 | ||
186 | case 1: | |
187 | /* 16-bit IO space, 16MB */ | |
188 | pbm->io_space.start = a; | |
189 | pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); | |
190 | pbm->io_space.flags = IORESOURCE_IO; | |
191 | saw_io = 1; | |
192 | break; | |
193 | ||
194 | case 2: | |
195 | /* 32-bit MEM space, 2GB */ | |
196 | pbm->mem_space.start = a; | |
197 | pbm->mem_space.end = a + (0x80000000UL - 1UL); | |
198 | pbm->mem_space.flags = IORESOURCE_MEM; | |
199 | saw_mem = 1; | |
200 | break; | |
201 | ||
202 | default: | |
203 | break; | |
204 | }; | |
205 | } | |
206 | ||
207 | if (!saw_cfg || !saw_io || !saw_mem) { | |
208 | prom_printf("%s: Fatal error, missing %s PBM range.\n", | |
209 | pbm->name, | |
210 | ((!saw_cfg ? | |
211 | "CFG" : | |
212 | (!saw_io ? | |
213 | "IO" : "MEM")))); | |
214 | prom_halt(); | |
215 | } | |
216 | ||
217 | printk("%s: PCI CFG[%lx] IO[%lx] MEM[%lx]\n", | |
218 | pbm->name, | |
219 | pbm->config_space, | |
220 | pbm->io_space.start, | |
221 | pbm->mem_space.start); | |
222 | } | |
223 | ||
224 | static void pbm_register_toplevel_resources(struct pci_controller_info *p, | |
225 | struct pci_pbm_info *pbm) | |
226 | { | |
227 | pbm->io_space.name = pbm->mem_space.name = pbm->name; | |
228 | ||
229 | request_resource(&ioport_resource, &pbm->io_space); | |
230 | request_resource(&iomem_resource, &pbm->mem_space); | |
231 | pci_register_legacy_regions(&pbm->io_space, | |
232 | &pbm->mem_space); | |
233 | } | |
234 | ||
235 | static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) | |
236 | { | |
237 | /* XXX Implement me! XXX */ | |
238 | } | |
239 | ||
240 | static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node) | |
241 | { | |
242 | struct pci_pbm_info *pbm; | |
243 | struct linux_prom64_registers regs; | |
244 | unsigned int busrange[2]; | |
245 | int err; | |
246 | ||
247 | /* XXX */ | |
248 | pbm = &p->pbm_A; | |
249 | ||
250 | pbm->parent = p; | |
251 | pbm->prom_node = prom_node; | |
252 | pbm->pci_first_slot = 1; | |
253 | ||
254 | prom_getproperty(prom_node, "reg", (char *)®s, sizeof(regs)); | |
255 | pbm->devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff; | |
256 | ||
257 | sprintf(pbm->name, "SUN4V-PCI%d PBM%c", | |
258 | p->index, (pbm == &p->pbm_A ? 'A' : 'B')); | |
259 | ||
260 | printk("%s: devhandle[%x]\n", pbm->name, pbm->devhandle); | |
261 | ||
262 | prom_getstring(prom_node, "name", | |
263 | pbm->prom_name, sizeof(pbm->prom_name)); | |
264 | ||
265 | err = prom_getproperty(prom_node, "ranges", | |
266 | (char *) pbm->pbm_ranges, | |
267 | sizeof(pbm->pbm_ranges)); | |
268 | if (err == 0 || err == -1) { | |
269 | prom_printf("%s: Fatal error, no ranges property.\n", | |
270 | pbm->name); | |
271 | prom_halt(); | |
272 | } | |
273 | ||
274 | pbm->num_pbm_ranges = | |
275 | (err / sizeof(struct linux_prom_pci_ranges)); | |
276 | ||
277 | pci_sun4v_determine_mem_io_space(pbm); | |
278 | pbm_register_toplevel_resources(p, pbm); | |
279 | ||
280 | err = prom_getproperty(prom_node, "interrupt-map", | |
281 | (char *)pbm->pbm_intmap, | |
282 | sizeof(pbm->pbm_intmap)); | |
283 | if (err != -1) { | |
284 | pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); | |
285 | err = prom_getproperty(prom_node, "interrupt-map-mask", | |
286 | (char *)&pbm->pbm_intmask, | |
287 | sizeof(pbm->pbm_intmask)); | |
288 | if (err == -1) { | |
289 | prom_printf("%s: Fatal error, no " | |
290 | "interrupt-map-mask.\n", pbm->name); | |
291 | prom_halt(); | |
292 | } | |
293 | } else { | |
294 | pbm->num_pbm_intmap = 0; | |
295 | memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); | |
296 | } | |
297 | ||
298 | err = prom_getproperty(prom_node, "bus-range", | |
299 | (char *)&busrange[0], | |
300 | sizeof(busrange)); | |
301 | if (err == 0 || err == -1) { | |
302 | prom_printf("%s: Fatal error, no bus-range.\n", pbm->name); | |
303 | prom_halt(); | |
304 | } | |
305 | pbm->pci_first_busno = busrange[0]; | |
306 | pbm->pci_last_busno = busrange[1]; | |
307 | ||
308 | pci_sun4v_iommu_init(pbm); | |
309 | } | |
310 | ||
8f6a93a1 DM |
311 | void sun4v_pci_init(int node, char *model_name) |
312 | { | |
bade5622 DM |
313 | struct pci_controller_info *p; |
314 | struct pci_iommu *iommu; | |
315 | ||
316 | p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); | |
317 | if (!p) { | |
318 | prom_printf("SUN4V_PCI: Fatal memory allocation error.\n"); | |
319 | prom_halt(); | |
320 | } | |
321 | memset(p, 0, sizeof(*p)); | |
322 | ||
323 | iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); | |
324 | if (!iommu) { | |
325 | prom_printf("SCHIZO: Fatal memory allocation error.\n"); | |
326 | prom_halt(); | |
327 | } | |
328 | memset(iommu, 0, sizeof(*iommu)); | |
329 | p->pbm_A.iommu = iommu; | |
330 | ||
331 | iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); | |
332 | if (!iommu) { | |
333 | prom_printf("SCHIZO: Fatal memory allocation error.\n"); | |
334 | prom_halt(); | |
335 | } | |
336 | memset(iommu, 0, sizeof(*iommu)); | |
337 | p->pbm_B.iommu = iommu; | |
338 | ||
339 | p->next = pci_controller_root; | |
340 | pci_controller_root = p; | |
341 | ||
342 | p->index = pci_num_controllers++; | |
343 | p->pbms_same_domain = 0; | |
344 | ||
345 | p->scan_bus = pci_sun4v_scan_bus; | |
346 | p->irq_build = pci_sun4v_irq_build; | |
347 | p->base_address_update = pci_sun4v_base_address_update; | |
348 | p->resource_adjust = pci_sun4v_resource_adjust; | |
349 | p->pci_ops = &pci_sun4v_ops; | |
350 | ||
351 | /* Like PSYCHO and SCHIZO we have a 2GB aligned area | |
352 | * for memory space. | |
353 | */ | |
354 | pci_memspace_mask = 0x7fffffffUL; | |
355 | ||
356 | pci_sun4v_pbm_init(p, node); | |
357 | ||
8f6a93a1 DM |
358 | prom_printf("sun4v_pci_init: Implement me.\n"); |
359 | prom_halt(); | |
360 | } |