1 | /* |
2 | * Emulation of the ibm,plb-pcix PCI controller |
3 | * This is found in some 440 SoCs e.g. the 460EX. |
4 | * |
5 | * Copyright (c) 2016-2018 BALATON Zoltan |
6 | * |
7 | * Derived from ppc4xx_pci.c and pci-host/ppce500.c |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License, version 2, as |
11 | * published by the Free Software Foundation. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #include "qemu/osdep.h" |
23 | #include "qemu/error-report.h" |
24 | #include "qemu/log.h" |
25 | #include "qemu/module.h" |
26 | #include "hw/irq.h" |
27 | #include "hw/ppc/ppc.h" |
28 | #include "hw/ppc/ppc4xx.h" |
29 | #include "hw/pci/pci.h" |
30 | #include "hw/pci/pci_host.h" |
31 | #include "exec/address-spaces.h" |
32 | #include "trace.h" |
33 | |
34 | struct PLBOutMap { |
35 | uint64_t la; |
36 | uint64_t pcia; |
37 | uint32_t sa; |
38 | MemoryRegion mr; |
39 | }; |
40 | |
41 | struct PLBInMap { |
42 | uint64_t sa; |
43 | uint64_t la; |
44 | MemoryRegion mr; |
45 | }; |
46 | |
47 | #define TYPE_PPC440_PCIX_HOST_BRIDGE "ppc440-pcix-host" |
48 | #define PPC440_PCIX_HOST_BRIDGE(obj) \ |
49 | OBJECT_CHECK(PPC440PCIXState, (obj), TYPE_PPC440_PCIX_HOST_BRIDGE) |
50 | |
51 | #define PPC440_PCIX_NR_POMS 3 |
52 | #define PPC440_PCIX_NR_PIMS 3 |
53 | |
54 | typedef struct PPC440PCIXState { |
55 | PCIHostState parent_obj; |
56 | |
57 | PCIDevice *dev; |
58 | struct PLBOutMap pom[PPC440_PCIX_NR_POMS]; |
59 | struct PLBInMap pim[PPC440_PCIX_NR_PIMS]; |
60 | uint32_t sts; |
61 | qemu_irq irq; |
62 | AddressSpace bm_as; |
63 | MemoryRegion bm; |
64 | |
65 | MemoryRegion container; |
66 | MemoryRegion iomem; |
67 | MemoryRegion busmem; |
68 | } PPC440PCIXState; |
69 | |
70 | #define PPC440_REG_BASE 0x80000 |
71 | #define PPC440_REG_SIZE 0xff |
72 | |
73 | #define PCIC0_CFGADDR 0x0 |
74 | #define PCIC0_CFGDATA 0x4 |
75 | |
76 | #define PCIX0_POM0LAL 0x68 |
77 | #define PCIX0_POM0LAH 0x6c |
78 | #define PCIX0_POM0SA 0x70 |
79 | #define PCIX0_POM0PCIAL 0x74 |
80 | #define PCIX0_POM0PCIAH 0x78 |
81 | #define PCIX0_POM1LAL 0x7c |
82 | #define PCIX0_POM1LAH 0x80 |
83 | #define PCIX0_POM1SA 0x84 |
84 | #define PCIX0_POM1PCIAL 0x88 |
85 | #define PCIX0_POM1PCIAH 0x8c |
86 | #define PCIX0_POM2SA 0x90 |
87 | |
88 | #define PCIX0_PIM0SAL 0x98 |
89 | #define PCIX0_PIM0LAL 0x9c |
90 | #define PCIX0_PIM0LAH 0xa0 |
91 | #define PCIX0_PIM1SA 0xa4 |
92 | #define PCIX0_PIM1LAL 0xa8 |
93 | #define PCIX0_PIM1LAH 0xac |
94 | #define PCIX0_PIM2SAL 0xb0 |
95 | #define PCIX0_PIM2LAL 0xb4 |
96 | #define PCIX0_PIM2LAH 0xb8 |
97 | #define PCIX0_PIM0SAH 0xf8 |
98 | #define PCIX0_PIM2SAH 0xfc |
99 | |
100 | #define PCIX0_STS 0xe0 |
101 | |
102 | #define PCI_ALL_SIZE (PPC440_REG_BASE + PPC440_REG_SIZE) |
103 | |
104 | static void ppc440_pcix_clear_region(MemoryRegion *parent, |
105 | MemoryRegion *mem) |
106 | { |
107 | if (memory_region_is_mapped(mem)) { |
108 | memory_region_del_subregion(parent, mem); |
109 | object_unparent(OBJECT(mem)); |
110 | } |
111 | } |
112 | |
113 | /* DMA mapping */ |
114 | static void ppc440_pcix_update_pim(PPC440PCIXState *s, int idx) |
115 | { |
116 | MemoryRegion *mem = &s->pim[idx].mr; |
117 | char *name; |
118 | uint64_t size; |
119 | |
120 | /* Before we modify anything, unmap and destroy the region */ |
121 | ppc440_pcix_clear_region(&s->bm, mem); |
122 | |
123 | if (!(s->pim[idx].sa & 1)) { |
124 | /* Not enabled, nothing to do */ |
125 | return; |
126 | } |
127 | |
128 | name = g_strdup_printf("PCI Inbound Window %d" , idx); |
129 | size = ~(s->pim[idx].sa & ~7ULL) + 1; |
130 | memory_region_init_alias(mem, OBJECT(s), name, get_system_memory(), |
131 | s->pim[idx].la, size); |
132 | memory_region_add_subregion_overlap(&s->bm, 0, mem, -1); |
133 | g_free(name); |
134 | |
135 | trace_ppc440_pcix_update_pim(idx, size, s->pim[idx].la); |
136 | } |
137 | |
138 | /* BAR mapping */ |
139 | static void ppc440_pcix_update_pom(PPC440PCIXState *s, int idx) |
140 | { |
141 | MemoryRegion *mem = &s->pom[idx].mr; |
142 | MemoryRegion *address_space_mem = get_system_memory(); |
143 | char *name; |
144 | uint32_t size; |
145 | |
146 | /* Before we modify anything, unmap and destroy the region */ |
147 | ppc440_pcix_clear_region(address_space_mem, mem); |
148 | |
149 | if (!(s->pom[idx].sa & 1)) { |
150 | /* Not enabled, nothing to do */ |
151 | return; |
152 | } |
153 | |
154 | name = g_strdup_printf("PCI Outbound Window %d" , idx); |
155 | size = ~(s->pom[idx].sa & 0xfffffffe) + 1; |
156 | if (!size) { |
157 | size = 0xffffffff; |
158 | } |
159 | memory_region_init_alias(mem, OBJECT(s), name, &s->busmem, |
160 | s->pom[idx].pcia, size); |
161 | memory_region_add_subregion(address_space_mem, s->pom[idx].la, mem); |
162 | g_free(name); |
163 | |
164 | trace_ppc440_pcix_update_pom(idx, size, s->pom[idx].la, s->pom[idx].pcia); |
165 | } |
166 | |
167 | static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, |
168 | uint64_t val, unsigned size) |
169 | { |
170 | struct PPC440PCIXState *s = opaque; |
171 | |
172 | trace_ppc440_pcix_reg_read(addr, val); |
173 | switch (addr) { |
174 | case PCI_VENDOR_ID ... PCI_MAX_LAT: |
175 | stl_le_p(s->dev->config + addr, val); |
176 | break; |
177 | |
178 | case PCIX0_POM0LAL: |
179 | s->pom[0].la &= 0xffffffff00000000ULL; |
180 | s->pom[0].la |= val; |
181 | ppc440_pcix_update_pom(s, 0); |
182 | break; |
183 | case PCIX0_POM0LAH: |
184 | s->pom[0].la &= 0xffffffffULL; |
185 | s->pom[0].la |= val << 32; |
186 | ppc440_pcix_update_pom(s, 0); |
187 | break; |
188 | case PCIX0_POM0SA: |
189 | s->pom[0].sa = val; |
190 | ppc440_pcix_update_pom(s, 0); |
191 | break; |
192 | case PCIX0_POM0PCIAL: |
193 | s->pom[0].pcia &= 0xffffffff00000000ULL; |
194 | s->pom[0].pcia |= val; |
195 | ppc440_pcix_update_pom(s, 0); |
196 | break; |
197 | case PCIX0_POM0PCIAH: |
198 | s->pom[0].pcia &= 0xffffffffULL; |
199 | s->pom[0].pcia |= val << 32; |
200 | ppc440_pcix_update_pom(s, 0); |
201 | break; |
202 | case PCIX0_POM1LAL: |
203 | s->pom[1].la &= 0xffffffff00000000ULL; |
204 | s->pom[1].la |= val; |
205 | ppc440_pcix_update_pom(s, 1); |
206 | break; |
207 | case PCIX0_POM1LAH: |
208 | s->pom[1].la &= 0xffffffffULL; |
209 | s->pom[1].la |= val << 32; |
210 | ppc440_pcix_update_pom(s, 1); |
211 | break; |
212 | case PCIX0_POM1SA: |
213 | s->pom[1].sa = val; |
214 | ppc440_pcix_update_pom(s, 1); |
215 | break; |
216 | case PCIX0_POM1PCIAL: |
217 | s->pom[1].pcia &= 0xffffffff00000000ULL; |
218 | s->pom[1].pcia |= val; |
219 | ppc440_pcix_update_pom(s, 1); |
220 | break; |
221 | case PCIX0_POM1PCIAH: |
222 | s->pom[1].pcia &= 0xffffffffULL; |
223 | s->pom[1].pcia |= val << 32; |
224 | ppc440_pcix_update_pom(s, 1); |
225 | break; |
226 | case PCIX0_POM2SA: |
227 | s->pom[2].sa = val; |
228 | break; |
229 | |
230 | case PCIX0_PIM0SAL: |
231 | s->pim[0].sa &= 0xffffffff00000000ULL; |
232 | s->pim[0].sa |= val; |
233 | ppc440_pcix_update_pim(s, 0); |
234 | break; |
235 | case PCIX0_PIM0LAL: |
236 | s->pim[0].la &= 0xffffffff00000000ULL; |
237 | s->pim[0].la |= val; |
238 | ppc440_pcix_update_pim(s, 0); |
239 | break; |
240 | case PCIX0_PIM0LAH: |
241 | s->pim[0].la &= 0xffffffffULL; |
242 | s->pim[0].la |= val << 32; |
243 | ppc440_pcix_update_pim(s, 0); |
244 | break; |
245 | case PCIX0_PIM1SA: |
246 | s->pim[1].sa = val; |
247 | ppc440_pcix_update_pim(s, 1); |
248 | break; |
249 | case PCIX0_PIM1LAL: |
250 | s->pim[1].la &= 0xffffffff00000000ULL; |
251 | s->pim[1].la |= val; |
252 | ppc440_pcix_update_pim(s, 1); |
253 | break; |
254 | case PCIX0_PIM1LAH: |
255 | s->pim[1].la &= 0xffffffffULL; |
256 | s->pim[1].la |= val << 32; |
257 | ppc440_pcix_update_pim(s, 1); |
258 | break; |
259 | case PCIX0_PIM2SAL: |
260 | s->pim[2].sa &= 0xffffffff00000000ULL; |
261 | s->pim[2].sa |= val; |
262 | ppc440_pcix_update_pim(s, 2); |
263 | break; |
264 | case PCIX0_PIM2LAL: |
265 | s->pim[2].la &= 0xffffffff00000000ULL; |
266 | s->pim[2].la |= val; |
267 | ppc440_pcix_update_pim(s, 2); |
268 | break; |
269 | case PCIX0_PIM2LAH: |
270 | s->pim[2].la &= 0xffffffffULL; |
271 | s->pim[2].la |= val << 32; |
272 | ppc440_pcix_update_pim(s, 2); |
273 | break; |
274 | |
275 | case PCIX0_STS: |
276 | s->sts = val; |
277 | break; |
278 | |
279 | case PCIX0_PIM0SAH: |
280 | s->pim[0].sa &= 0xffffffffULL; |
281 | s->pim[0].sa |= val << 32; |
282 | ppc440_pcix_update_pim(s, 0); |
283 | break; |
284 | case PCIX0_PIM2SAH: |
285 | s->pim[2].sa &= 0xffffffffULL; |
286 | s->pim[2].sa |= val << 32; |
287 | ppc440_pcix_update_pim(s, 2); |
288 | break; |
289 | |
290 | default: |
291 | qemu_log_mask(LOG_UNIMP, |
292 | "%s: unhandled PCI internal register 0x%" HWADDR_PRIx"\n" , |
293 | __func__, addr); |
294 | break; |
295 | } |
296 | } |
297 | |
298 | static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr, |
299 | unsigned size) |
300 | { |
301 | struct PPC440PCIXState *s = opaque; |
302 | uint32_t val; |
303 | |
304 | switch (addr) { |
305 | case PCI_VENDOR_ID ... PCI_MAX_LAT: |
306 | val = ldl_le_p(s->dev->config + addr); |
307 | break; |
308 | |
309 | case PCIX0_POM0LAL: |
310 | val = s->pom[0].la; |
311 | break; |
312 | case PCIX0_POM0LAH: |
313 | val = s->pom[0].la >> 32; |
314 | break; |
315 | case PCIX0_POM0SA: |
316 | val = s->pom[0].sa; |
317 | break; |
318 | case PCIX0_POM0PCIAL: |
319 | val = s->pom[0].pcia; |
320 | break; |
321 | case PCIX0_POM0PCIAH: |
322 | val = s->pom[0].pcia >> 32; |
323 | break; |
324 | case PCIX0_POM1LAL: |
325 | val = s->pom[1].la; |
326 | break; |
327 | case PCIX0_POM1LAH: |
328 | val = s->pom[1].la >> 32; |
329 | break; |
330 | case PCIX0_POM1SA: |
331 | val = s->pom[1].sa; |
332 | break; |
333 | case PCIX0_POM1PCIAL: |
334 | val = s->pom[1].pcia; |
335 | break; |
336 | case PCIX0_POM1PCIAH: |
337 | val = s->pom[1].pcia >> 32; |
338 | break; |
339 | case PCIX0_POM2SA: |
340 | val = s->pom[2].sa; |
341 | break; |
342 | |
343 | case PCIX0_PIM0SAL: |
344 | val = s->pim[0].sa; |
345 | break; |
346 | case PCIX0_PIM0LAL: |
347 | val = s->pim[0].la; |
348 | break; |
349 | case PCIX0_PIM0LAH: |
350 | val = s->pim[0].la >> 32; |
351 | break; |
352 | case PCIX0_PIM1SA: |
353 | val = s->pim[1].sa; |
354 | break; |
355 | case PCIX0_PIM1LAL: |
356 | val = s->pim[1].la; |
357 | break; |
358 | case PCIX0_PIM1LAH: |
359 | val = s->pim[1].la >> 32; |
360 | break; |
361 | case PCIX0_PIM2SAL: |
362 | val = s->pim[2].sa; |
363 | break; |
364 | case PCIX0_PIM2LAL: |
365 | val = s->pim[2].la; |
366 | break; |
367 | case PCIX0_PIM2LAH: |
368 | val = s->pim[2].la >> 32; |
369 | break; |
370 | |
371 | case PCIX0_STS: |
372 | val = s->sts; |
373 | break; |
374 | |
375 | case PCIX0_PIM0SAH: |
376 | val = s->pim[0].sa >> 32; |
377 | break; |
378 | case PCIX0_PIM2SAH: |
379 | val = s->pim[2].sa >> 32; |
380 | break; |
381 | |
382 | default: |
383 | qemu_log_mask(LOG_UNIMP, |
384 | "%s: invalid PCI internal register 0x%" HWADDR_PRIx "\n" , |
385 | __func__, addr); |
386 | val = 0; |
387 | } |
388 | |
389 | trace_ppc440_pcix_reg_read(addr, val); |
390 | return val; |
391 | } |
392 | |
393 | static const MemoryRegionOps pci_reg_ops = { |
394 | .read = ppc440_pcix_reg_read4, |
395 | .write = ppc440_pcix_reg_write4, |
396 | .endianness = DEVICE_LITTLE_ENDIAN, |
397 | }; |
398 | |
399 | static void ppc440_pcix_reset(DeviceState *dev) |
400 | { |
401 | struct PPC440PCIXState *s = PPC440_PCIX_HOST_BRIDGE(dev); |
402 | int i; |
403 | |
404 | for (i = 0; i < PPC440_PCIX_NR_POMS; i++) { |
405 | ppc440_pcix_clear_region(get_system_memory(), &s->pom[i].mr); |
406 | } |
407 | for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) { |
408 | ppc440_pcix_clear_region(&s->bm, &s->pim[i].mr); |
409 | } |
410 | memset(s->pom, 0, sizeof(s->pom)); |
411 | memset(s->pim, 0, sizeof(s->pim)); |
412 | for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) { |
413 | s->pim[i].sa = 0xffffffff00000000ULL; |
414 | } |
415 | s->sts = 0; |
416 | } |
417 | |
418 | /* All pins from each slot are tied to a single board IRQ. |
419 | * This may need further refactoring for other boards. */ |
420 | static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) |
421 | { |
422 | trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0); |
423 | return 0; |
424 | } |
425 | |
426 | static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level) |
427 | { |
428 | qemu_irq *pci_irq = opaque; |
429 | |
430 | trace_ppc440_pcix_set_irq(irq_num); |
431 | if (irq_num < 0) { |
432 | error_report("%s: PCI irq %d" , __func__, irq_num); |
433 | return; |
434 | } |
435 | qemu_set_irq(*pci_irq, level); |
436 | } |
437 | |
438 | static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) |
439 | { |
440 | PPC440PCIXState *s = opaque; |
441 | |
442 | return &s->bm_as; |
443 | } |
444 | |
445 | /* The default pci_host_data_{read,write} functions in pci/pci_host.c |
446 | * deny access to registers without bit 31 set but our clients want |
447 | * this to work so we have to override these here */ |
448 | static void pci_host_data_write(void *opaque, hwaddr addr, |
449 | uint64_t val, unsigned len) |
450 | { |
451 | PCIHostState *s = opaque; |
452 | pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); |
453 | } |
454 | |
455 | static uint64_t pci_host_data_read(void *opaque, |
456 | hwaddr addr, unsigned len) |
457 | { |
458 | PCIHostState *s = opaque; |
459 | uint32_t val; |
460 | val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); |
461 | return val; |
462 | } |
463 | |
464 | const MemoryRegionOps ppc440_pcix_host_data_ops = { |
465 | .read = pci_host_data_read, |
466 | .write = pci_host_data_write, |
467 | .endianness = DEVICE_LITTLE_ENDIAN, |
468 | }; |
469 | |
470 | static void ppc440_pcix_realize(DeviceState *dev, Error **errp) |
471 | { |
472 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
473 | PPC440PCIXState *s; |
474 | PCIHostState *h; |
475 | |
476 | h = PCI_HOST_BRIDGE(dev); |
477 | s = PPC440_PCIX_HOST_BRIDGE(dev); |
478 | |
479 | sysbus_init_irq(sbd, &s->irq); |
480 | memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory" , UINT64_MAX); |
481 | h->bus = pci_register_root_bus(dev, NULL, ppc440_pcix_set_irq, |
482 | ppc440_pcix_map_irq, &s->irq, &s->busmem, |
483 | get_system_io(), PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS); |
484 | |
485 | s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge" ); |
486 | |
487 | memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix" , UINT64_MAX); |
488 | memory_region_add_subregion(&s->bm, 0x0, &s->busmem); |
489 | address_space_init(&s->bm_as, &s->bm, "pci-bm" ); |
490 | pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); |
491 | |
492 | memory_region_init(&s->container, OBJECT(s), "pci-container" , PCI_ALL_SIZE); |
493 | memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, |
494 | h, "pci-conf-idx" , 4); |
495 | memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops, |
496 | h, "pci-conf-data" , 4); |
497 | memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, |
498 | "pci.reg" , PPC440_REG_SIZE); |
499 | memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); |
500 | memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); |
501 | memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem); |
502 | sysbus_init_mmio(sbd, &s->container); |
503 | } |
504 | |
505 | static void ppc440_pcix_class_init(ObjectClass *klass, void *data) |
506 | { |
507 | DeviceClass *dc = DEVICE_CLASS(klass); |
508 | |
509 | dc->realize = ppc440_pcix_realize; |
510 | dc->reset = ppc440_pcix_reset; |
511 | } |
512 | |
513 | static const TypeInfo ppc440_pcix_info = { |
514 | .name = TYPE_PPC440_PCIX_HOST_BRIDGE, |
515 | .parent = TYPE_PCI_HOST_BRIDGE, |
516 | .instance_size = sizeof(PPC440PCIXState), |
517 | .class_init = ppc440_pcix_class_init, |
518 | }; |
519 | |
520 | static void ppc440_pcix_register_types(void) |
521 | { |
522 | type_register_static(&ppc440_pcix_info); |
523 | } |
524 | |
525 | type_init(ppc440_pcix_register_types) |
526 | |