1 | /* |
2 | * Copyright (c) 2018, Impinj, Inc. |
3 | * |
4 | * Designware PCIe IP block emulation |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, see |
18 | * <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | #include "qemu/osdep.h" |
22 | #include "qapi/error.h" |
23 | #include "qemu/module.h" |
24 | #include "hw/pci/msi.h" |
25 | #include "hw/pci/pci_bridge.h" |
26 | #include "hw/pci/pci_host.h" |
27 | #include "hw/pci/pcie_port.h" |
28 | #include "hw/qdev-properties.h" |
29 | #include "migration/vmstate.h" |
30 | #include "hw/irq.h" |
31 | #include "hw/pci-host/designware.h" |
32 | |
33 | #define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710 |
34 | #define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C |
35 | #define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4) |
36 | #define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C |
37 | #define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17) |
38 | #define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820 |
39 | #define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824 |
40 | #define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828 |
41 | #define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C |
42 | #define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830 |
43 | #define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900 |
44 | #define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31) |
45 | #define DESIGNWARE_PCIE_ATU_CR1 0x904 |
46 | #define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0) |
47 | #define DESIGNWARE_PCIE_ATU_CR2 0x908 |
48 | #define DESIGNWARE_PCIE_ATU_ENABLE BIT(31) |
49 | #define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C |
50 | #define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910 |
51 | #define DESIGNWARE_PCIE_ATU_LIMIT 0x914 |
52 | #define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918 |
53 | #define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff) |
54 | #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) |
55 | #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C |
56 | |
57 | #define DESIGNWARE_PCIE_IRQ_MSI 3 |
58 | |
59 | static DesignwarePCIEHost * |
60 | designware_pcie_root_to_host(DesignwarePCIERoot *root) |
61 | { |
62 | BusState *bus = qdev_get_parent_bus(DEVICE(root)); |
63 | return DESIGNWARE_PCIE_HOST(bus->parent); |
64 | } |
65 | |
66 | static void designware_pcie_root_msi_write(void *opaque, hwaddr addr, |
67 | uint64_t val, unsigned len) |
68 | { |
69 | DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque); |
70 | DesignwarePCIEHost *host = designware_pcie_root_to_host(root); |
71 | |
72 | root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable; |
73 | |
74 | if (root->msi.intr[0].status & ~root->msi.intr[0].mask) { |
75 | qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 1); |
76 | } |
77 | } |
78 | |
79 | static const MemoryRegionOps designware_pci_host_msi_ops = { |
80 | .write = designware_pcie_root_msi_write, |
81 | .endianness = DEVICE_LITTLE_ENDIAN, |
82 | .valid = { |
83 | .min_access_size = 4, |
84 | .max_access_size = 4, |
85 | }, |
86 | }; |
87 | |
88 | static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root) |
89 | |
90 | { |
91 | MemoryRegion *mem = &root->msi.iomem; |
92 | const uint64_t base = root->msi.base; |
93 | const bool enable = root->msi.intr[0].enable; |
94 | |
95 | memory_region_set_address(mem, base); |
96 | memory_region_set_enabled(mem, enable); |
97 | } |
98 | |
99 | static DesignwarePCIEViewport * |
100 | designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root) |
101 | { |
102 | const unsigned int idx = root->atu_viewport & 0xF; |
103 | const unsigned int dir = |
104 | !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND); |
105 | return &root->viewports[dir][idx]; |
106 | } |
107 | |
108 | static uint32_t |
109 | designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) |
110 | { |
111 | DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d); |
112 | DesignwarePCIEViewport *viewport = |
113 | designware_pcie_root_get_current_viewport(root); |
114 | |
115 | uint32_t val; |
116 | |
117 | switch (address) { |
118 | case DESIGNWARE_PCIE_PORT_LINK_CONTROL: |
119 | /* |
120 | * Linux guest uses this register only to configure number of |
121 | * PCIE lane (which in our case is irrelevant) and doesn't |
122 | * really care about the value it reads from this register |
123 | */ |
124 | val = 0xDEADBEEF; |
125 | break; |
126 | |
127 | case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL: |
128 | /* |
129 | * To make sure that any code in guest waiting for speed |
130 | * change does not time out we always report |
131 | * PORT_LOGIC_SPEED_CHANGE as set |
132 | */ |
133 | val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE; |
134 | break; |
135 | |
136 | case DESIGNWARE_PCIE_MSI_ADDR_LO: |
137 | val = root->msi.base; |
138 | break; |
139 | |
140 | case DESIGNWARE_PCIE_MSI_ADDR_HI: |
141 | val = root->msi.base >> 32; |
142 | break; |
143 | |
144 | case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: |
145 | val = root->msi.intr[0].enable; |
146 | break; |
147 | |
148 | case DESIGNWARE_PCIE_MSI_INTR0_MASK: |
149 | val = root->msi.intr[0].mask; |
150 | break; |
151 | |
152 | case DESIGNWARE_PCIE_MSI_INTR0_STATUS: |
153 | val = root->msi.intr[0].status; |
154 | break; |
155 | |
156 | case DESIGNWARE_PCIE_PHY_DEBUG_R1: |
157 | val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; |
158 | break; |
159 | |
160 | case DESIGNWARE_PCIE_ATU_VIEWPORT: |
161 | val = root->atu_viewport; |
162 | break; |
163 | |
164 | case DESIGNWARE_PCIE_ATU_LOWER_BASE: |
165 | val = viewport->base; |
166 | break; |
167 | |
168 | case DESIGNWARE_PCIE_ATU_UPPER_BASE: |
169 | val = viewport->base >> 32; |
170 | break; |
171 | |
172 | case DESIGNWARE_PCIE_ATU_LOWER_TARGET: |
173 | val = viewport->target; |
174 | break; |
175 | |
176 | case DESIGNWARE_PCIE_ATU_UPPER_TARGET: |
177 | val = viewport->target >> 32; |
178 | break; |
179 | |
180 | case DESIGNWARE_PCIE_ATU_LIMIT: |
181 | val = viewport->limit; |
182 | break; |
183 | |
184 | case DESIGNWARE_PCIE_ATU_CR1: |
185 | case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */ |
186 | val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) / |
187 | sizeof(uint32_t)]; |
188 | break; |
189 | |
190 | default: |
191 | val = pci_default_read_config(d, address, len); |
192 | break; |
193 | } |
194 | |
195 | return val; |
196 | } |
197 | |
198 | static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr, |
199 | uint64_t *val, unsigned len) |
200 | { |
201 | DesignwarePCIEViewport *viewport = opaque; |
202 | DesignwarePCIERoot *root = viewport->root; |
203 | |
204 | const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target); |
205 | const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target); |
206 | PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root)); |
207 | PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn); |
208 | |
209 | if (pcidev) { |
210 | addr &= pci_config_size(pcidev) - 1; |
211 | |
212 | if (val) { |
213 | pci_host_config_write_common(pcidev, addr, |
214 | pci_config_size(pcidev), |
215 | *val, len); |
216 | } else { |
217 | return pci_host_config_read_common(pcidev, addr, |
218 | pci_config_size(pcidev), |
219 | len); |
220 | } |
221 | } |
222 | |
223 | return UINT64_MAX; |
224 | } |
225 | |
226 | static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr, |
227 | unsigned len) |
228 | { |
229 | return designware_pcie_root_data_access(opaque, addr, NULL, len); |
230 | } |
231 | |
232 | static void designware_pcie_root_data_write(void *opaque, hwaddr addr, |
233 | uint64_t val, unsigned len) |
234 | { |
235 | designware_pcie_root_data_access(opaque, addr, &val, len); |
236 | } |
237 | |
238 | static const MemoryRegionOps designware_pci_host_conf_ops = { |
239 | .read = designware_pcie_root_data_read, |
240 | .write = designware_pcie_root_data_write, |
241 | .endianness = DEVICE_LITTLE_ENDIAN, |
242 | .valid = { |
243 | .min_access_size = 1, |
244 | .max_access_size = 4, |
245 | }, |
246 | }; |
247 | |
248 | static void designware_pcie_update_viewport(DesignwarePCIERoot *root, |
249 | DesignwarePCIEViewport *viewport) |
250 | { |
251 | const uint64_t target = viewport->target; |
252 | const uint64_t base = viewport->base; |
253 | const uint64_t size = (uint64_t)viewport->limit - base + 1; |
254 | const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE; |
255 | |
256 | MemoryRegion *current, *other; |
257 | |
258 | if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) { |
259 | current = &viewport->mem; |
260 | other = &viewport->cfg; |
261 | memory_region_set_alias_offset(current, target); |
262 | } else { |
263 | current = &viewport->cfg; |
264 | other = &viewport->mem; |
265 | } |
266 | |
267 | /* |
268 | * An outbound viewport can be reconfigure from being MEM to CFG, |
269 | * to account for that we disable the "other" memory region that |
270 | * becomes unused due to that fact. |
271 | */ |
272 | memory_region_set_enabled(other, false); |
273 | if (enabled) { |
274 | memory_region_set_size(current, size); |
275 | memory_region_set_address(current, base); |
276 | } |
277 | memory_region_set_enabled(current, enabled); |
278 | } |
279 | |
280 | static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, |
281 | uint32_t val, int len) |
282 | { |
283 | DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d); |
284 | DesignwarePCIEHost *host = designware_pcie_root_to_host(root); |
285 | DesignwarePCIEViewport *viewport = |
286 | designware_pcie_root_get_current_viewport(root); |
287 | |
288 | switch (address) { |
289 | case DESIGNWARE_PCIE_PORT_LINK_CONTROL: |
290 | case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL: |
291 | case DESIGNWARE_PCIE_PHY_DEBUG_R1: |
292 | /* No-op */ |
293 | break; |
294 | |
295 | case DESIGNWARE_PCIE_MSI_ADDR_LO: |
296 | root->msi.base &= 0xFFFFFFFF00000000ULL; |
297 | root->msi.base |= val; |
298 | designware_pcie_root_update_msi_mapping(root); |
299 | break; |
300 | |
301 | case DESIGNWARE_PCIE_MSI_ADDR_HI: |
302 | root->msi.base &= 0x00000000FFFFFFFFULL; |
303 | root->msi.base |= (uint64_t)val << 32; |
304 | designware_pcie_root_update_msi_mapping(root); |
305 | break; |
306 | |
307 | case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: |
308 | root->msi.intr[0].enable = val; |
309 | designware_pcie_root_update_msi_mapping(root); |
310 | break; |
311 | |
312 | case DESIGNWARE_PCIE_MSI_INTR0_MASK: |
313 | root->msi.intr[0].mask = val; |
314 | break; |
315 | |
316 | case DESIGNWARE_PCIE_MSI_INTR0_STATUS: |
317 | root->msi.intr[0].status ^= val; |
318 | if (!root->msi.intr[0].status) { |
319 | qemu_set_irq(host->pci.irqs[DESIGNWARE_PCIE_IRQ_MSI], 0); |
320 | } |
321 | break; |
322 | |
323 | case DESIGNWARE_PCIE_ATU_VIEWPORT: |
324 | root->atu_viewport = val; |
325 | break; |
326 | |
327 | case DESIGNWARE_PCIE_ATU_LOWER_BASE: |
328 | viewport->base &= 0xFFFFFFFF00000000ULL; |
329 | viewport->base |= val; |
330 | break; |
331 | |
332 | case DESIGNWARE_PCIE_ATU_UPPER_BASE: |
333 | viewport->base &= 0x00000000FFFFFFFFULL; |
334 | viewport->base |= (uint64_t)val << 32; |
335 | break; |
336 | |
337 | case DESIGNWARE_PCIE_ATU_LOWER_TARGET: |
338 | viewport->target &= 0xFFFFFFFF00000000ULL; |
339 | viewport->target |= val; |
340 | break; |
341 | |
342 | case DESIGNWARE_PCIE_ATU_UPPER_TARGET: |
343 | viewport->target &= 0x00000000FFFFFFFFULL; |
344 | viewport->target |= val; |
345 | break; |
346 | |
347 | case DESIGNWARE_PCIE_ATU_LIMIT: |
348 | viewport->limit = val; |
349 | break; |
350 | |
351 | case DESIGNWARE_PCIE_ATU_CR1: |
352 | viewport->cr[0] = val; |
353 | break; |
354 | case DESIGNWARE_PCIE_ATU_CR2: |
355 | viewport->cr[1] = val; |
356 | designware_pcie_update_viewport(root, viewport); |
357 | break; |
358 | |
359 | default: |
360 | pci_bridge_write_config(d, address, val, len); |
361 | break; |
362 | } |
363 | } |
364 | |
365 | static char *designware_pcie_viewport_name(const char *direction, |
366 | unsigned int i, |
367 | const char *type) |
368 | { |
369 | return g_strdup_printf("PCI %s Viewport %u [%s]" , |
370 | direction, i, type); |
371 | } |
372 | |
373 | static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) |
374 | { |
375 | DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev); |
376 | DesignwarePCIEHost *host = designware_pcie_root_to_host(root); |
377 | MemoryRegion *address_space = &host->pci.memory; |
378 | PCIBridge *br = PCI_BRIDGE(dev); |
379 | DesignwarePCIEViewport *viewport; |
380 | /* |
381 | * Dummy values used for initial configuration of MemoryRegions |
382 | * that belong to a given viewport |
383 | */ |
384 | const hwaddr dummy_offset = 0; |
385 | const uint64_t dummy_size = 4; |
386 | size_t i; |
387 | |
388 | br->bus_name = "dw-pcie" ; |
389 | |
390 | pci_set_word(dev->config + PCI_COMMAND, |
391 | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); |
392 | |
393 | pci_config_set_interrupt_pin(dev->config, 1); |
394 | pci_bridge_initfn(dev, TYPE_PCIE_BUS); |
395 | |
396 | pcie_port_init_reg(dev); |
397 | |
398 | pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT, |
399 | 0, &error_fatal); |
400 | |
401 | msi_nonbroken = true; |
402 | msi_init(dev, 0x50, 32, true, true, &error_fatal); |
403 | |
404 | for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) { |
405 | MemoryRegion *source, *destination, *mem; |
406 | const char *direction; |
407 | char *name; |
408 | |
409 | viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i]; |
410 | viewport->inbound = true; |
411 | viewport->base = 0x0000000000000000ULL; |
412 | viewport->target = 0x0000000000000000ULL; |
413 | viewport->limit = UINT32_MAX; |
414 | viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; |
415 | |
416 | source = &host->pci.address_space_root; |
417 | destination = get_system_memory(); |
418 | direction = "Inbound" ; |
419 | |
420 | /* |
421 | * Configure MemoryRegion implementing PCI -> CPU memory |
422 | * access |
423 | */ |
424 | mem = &viewport->mem; |
425 | name = designware_pcie_viewport_name(direction, i, "MEM" ); |
426 | memory_region_init_alias(mem, OBJECT(root), name, destination, |
427 | dummy_offset, dummy_size); |
428 | memory_region_add_subregion_overlap(source, dummy_offset, mem, -1); |
429 | memory_region_set_enabled(mem, false); |
430 | g_free(name); |
431 | |
432 | viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i]; |
433 | viewport->root = root; |
434 | viewport->inbound = false; |
435 | viewport->base = 0x0000000000000000ULL; |
436 | viewport->target = 0x0000000000000000ULL; |
437 | viewport->limit = UINT32_MAX; |
438 | viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; |
439 | |
440 | destination = &host->pci.memory; |
441 | direction = "Outbound" ; |
442 | source = get_system_memory(); |
443 | |
444 | /* |
445 | * Configure MemoryRegion implementing CPU -> PCI memory |
446 | * access |
447 | */ |
448 | mem = &viewport->mem; |
449 | name = designware_pcie_viewport_name(direction, i, "MEM" ); |
450 | memory_region_init_alias(mem, OBJECT(root), name, destination, |
451 | dummy_offset, dummy_size); |
452 | memory_region_add_subregion(source, dummy_offset, mem); |
453 | memory_region_set_enabled(mem, false); |
454 | g_free(name); |
455 | |
456 | /* |
457 | * Configure MemoryRegion implementing access to configuration |
458 | * space |
459 | */ |
460 | mem = &viewport->cfg; |
461 | name = designware_pcie_viewport_name(direction, i, "CFG" ); |
462 | memory_region_init_io(&viewport->cfg, OBJECT(root), |
463 | &designware_pci_host_conf_ops, |
464 | viewport, name, dummy_size); |
465 | memory_region_add_subregion(source, dummy_offset, mem); |
466 | memory_region_set_enabled(mem, false); |
467 | g_free(name); |
468 | } |
469 | |
470 | /* |
471 | * If no inbound iATU windows are configured, HW defaults to |
472 | * letting inbound TLPs to pass in. We emulate that by exlicitly |
473 | * configuring first inbound window to cover all of target's |
474 | * address space. |
475 | * |
476 | * NOTE: This will not work correctly for the case when first |
477 | * configured inbound window is window 0 |
478 | */ |
479 | viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0]; |
480 | viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE; |
481 | designware_pcie_update_viewport(root, viewport); |
482 | |
483 | memory_region_init_io(&root->msi.iomem, OBJECT(root), |
484 | &designware_pci_host_msi_ops, |
485 | root, "pcie-msi" , 0x4); |
486 | /* |
487 | * We initially place MSI interrupt I/O region a adress 0 and |
488 | * disable it. It'll be later moved to correct offset and enabled |
489 | * in designware_pcie_root_update_msi_mapping() as a part of |
490 | * initialization done by guest OS |
491 | */ |
492 | memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem); |
493 | memory_region_set_enabled(&root->msi.iomem, false); |
494 | } |
495 | |
496 | static void designware_pcie_set_irq(void *opaque, int irq_num, int level) |
497 | { |
498 | DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque); |
499 | |
500 | qemu_set_irq(host->pci.irqs[irq_num], level); |
501 | } |
502 | |
503 | static const char * |
504 | designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) |
505 | { |
506 | return "0000:00" ; |
507 | } |
508 | |
509 | static const VMStateDescription vmstate_designware_pcie_msi_bank = { |
510 | .name = "designware-pcie-msi-bank" , |
511 | .version_id = 1, |
512 | .minimum_version_id = 1, |
513 | .fields = (VMStateField[]) { |
514 | VMSTATE_UINT32(enable, DesignwarePCIEMSIBank), |
515 | VMSTATE_UINT32(mask, DesignwarePCIEMSIBank), |
516 | VMSTATE_UINT32(status, DesignwarePCIEMSIBank), |
517 | VMSTATE_END_OF_LIST() |
518 | } |
519 | }; |
520 | |
521 | static const VMStateDescription vmstate_designware_pcie_msi = { |
522 | .name = "designware-pcie-msi" , |
523 | .version_id = 1, |
524 | .minimum_version_id = 1, |
525 | .fields = (VMStateField[]) { |
526 | VMSTATE_UINT64(base, DesignwarePCIEMSI), |
527 | VMSTATE_STRUCT_ARRAY(intr, |
528 | DesignwarePCIEMSI, |
529 | DESIGNWARE_PCIE_NUM_MSI_BANKS, |
530 | 1, |
531 | vmstate_designware_pcie_msi_bank, |
532 | DesignwarePCIEMSIBank), |
533 | VMSTATE_END_OF_LIST() |
534 | } |
535 | }; |
536 | |
537 | static const VMStateDescription vmstate_designware_pcie_viewport = { |
538 | .name = "designware-pcie-viewport" , |
539 | .version_id = 1, |
540 | .minimum_version_id = 1, |
541 | .fields = (VMStateField[]) { |
542 | VMSTATE_UINT64(base, DesignwarePCIEViewport), |
543 | VMSTATE_UINT64(target, DesignwarePCIEViewport), |
544 | VMSTATE_UINT32(limit, DesignwarePCIEViewport), |
545 | VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2), |
546 | VMSTATE_END_OF_LIST() |
547 | } |
548 | }; |
549 | |
550 | static const VMStateDescription vmstate_designware_pcie_root = { |
551 | .name = "designware-pcie-root" , |
552 | .version_id = 1, |
553 | .minimum_version_id = 1, |
554 | .fields = (VMStateField[]) { |
555 | VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), |
556 | VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot), |
557 | VMSTATE_STRUCT_2DARRAY(viewports, |
558 | DesignwarePCIERoot, |
559 | 2, |
560 | DESIGNWARE_PCIE_NUM_VIEWPORTS, |
561 | 1, |
562 | vmstate_designware_pcie_viewport, |
563 | DesignwarePCIEViewport), |
564 | VMSTATE_STRUCT(msi, |
565 | DesignwarePCIERoot, |
566 | 1, |
567 | vmstate_designware_pcie_msi, |
568 | DesignwarePCIEMSI), |
569 | VMSTATE_END_OF_LIST() |
570 | } |
571 | }; |
572 | |
573 | static void designware_pcie_root_class_init(ObjectClass *klass, void *data) |
574 | { |
575 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
576 | DeviceClass *dc = DEVICE_CLASS(klass); |
577 | |
578 | set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); |
579 | |
580 | k->vendor_id = PCI_VENDOR_ID_SYNOPSYS; |
581 | k->device_id = 0xABCD; |
582 | k->revision = 0; |
583 | k->class_id = PCI_CLASS_BRIDGE_PCI; |
584 | k->is_bridge = true; |
585 | k->exit = pci_bridge_exitfn; |
586 | k->realize = designware_pcie_root_realize; |
587 | k->config_read = designware_pcie_root_config_read; |
588 | k->config_write = designware_pcie_root_config_write; |
589 | |
590 | dc->reset = pci_bridge_reset; |
591 | /* |
592 | * PCI-facing part of the host bridge, not usable without the |
593 | * host-facing part, which can't be device_add'ed, yet. |
594 | */ |
595 | dc->user_creatable = false; |
596 | dc->vmsd = &vmstate_designware_pcie_root; |
597 | } |
598 | |
599 | static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr, |
600 | unsigned int size) |
601 | { |
602 | PCIHostState *pci = PCI_HOST_BRIDGE(opaque); |
603 | PCIDevice *device = pci_find_device(pci->bus, 0, 0); |
604 | |
605 | return pci_host_config_read_common(device, |
606 | addr, |
607 | pci_config_size(device), |
608 | size); |
609 | } |
610 | |
611 | static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr, |
612 | uint64_t val, unsigned int size) |
613 | { |
614 | PCIHostState *pci = PCI_HOST_BRIDGE(opaque); |
615 | PCIDevice *device = pci_find_device(pci->bus, 0, 0); |
616 | |
617 | return pci_host_config_write_common(device, |
618 | addr, |
619 | pci_config_size(device), |
620 | val, size); |
621 | } |
622 | |
623 | static const MemoryRegionOps designware_pci_mmio_ops = { |
624 | .read = designware_pcie_host_mmio_read, |
625 | .write = designware_pcie_host_mmio_write, |
626 | .endianness = DEVICE_LITTLE_ENDIAN, |
627 | .impl = { |
628 | /* |
629 | * Our device would not work correctly if the guest was doing |
630 | * unaligned access. This might not be a limitation on the real |
631 | * device but in practice there is no reason for a guest to access |
632 | * this device unaligned. |
633 | */ |
634 | .min_access_size = 4, |
635 | .max_access_size = 4, |
636 | .unaligned = false, |
637 | }, |
638 | }; |
639 | |
640 | static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque, |
641 | int devfn) |
642 | { |
643 | DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque); |
644 | |
645 | return &s->pci.address_space; |
646 | } |
647 | |
648 | static void designware_pcie_host_realize(DeviceState *dev, Error **errp) |
649 | { |
650 | PCIHostState *pci = PCI_HOST_BRIDGE(dev); |
651 | DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev); |
652 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
653 | size_t i; |
654 | |
655 | for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) { |
656 | sysbus_init_irq(sbd, &s->pci.irqs[i]); |
657 | } |
658 | |
659 | memory_region_init_io(&s->mmio, |
660 | OBJECT(s), |
661 | &designware_pci_mmio_ops, |
662 | s, |
663 | "pcie.reg" , 4 * 1024); |
664 | sysbus_init_mmio(sbd, &s->mmio); |
665 | |
666 | memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio" , 16); |
667 | memory_region_init(&s->pci.memory, OBJECT(s), |
668 | "pcie-bus-memory" , |
669 | UINT64_MAX); |
670 | |
671 | pci->bus = pci_register_root_bus(dev, "pcie" , |
672 | designware_pcie_set_irq, |
673 | pci_swizzle_map_irq_fn, |
674 | s, |
675 | &s->pci.memory, |
676 | &s->pci.io, |
677 | 0, 4, |
678 | TYPE_PCIE_BUS); |
679 | |
680 | memory_region_init(&s->pci.address_space_root, |
681 | OBJECT(s), |
682 | "pcie-bus-address-space-root" , |
683 | UINT64_MAX); |
684 | memory_region_add_subregion(&s->pci.address_space_root, |
685 | 0x0, &s->pci.memory); |
686 | address_space_init(&s->pci.address_space, |
687 | &s->pci.address_space_root, |
688 | "pcie-bus-address-space" ); |
689 | pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); |
690 | |
691 | qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus)); |
692 | qdev_init_nofail(DEVICE(&s->root)); |
693 | } |
694 | |
695 | static const VMStateDescription vmstate_designware_pcie_host = { |
696 | .name = "designware-pcie-host" , |
697 | .version_id = 1, |
698 | .minimum_version_id = 1, |
699 | .fields = (VMStateField[]) { |
700 | VMSTATE_STRUCT(root, |
701 | DesignwarePCIEHost, |
702 | 1, |
703 | vmstate_designware_pcie_root, |
704 | DesignwarePCIERoot), |
705 | VMSTATE_END_OF_LIST() |
706 | } |
707 | }; |
708 | |
709 | static void designware_pcie_host_class_init(ObjectClass *klass, void *data) |
710 | { |
711 | DeviceClass *dc = DEVICE_CLASS(klass); |
712 | PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); |
713 | |
714 | hc->root_bus_path = designware_pcie_host_root_bus_path; |
715 | dc->realize = designware_pcie_host_realize; |
716 | set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); |
717 | dc->fw_name = "pci" ; |
718 | dc->vmsd = &vmstate_designware_pcie_host; |
719 | } |
720 | |
721 | static void designware_pcie_host_init(Object *obj) |
722 | { |
723 | DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj); |
724 | DesignwarePCIERoot *root = &s->root; |
725 | |
726 | object_initialize_child(obj, "root" , root, sizeof(*root), |
727 | TYPE_DESIGNWARE_PCIE_ROOT, &error_abort, NULL); |
728 | qdev_prop_set_int32(DEVICE(root), "addr" , PCI_DEVFN(0, 0)); |
729 | qdev_prop_set_bit(DEVICE(root), "multifunction" , false); |
730 | } |
731 | |
732 | static const TypeInfo designware_pcie_root_info = { |
733 | .name = TYPE_DESIGNWARE_PCIE_ROOT, |
734 | .parent = TYPE_PCI_BRIDGE, |
735 | .instance_size = sizeof(DesignwarePCIERoot), |
736 | .class_init = designware_pcie_root_class_init, |
737 | .interfaces = (InterfaceInfo[]) { |
738 | { INTERFACE_PCIE_DEVICE }, |
739 | { } |
740 | }, |
741 | }; |
742 | |
743 | static const TypeInfo designware_pcie_host_info = { |
744 | .name = TYPE_DESIGNWARE_PCIE_HOST, |
745 | .parent = TYPE_PCI_HOST_BRIDGE, |
746 | .instance_size = sizeof(DesignwarePCIEHost), |
747 | .instance_init = designware_pcie_host_init, |
748 | .class_init = designware_pcie_host_class_init, |
749 | }; |
750 | |
751 | static void designware_pcie_register(void) |
752 | { |
753 | type_register_static(&designware_pcie_root_info); |
754 | type_register_static(&designware_pcie_host_info); |
755 | } |
756 | type_init(designware_pcie_register) |
757 | |