[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP blo
From: |
Michael S. Tsirkin |
Subject: |
Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block |
Date: |
Thu, 8 Feb 2018 19:45:58 +0200 |
On Tue, Feb 06, 2018 at 08:24:34PM -0800, Andrey Smirnov wrote:
> Add code needed to get a functional PCI subsytem when using in
> conjunction with upstream Linux guest (4.13+). Tested to work against
> "e1000e" (network adapter, using MSI interrupts) as well as
> "usb-ehci" (USB controller, using legacy PCI interrupts).
>
> Based on "i.MX6 Applications Processor Reference Manual" (Document
> Number: IMX6DQRM Rev. 4) as well as corresponding dirver in Linux
> kernel (circa 4.13 - 4.16 found in drivers/pci/dwc/*)
>
> Cc: Peter Maydell <address@hidden>
> Cc: Jason Wang <address@hidden>
> Cc: Philippe Mathieu-Daudé <address@hidden>
> Cc: Marcel Apfelbaum <address@hidden>
> Cc: Michael S. Tsirkin <address@hidden>
> Cc: address@hidden
> Cc: address@hidden
> Cc: address@hidden
> Signed-off-by: Andrey Smirnov <address@hidden>
> ---
> default-configs/arm-softmmu.mak | 2 +
> hw/pci-host/Makefile.objs | 2 +
> hw/pci-host/designware.c | 759
> +++++++++++++++++++++++++++++++++++++++
> include/hw/pci-host/designware.h | 97 +++++
> include/hw/pci/pci_ids.h | 2 +
> 5 files changed, 862 insertions(+)
> create mode 100644 hw/pci-host/designware.c
> create mode 100644 include/hw/pci-host/designware.h
>
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index b0d6e65038..0c5ae914ed 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -132,3 +132,5 @@ CONFIG_GPIO_KEY=y
> CONFIG_MSF2=y
> CONFIG_FW_CFG_DMA=y
> CONFIG_XILINX_AXI=y
> +CONFIG_PCI_DESIGNWARE=y
> +
> diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs
> index 4b69f737b5..6d6597c065 100644
> --- a/hw/pci-host/Makefile.objs
> +++ b/hw/pci-host/Makefile.objs
> @@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o
> common-obj-$(CONFIG_PCI_Q35) += q35.o
> common-obj-$(CONFIG_PCI_GENERIC) += gpex.o
> common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o
> +
> +common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o
> diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c
> new file mode 100644
> index 0000000000..551a881af0
> --- /dev/null
> +++ b/hw/pci-host/designware.c
> @@ -0,0 +1,759 @@
> +/*
> + * 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 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 "hw/pci/msi.h"
> +#include "hw/pci/pci_bridge.h"
> +#include "hw/pci/pci_host.h"
> +#include "hw/pci/pcie_port.h"
> +#include "hw/pci-host/designware.h"
> +
> +#define PCIE_PORT_LINK_CONTROL 0x710
> +
> +#define PCIE_PHY_DEBUG_R1 0x72C
> +#define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4)
> +
> +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
> +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
> +
> +#define PCIE_MSI_ADDR_LO 0x820
> +#define PCIE_MSI_ADDR_HI 0x824
> +#define PCIE_MSI_INTR0_ENABLE 0x828
> +#define PCIE_MSI_INTR0_MASK 0x82C
> +#define PCIE_MSI_INTR0_STATUS 0x830
> +
> +#define PCIE_ATU_VIEWPORT 0x900
> +#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
> +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
> +#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
> +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
> +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
> +#define PCIE_ATU_CR1 0x904
> +#define PCIE_ATU_TYPE_MEM (0x0 << 0)
> +#define PCIE_ATU_TYPE_IO (0x2 << 0)
> +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
> +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
> +#define PCIE_ATU_CR2 0x908
> +#define PCIE_ATU_ENABLE (0x1 << 31)
> +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
> +#define PCIE_ATU_LOWER_BASE 0x90C
> +#define PCIE_ATU_UPPER_BASE 0x910
> +#define PCIE_ATU_LIMIT 0x914
> +#define PCIE_ATU_LOWER_TARGET 0x918
> +#define PCIE_ATU_BUS(x) (((x) >> 24) & 0xff)
> +#define PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff)
> +#define PCIE_ATU_UPPER_TARGET 0x91C
> +
> +static DesignwarePCIEHost *
> +designware_pcie_root_to_host(DesignwarePCIERoot *root)
> +{
> + BusState *bus = qdev_get_parent_bus(DEVICE(root));
> + return DESIGNWARE_PCIE_HOST(bus->parent);
> +}
> +
> +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.irqs[0], 1);
> + }
> +}
> +
> +static const MemoryRegionOps designware_pci_host_msi_ops = {
> + .write = designware_pcie_root_msi_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
Native endian normally means you need to do byteswaps yourself.
I don't see any here which looks suspicious.
> + .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 &
> 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 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 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 = PORT_LOGIC_SPEED_CHANGE;
> + break;
> +
> + case PCIE_MSI_ADDR_LO:
> + val = root->msi.base;
> + break;
> +
> + case PCIE_MSI_ADDR_HI:
> + val = root->msi.base >> 32;
> + break;
> +
> + case PCIE_MSI_INTR0_ENABLE:
> + val = root->msi.intr[0].enable;
> + break;
> +
> + case PCIE_MSI_INTR0_MASK:
> + val = root->msi.intr[0].mask;
> + break;
> +
> + case PCIE_MSI_INTR0_STATUS:
> + val = root->msi.intr[0].status;
> + break;
> +
> + case PCIE_PHY_DEBUG_R1:
> + val = PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
> + break;
> +
> + case PCIE_ATU_VIEWPORT:
> + val = root->atu_viewport;
> + break;
> +
> + case PCIE_ATU_LOWER_BASE:
> + val = viewport->base;
> + break;
> +
> + case PCIE_ATU_UPPER_BASE:
> + val = viewport->base >> 32;
> + break;
> +
> + case PCIE_ATU_LOWER_TARGET:
> + val = viewport->target;
> + break;
> +
> + case PCIE_ATU_UPPER_TARGET:
> + val = viewport->target >> 32;
> + break;
> +
> + case PCIE_ATU_LIMIT:
> + val = viewport->limit;
> + break;
> +
> + case PCIE_ATU_CR1:
> + case PCIE_ATU_CR2: /* FALLTHROUGH */
> + val = viewport->cr[(address - 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;
> + DesignwarePCIERoot *root;
> + PCIBus *pcibus;
> +
> + root = DESIGNWARE_PCIE_ROOT(opaque);
> + viewport = designware_pcie_root_get_current_viewport(root);
> + pcibus = pci_get_bus(PCI_DEVICE(root));
> +
> + addr &= PCIE_CONFIG_SPACE_SIZE - 1;
> + addr |= PCIE_ATU_BUS(viewport->target) << 16;
> + addr |= PCIE_ATU_DEVFN(viewport->target) << 8;
> +
> + if (val) {
> + pci_data_write(pcibus, addr, *val, len);
> + return 0;
> + }
> +
> + return pci_data_read(pcibus, addr, len);
> +}
> +
> +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_NATIVE_ENDIAN,
Native endian is usually not what's needed for device emulation.
> + .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] & PCIE_ATU_ENABLE;
> +
> + MemoryRegion *current, *other;
> +
> + if (viewport->cr[0] == 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 PCIE_PORT_LINK_CONTROL:
> + case PCIE_LINK_WIDTH_SPEED_CONTROL:
> + case PCIE_PHY_DEBUG_R1:
> + /* No-op */
> + break;
> +
> + case PCIE_MSI_ADDR_LO:
> + root->msi.base &= 0xFFFFFFFF00000000ULL;
> + root->msi.base |= val;
> + break;
> +
> + case PCIE_MSI_ADDR_HI:
> + root->msi.base &= 0x00000000FFFFFFFFULL;
> + root->msi.base |= (uint64_t)val << 32;
> + break;
> +
> + case PCIE_MSI_INTR0_ENABLE: {
> + const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val;
> +
> + root->msi.intr[0].enable = val;
> +
> + if (update_msi_mapping) {
> + designware_pcie_root_update_msi_mapping(root);
> + }
> + break;
> + }
> +
> + case PCIE_MSI_INTR0_MASK:
> + root->msi.intr[0].mask = val;
> + break;
> +
> + case PCIE_MSI_INTR0_STATUS:
> + root->msi.intr[0].status ^= val;
> + if (!root->msi.intr[0].status) {
> + qemu_set_irq(host->pci.irqs[0], 0);
> + }
> + break;
> +
> + case PCIE_ATU_VIEWPORT:
> + root->atu_viewport = val;
> + break;
> +
> + case PCIE_ATU_LOWER_BASE:
> + viewport->base &= 0xFFFFFFFF00000000ULL;
> + viewport->base |= val;
> + break;
> +
> + case PCIE_ATU_UPPER_BASE:
> + viewport->base &= 0x00000000FFFFFFFFULL;
> + viewport->base |= (uint64_t)val << 32;
> + break;
> +
> + case PCIE_ATU_LOWER_TARGET:
> + viewport->target &= 0xFFFFFFFF00000000ULL;
> + viewport->target |= val;
> + break;
> +
> + case PCIE_ATU_UPPER_TARGET:
> + viewport->target &= 0x00000000FFFFFFFFULL;
> + viewport->target |= val;
> + break;
> +
> + case PCIE_ATU_LIMIT:
> + viewport->limit = val;
> + break;
> +
> + case PCIE_ATU_CR1:
> + viewport->cr[0] = val;
> + break;
> + case 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 *address_space = &host->pci.memory;
> + PCIBridge *br = PCI_BRIDGE(dev);
> + DesignwarePCIEViewport *viewport;
> + /*
> + * Dummy values used for initial configuration of MemoryRegions
> + * that belong to a give 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] = PCIE_ATU_TYPE_MEM;
> +
> + source = &host->pci.address_space_root;
> + destination = get_system_memory();
> + 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->inbound = false;
> + viewport->base = 0x0000000000000000ULL;
> + viewport->target = 0x0000000000000000ULL;
> + viewport->limit = UINT32_MAX;
> + viewport->cr[0] = PCIE_ATU_TYPE_MEM;
> +
> + destination = &host->pci.memory;
> + direction = "Outbound";
> + source = get_system_memory();
> +
> + /*
> + * 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,
> + root, name, dummy_size);
> + memory_region_add_subregion(source, dummy_offset, mem);
> + memory_region_set_enabled(mem, false);
> + g_free(name);
This implements the MMCFG, doesn't it?
I suspect you need pcie_host_mmcfg_update instead of rolling
your own.
> + }
> +
> + /*
> + * If no inbound iATU windows are configured, HW defaults to
> + * letting inbound TLPs to pass in. We emulate that by exlicitly
> + * 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] = 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 a adress 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 = (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 = (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 = (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 = (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->is_express = true;
> + k->is_bridge = true;
> + 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;
> +
> + dc->reset = 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_NATIVE_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 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]);
> + }
> +
> + 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_PCIE_BUS);
> +
> + 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_pcie_host_set_iommu, s);
> +
> + qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus));
> + qdev_init_nofail(DEVICE(&s->root));
> +}
> +
> +static const VMStateDescription vmstate_designware_pcie_host = {
> + .name = "designware-pcie-host",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (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(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT);
> + object_property_add_child(obj, "root", OBJECT(root), NULL);
> + qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0));
> + qdev_prop_set_bit(DEVICE(root), "multifunction", false);
> +}
> +
> +static const TypeInfo designware_pcie_root_info = {
> + .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 },
> + { }
> + },
> +};
> +
> +static const TypeInfo designware_pcie_host_info = {
> + .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,
> +};
> +
> +static void designware_pcie_register(void)
> +{
> + type_register_static(&designware_pcie_root_info);
> + type_register_static(&designware_pcie_host_info);
> +}
> +type_init(designware_pcie_register)
> diff --git a/include/hw/pci-host/designware.h
> b/include/hw/pci-host/designware.h
> new file mode 100644
> index 0000000000..63db9cdfbe
> --- /dev/null
> +++ b/include/hw/pci-host/designware.h
> @@ -0,0 +1,97 @@
> +/*
> + * Copyright (c) 2017, 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 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/>.
> + */
> +
> +#ifndef DESIGNWARE_H
> +#define DESIGNWARE_H
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_bus.h"
> +#include "hw/pci/pcie_host.h"
> +#include "hw/pci/pci_bridge.h"
> +
> +#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host"
> +#define DESIGNWARE_PCIE_HOST(obj) \
> + OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST)
> +
> +#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root"
> +#define DESIGNWARE_PCIE_ROOT(obj) \
> + OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT)
> +
> +typedef struct DesignwarePCIEViewport {
> + MemoryRegion cfg;
> + MemoryRegion mem;
> +
> + uint64_t base;
> + uint64_t target;
> + uint32_t limit;
> + uint32_t cr[2];
> +
> + bool inbound;
> +} DesignwarePCIEViewport;
> +
> +typedef struct DesignwarePCIEMSIBank {
> + uint32_t enable;
> + uint32_t mask;
> + uint32_t status;
> +} DesignwarePCIEMSIBank;
> +
> +typedef struct DesignwarePCIEMSI {
> + uint64_t base;
> + MemoryRegion iomem;
> +
> +#define DESIGNWARE_PCIE_NUM_MSI_BANKS 1
> +
> + DesignwarePCIEMSIBank intr[DESIGNWARE_PCIE_NUM_MSI_BANKS];
> +} DesignwarePCIEMSI;
> +
> +typedef struct DesignwarePCIERoot {
> + PCIBridge parent_obj;
> +
> + uint32_t atu_viewport;
> +
> +#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND 0
> +#define DESIGNWARE_PCIE_VIEWPORT_INBOUND 1
> +#define DESIGNWARE_PCIE_NUM_VIEWPORTS 4
> +
> + DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS];
> + DesignwarePCIEMSI msi;
> +} DesignwarePCIERoot;
> +
> +typedef struct DesignwarePCIEHost {
> + PCIHostState parent_obj;
> +
> + DesignwarePCIERoot root;
> +
> + struct {
> + AddressSpace address_space;
> + MemoryRegion address_space_root;
> +
> + MemoryRegion memory;
> + MemoryRegion io;
> +
> + qemu_irq irqs[4];
> + } pci;
> +
> + MemoryRegion mmio;
> +} DesignwarePCIEHost;
> +
> +#endif /* DESIGNWARE_H */
> diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
> index 35df1874a9..23fefe1bc6 100644
> --- a/include/hw/pci/pci_ids.h
> +++ b/include/hw/pci/pci_ids.h
> @@ -266,4 +266,6 @@
> #define PCI_VENDOR_ID_TEWS 0x1498
> #define PCI_DEVICE_ID_TEWS_TPCI200 0x30C8
>
> +#define PCI_VENDOR_ID_SYNOPSYS 0x16C3
> +
> #endif
> --
> 2.14.3
- Re: [Qemu-devel] [PATCH v5 09/14] pci: Use pci_config_size in pci_data_* accessors, (continued)
[Qemu-devel] [PATCH v5 11/14] usb: Add basic code to emulate Chipidea USB IP, Andrey Smirnov, 2018/02/06
[Qemu-devel] [PATCH v5 13/14] hw/arm: Move virt's PSCI DT fixup code to arm/boot.c, Andrey Smirnov, 2018/02/06
[Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Andrey Smirnov, 2018/02/06
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block,
Michael S. Tsirkin <=
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Andrey Smirnov, 2018/02/08
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Michael S. Tsirkin, 2018/02/08
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Andrey Smirnov, 2018/02/08
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Michael S. Tsirkin, 2018/02/08
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Andrey Smirnov, 2018/02/08
- Re: [Qemu-devel] [PATCH v5 10/14] pci: Add support for Designware IP block, Michael S. Tsirkin, 2018/02/08
[Qemu-devel] [PATCH v5 12/14] i.MX: Add i.MX7 SOC implementation., Andrey Smirnov, 2018/02/06
Re: [Qemu-devel] [PATCH v5 00/14] Initial i.MX7 support, Peter Maydell, 2018/02/08