123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790 |
- /*
- * Copyright (c) 2018, Impinj, Inc.
- *
- * Designware PCIe IP block emulation
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see
- * <http://www.gnu.org/licenses/>.
- */
- #include "qemu/osdep.h"
- #include "qapi/error.h"
- #include "qemu/module.h"
- #include "qemu/log.h"
- #include "hw/pci/msi.h"
- #include "hw/pci/pci_bridge.h"
- #include "hw/pci/pci_host.h"
- #include "hw/pci/pcie_port.h"
- #include "hw/qdev-properties.h"
- #include "migration/vmstate.h"
- #include "hw/irq.h"
- #include "hw/pci-host/designware.h"
- #define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710
- #define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C
- #define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4)
- #define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
- #define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17)
- #define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820
- #define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824
- #define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828
- #define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C
- #define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830
- #define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900
- #define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31)
- #define DESIGNWARE_PCIE_ATU_CR1 0x904
- #define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0)
- #define DESIGNWARE_PCIE_ATU_CR2 0x908
- #define DESIGNWARE_PCIE_ATU_ENABLE BIT(31)
- #define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C
- #define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910
- #define DESIGNWARE_PCIE_ATU_LIMIT 0x914
- #define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918
- #define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff)
- #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
- #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C
- static void designware_pcie_root_bus_class_init(ObjectClass *klass, void *data)
- {
- BusClass *k = BUS_CLASS(klass);
- /*
- * Designware has only a single root complex. Enforce the limit on the
- * parent bus
- */
- k->max_dev = 1;
- }
- static DesignwarePCIEHost *
- designware_pcie_root_to_host(DesignwarePCIERoot *root)
- {
- BusState *bus = qdev_get_parent_bus(DEVICE(root));
- return DESIGNWARE_PCIE_HOST(bus->parent);
- }
- static uint64_t designware_pcie_root_msi_read(void *opaque, hwaddr addr,
- unsigned size)
- {
- /*
- * Attempts to read from the MSI address are undefined in
- * the PCI specifications. For this hardware, the datasheet
- * specifies that a read from the magic address is simply not
- * intercepted by the MSI controller, and will go out to the
- * AHB/AXI bus like any other PCI-device-initiated DMA read.
- * This is not trivial to implement in QEMU, so since
- * well-behaved guests won't ever ask a PCI device to DMA from
- * this address we just log the missing functionality.
- */
- qemu_log_mask(LOG_UNIMP, "%s not implemented\n", __func__);
- return 0;
- }
- static void designware_pcie_root_msi_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned len)
- {
- DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque);
- DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
- root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable;
- if (root->msi.intr[0].status & ~root->msi.intr[0].mask) {
- qemu_set_irq(host->pci.msi, 1);
- }
- }
- static const MemoryRegionOps designware_pci_host_msi_ops = {
- .read = designware_pcie_root_msi_read,
- .write = designware_pcie_root_msi_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- };
- static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root)
- {
- MemoryRegion *mem = &root->msi.iomem;
- const uint64_t base = root->msi.base;
- const bool enable = root->msi.intr[0].enable;
- memory_region_set_address(mem, base);
- memory_region_set_enabled(mem, enable);
- }
- static DesignwarePCIEViewport *
- designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root)
- {
- const unsigned int idx = root->atu_viewport & 0xF;
- const unsigned int dir =
- !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND);
- return &root->viewports[dir][idx];
- }
- static uint32_t
- designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len)
- {
- DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
- DesignwarePCIEViewport *viewport =
- designware_pcie_root_get_current_viewport(root);
- uint32_t val;
- switch (address) {
- case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
- /*
- * Linux guest uses this register only to configure number of
- * PCIE lane (which in our case is irrelevant) and doesn't
- * really care about the value it reads from this register
- */
- val = 0xDEADBEEF;
- break;
- case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
- /*
- * To make sure that any code in guest waiting for speed
- * change does not time out we always report
- * PORT_LOGIC_SPEED_CHANGE as set
- */
- val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE;
- break;
- case DESIGNWARE_PCIE_MSI_ADDR_LO:
- val = root->msi.base;
- break;
- case DESIGNWARE_PCIE_MSI_ADDR_HI:
- val = root->msi.base >> 32;
- break;
- case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
- val = root->msi.intr[0].enable;
- break;
- case DESIGNWARE_PCIE_MSI_INTR0_MASK:
- val = root->msi.intr[0].mask;
- break;
- case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
- val = root->msi.intr[0].status;
- break;
- case DESIGNWARE_PCIE_PHY_DEBUG_R1:
- val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
- break;
- case DESIGNWARE_PCIE_ATU_VIEWPORT:
- val = root->atu_viewport;
- break;
- case DESIGNWARE_PCIE_ATU_LOWER_BASE:
- val = viewport->base;
- break;
- case DESIGNWARE_PCIE_ATU_UPPER_BASE:
- val = viewport->base >> 32;
- break;
- case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
- val = viewport->target;
- break;
- case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
- val = viewport->target >> 32;
- break;
- case DESIGNWARE_PCIE_ATU_LIMIT:
- val = viewport->limit;
- break;
- case DESIGNWARE_PCIE_ATU_CR1:
- case DESIGNWARE_PCIE_ATU_CR2:
- val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) /
- sizeof(uint32_t)];
- break;
- default:
- val = pci_default_read_config(d, address, len);
- break;
- }
- return val;
- }
- static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr,
- uint64_t *val, unsigned len)
- {
- DesignwarePCIEViewport *viewport = opaque;
- DesignwarePCIERoot *root = viewport->root;
- const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target);
- const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target);
- PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root));
- PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn);
- if (pcidev) {
- addr &= pci_config_size(pcidev) - 1;
- if (val) {
- pci_host_config_write_common(pcidev, addr,
- pci_config_size(pcidev),
- *val, len);
- } else {
- return pci_host_config_read_common(pcidev, addr,
- pci_config_size(pcidev),
- len);
- }
- }
- return UINT64_MAX;
- }
- static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr,
- unsigned len)
- {
- return designware_pcie_root_data_access(opaque, addr, NULL, len);
- }
- static void designware_pcie_root_data_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned len)
- {
- designware_pcie_root_data_access(opaque, addr, &val, len);
- }
- static const MemoryRegionOps designware_pci_host_conf_ops = {
- .read = designware_pcie_root_data_read,
- .write = designware_pcie_root_data_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 1,
- .max_access_size = 4,
- },
- };
- static void designware_pcie_update_viewport(DesignwarePCIERoot *root,
- DesignwarePCIEViewport *viewport)
- {
- const uint64_t target = viewport->target;
- const uint64_t base = viewport->base;
- const uint64_t size = (uint64_t)viewport->limit - base + 1;
- const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE;
- MemoryRegion *current, *other;
- if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) {
- current = &viewport->mem;
- other = &viewport->cfg;
- memory_region_set_alias_offset(current, target);
- } else {
- current = &viewport->cfg;
- other = &viewport->mem;
- }
- /*
- * An outbound viewport can be reconfigure from being MEM to CFG,
- * to account for that we disable the "other" memory region that
- * becomes unused due to that fact.
- */
- memory_region_set_enabled(other, false);
- if (enabled) {
- memory_region_set_size(current, size);
- memory_region_set_address(current, base);
- }
- memory_region_set_enabled(current, enabled);
- }
- static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address,
- uint32_t val, int len)
- {
- DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d);
- DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
- DesignwarePCIEViewport *viewport =
- designware_pcie_root_get_current_viewport(root);
- switch (address) {
- case DESIGNWARE_PCIE_PORT_LINK_CONTROL:
- case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL:
- case DESIGNWARE_PCIE_PHY_DEBUG_R1:
- /* No-op */
- break;
- case DESIGNWARE_PCIE_MSI_ADDR_LO:
- root->msi.base &= 0xFFFFFFFF00000000ULL;
- root->msi.base |= val;
- designware_pcie_root_update_msi_mapping(root);
- break;
- case DESIGNWARE_PCIE_MSI_ADDR_HI:
- root->msi.base &= 0x00000000FFFFFFFFULL;
- root->msi.base |= (uint64_t)val << 32;
- designware_pcie_root_update_msi_mapping(root);
- break;
- case DESIGNWARE_PCIE_MSI_INTR0_ENABLE:
- root->msi.intr[0].enable = val;
- designware_pcie_root_update_msi_mapping(root);
- break;
- case DESIGNWARE_PCIE_MSI_INTR0_MASK:
- root->msi.intr[0].mask = val;
- break;
- case DESIGNWARE_PCIE_MSI_INTR0_STATUS:
- root->msi.intr[0].status ^= val;
- if (!root->msi.intr[0].status) {
- qemu_set_irq(host->pci.msi, 0);
- }
- break;
- case DESIGNWARE_PCIE_ATU_VIEWPORT:
- val &= DESIGNWARE_PCIE_ATU_REGION_INBOUND |
- (DESIGNWARE_PCIE_NUM_VIEWPORTS - 1);
- root->atu_viewport = val;
- break;
- case DESIGNWARE_PCIE_ATU_LOWER_BASE:
- viewport->base &= 0xFFFFFFFF00000000ULL;
- viewport->base |= val;
- break;
- case DESIGNWARE_PCIE_ATU_UPPER_BASE:
- viewport->base &= 0x00000000FFFFFFFFULL;
- viewport->base |= (uint64_t)val << 32;
- break;
- case DESIGNWARE_PCIE_ATU_LOWER_TARGET:
- viewport->target &= 0xFFFFFFFF00000000ULL;
- viewport->target |= val;
- break;
- case DESIGNWARE_PCIE_ATU_UPPER_TARGET:
- viewport->target &= 0x00000000FFFFFFFFULL;
- viewport->target |= (uint64_t)val << 32;
- break;
- case DESIGNWARE_PCIE_ATU_LIMIT:
- viewport->limit = val;
- break;
- case DESIGNWARE_PCIE_ATU_CR1:
- viewport->cr[0] = val;
- break;
- case DESIGNWARE_PCIE_ATU_CR2:
- viewport->cr[1] = val;
- designware_pcie_update_viewport(root, viewport);
- break;
- default:
- pci_bridge_write_config(d, address, val, len);
- break;
- }
- }
- static char *designware_pcie_viewport_name(const char *direction,
- unsigned int i,
- const char *type)
- {
- return g_strdup_printf("PCI %s Viewport %u [%s]",
- direction, i, type);
- }
- static void designware_pcie_root_realize(PCIDevice *dev, Error **errp)
- {
- DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev);
- DesignwarePCIEHost *host = designware_pcie_root_to_host(root);
- MemoryRegion *host_mem = get_system_memory();
- MemoryRegion *address_space = &host->pci.memory;
- PCIBridge *br = PCI_BRIDGE(dev);
- DesignwarePCIEViewport *viewport;
- /*
- * Dummy values used for initial configuration of MemoryRegions
- * that belong to a given viewport
- */
- const hwaddr dummy_offset = 0;
- const uint64_t dummy_size = 4;
- size_t i;
- br->bus_name = "dw-pcie";
- pci_set_word(dev->config + PCI_COMMAND,
- PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- pci_config_set_interrupt_pin(dev->config, 1);
- pci_bridge_initfn(dev, TYPE_PCIE_BUS);
- pcie_port_init_reg(dev);
- pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT,
- 0, &error_fatal);
- msi_nonbroken = true;
- msi_init(dev, 0x50, 32, true, true, &error_fatal);
- for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) {
- MemoryRegion *source, *destination, *mem;
- const char *direction;
- char *name;
- viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i];
- viewport->inbound = true;
- viewport->base = 0x0000000000000000ULL;
- viewport->target = 0x0000000000000000ULL;
- viewport->limit = UINT32_MAX;
- viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
- source = &host->pci.address_space_root;
- destination = host_mem;
- direction = "Inbound";
- /*
- * Configure MemoryRegion implementing PCI -> CPU memory
- * access
- */
- mem = &viewport->mem;
- name = designware_pcie_viewport_name(direction, i, "MEM");
- memory_region_init_alias(mem, OBJECT(root), name, destination,
- dummy_offset, dummy_size);
- memory_region_add_subregion_overlap(source, dummy_offset, mem, -1);
- memory_region_set_enabled(mem, false);
- g_free(name);
- viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i];
- viewport->root = root;
- viewport->inbound = false;
- viewport->base = 0x0000000000000000ULL;
- viewport->target = 0x0000000000000000ULL;
- viewport->limit = UINT32_MAX;
- viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM;
- destination = &host->pci.memory;
- direction = "Outbound";
- source = host_mem;
- /*
- * Configure MemoryRegion implementing CPU -> PCI memory
- * access
- */
- mem = &viewport->mem;
- name = designware_pcie_viewport_name(direction, i, "MEM");
- memory_region_init_alias(mem, OBJECT(root), name, destination,
- dummy_offset, dummy_size);
- memory_region_add_subregion(source, dummy_offset, mem);
- memory_region_set_enabled(mem, false);
- g_free(name);
- /*
- * Configure MemoryRegion implementing access to configuration
- * space
- */
- mem = &viewport->cfg;
- name = designware_pcie_viewport_name(direction, i, "CFG");
- memory_region_init_io(&viewport->cfg, OBJECT(root),
- &designware_pci_host_conf_ops,
- viewport, name, dummy_size);
- memory_region_add_subregion(source, dummy_offset, mem);
- memory_region_set_enabled(mem, false);
- g_free(name);
- }
- /*
- * If no inbound iATU windows are configured, HW defaults to
- * letting inbound TLPs to pass in. We emulate that by explicitly
- * configuring first inbound window to cover all of target's
- * address space.
- *
- * NOTE: This will not work correctly for the case when first
- * configured inbound window is window 0
- */
- viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0];
- viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE;
- designware_pcie_update_viewport(root, viewport);
- memory_region_init_io(&root->msi.iomem, OBJECT(root),
- &designware_pci_host_msi_ops,
- root, "pcie-msi", 0x4);
- /*
- * We initially place MSI interrupt I/O region at address 0 and
- * disable it. It'll be later moved to correct offset and enabled
- * in designware_pcie_root_update_msi_mapping() as a part of
- * initialization done by guest OS
- */
- memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem);
- memory_region_set_enabled(&root->msi.iomem, false);
- }
- static void designware_pcie_set_irq(void *opaque, int irq_num, int level)
- {
- DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque);
- qemu_set_irq(host->pci.irqs[irq_num], level);
- }
- static const char *
- designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus)
- {
- return "0000:00";
- }
- static const VMStateDescription vmstate_designware_pcie_msi_bank = {
- .name = "designware-pcie-msi-bank",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32(enable, DesignwarePCIEMSIBank),
- VMSTATE_UINT32(mask, DesignwarePCIEMSIBank),
- VMSTATE_UINT32(status, DesignwarePCIEMSIBank),
- VMSTATE_END_OF_LIST()
- }
- };
- static const VMStateDescription vmstate_designware_pcie_msi = {
- .name = "designware-pcie-msi",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT64(base, DesignwarePCIEMSI),
- VMSTATE_STRUCT_ARRAY(intr,
- DesignwarePCIEMSI,
- DESIGNWARE_PCIE_NUM_MSI_BANKS,
- 1,
- vmstate_designware_pcie_msi_bank,
- DesignwarePCIEMSIBank),
- VMSTATE_END_OF_LIST()
- }
- };
- static const VMStateDescription vmstate_designware_pcie_viewport = {
- .name = "designware-pcie-viewport",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT64(base, DesignwarePCIEViewport),
- VMSTATE_UINT64(target, DesignwarePCIEViewport),
- VMSTATE_UINT32(limit, DesignwarePCIEViewport),
- VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2),
- VMSTATE_END_OF_LIST()
- }
- };
- static const VMStateDescription vmstate_designware_pcie_root = {
- .name = "designware-pcie-root",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
- VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot),
- VMSTATE_STRUCT_2DARRAY(viewports,
- DesignwarePCIERoot,
- 2,
- DESIGNWARE_PCIE_NUM_VIEWPORTS,
- 1,
- vmstate_designware_pcie_viewport,
- DesignwarePCIEViewport),
- VMSTATE_STRUCT(msi,
- DesignwarePCIERoot,
- 1,
- vmstate_designware_pcie_msi,
- DesignwarePCIEMSI),
- VMSTATE_END_OF_LIST()
- }
- };
- static void designware_pcie_root_class_init(ObjectClass *klass, void *data)
- {
- PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
- k->vendor_id = PCI_VENDOR_ID_SYNOPSYS;
- k->device_id = 0xABCD;
- k->revision = 0;
- k->class_id = PCI_CLASS_BRIDGE_PCI;
- k->exit = pci_bridge_exitfn;
- k->realize = designware_pcie_root_realize;
- k->config_read = designware_pcie_root_config_read;
- k->config_write = designware_pcie_root_config_write;
- device_class_set_legacy_reset(dc, pci_bridge_reset);
- /*
- * PCI-facing part of the host bridge, not usable without the
- * host-facing part, which can't be device_add'ed, yet.
- */
- dc->user_creatable = false;
- dc->vmsd = &vmstate_designware_pcie_root;
- }
- static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr,
- unsigned int size)
- {
- PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
- PCIDevice *device = pci_find_device(pci->bus, 0, 0);
- return pci_host_config_read_common(device,
- addr,
- pci_config_size(device),
- size);
- }
- static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned int size)
- {
- PCIHostState *pci = PCI_HOST_BRIDGE(opaque);
- PCIDevice *device = pci_find_device(pci->bus, 0, 0);
- return pci_host_config_write_common(device,
- addr,
- pci_config_size(device),
- val, size);
- }
- static const MemoryRegionOps designware_pci_mmio_ops = {
- .read = designware_pcie_host_mmio_read,
- .write = designware_pcie_host_mmio_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .impl = {
- /*
- * Our device would not work correctly if the guest was doing
- * unaligned access. This might not be a limitation on the real
- * device but in practice there is no reason for a guest to access
- * this device unaligned.
- */
- .min_access_size = 4,
- .max_access_size = 4,
- .unaligned = false,
- },
- };
- static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque,
- int devfn)
- {
- DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque);
- return &s->pci.address_space;
- }
- static const PCIIOMMUOps designware_iommu_ops = {
- .get_address_space = designware_pcie_host_set_iommu,
- };
- static void designware_pcie_host_realize(DeviceState *dev, Error **errp)
- {
- PCIHostState *pci = PCI_HOST_BRIDGE(dev);
- DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- size_t i;
- for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) {
- sysbus_init_irq(sbd, &s->pci.irqs[i]);
- }
- sysbus_init_irq(sbd, &s->pci.msi);
- memory_region_init_io(&s->mmio,
- OBJECT(s),
- &designware_pci_mmio_ops,
- s,
- "pcie.reg", 4 * 1024);
- sysbus_init_mmio(sbd, &s->mmio);
- memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16);
- memory_region_init(&s->pci.memory, OBJECT(s),
- "pcie-bus-memory",
- UINT64_MAX);
- pci->bus = pci_register_root_bus(dev, "pcie",
- designware_pcie_set_irq,
- pci_swizzle_map_irq_fn,
- s,
- &s->pci.memory,
- &s->pci.io,
- 0, 4,
- TYPE_DESIGNWARE_PCIE_ROOT_BUS);
- pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
- memory_region_init(&s->pci.address_space_root,
- OBJECT(s),
- "pcie-bus-address-space-root",
- UINT64_MAX);
- memory_region_add_subregion(&s->pci.address_space_root,
- 0x0, &s->pci.memory);
- address_space_init(&s->pci.address_space,
- &s->pci.address_space_root,
- "pcie-bus-address-space");
- pci_setup_iommu(pci->bus, &designware_iommu_ops, s);
- qdev_realize(DEVICE(&s->root), BUS(pci->bus), &error_fatal);
- }
- static const VMStateDescription vmstate_designware_pcie_host = {
- .name = "designware-pcie-host",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_STRUCT(root,
- DesignwarePCIEHost,
- 1,
- vmstate_designware_pcie_root,
- DesignwarePCIERoot),
- VMSTATE_END_OF_LIST()
- }
- };
- static void designware_pcie_host_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
- hc->root_bus_path = designware_pcie_host_root_bus_path;
- dc->realize = designware_pcie_host_realize;
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
- dc->fw_name = "pci";
- dc->vmsd = &vmstate_designware_pcie_host;
- }
- static void designware_pcie_host_init(Object *obj)
- {
- DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj);
- DesignwarePCIERoot *root = &s->root;
- object_initialize_child(obj, "root", root, TYPE_DESIGNWARE_PCIE_ROOT);
- qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
- qdev_prop_set_bit(DEVICE(root), "multifunction", false);
- }
- static const TypeInfo designware_pcie_types[] = {
- {
- .name = TYPE_DESIGNWARE_PCIE_ROOT_BUS,
- .parent = TYPE_PCIE_BUS,
- .instance_size = sizeof(DesignwarePCIERootBus),
- .class_init = designware_pcie_root_bus_class_init,
- }, {
- .name = TYPE_DESIGNWARE_PCIE_HOST,
- .parent = TYPE_PCI_HOST_BRIDGE,
- .instance_size = sizeof(DesignwarePCIEHost),
- .instance_init = designware_pcie_host_init,
- .class_init = designware_pcie_host_class_init,
- }, {
- .name = TYPE_DESIGNWARE_PCIE_ROOT,
- .parent = TYPE_PCI_BRIDGE,
- .instance_size = sizeof(DesignwarePCIERoot),
- .class_init = designware_pcie_root_class_init,
- .interfaces = (InterfaceInfo[]) {
- { INTERFACE_PCIE_DEVICE },
- { }
- },
- },
- };
- DEFINE_TYPES(designware_pcie_types)
|