From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2F0B2C531C9 for ; Sat, 25 Jul 2026 01:01:37 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnQlR-0000nl-Td; Fri, 24 Jul 2026 21:01:18 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnQTE-0006vC-25 for qemu-arm@nongnu.org; Fri, 24 Jul 2026 20:42:28 -0400 Received: from mail-ua1-x92d.google.com ([2607:f8b0:4864:20::92d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnQTA-0000m0-TM for qemu-arm@nongnu.org; Fri, 24 Jul 2026 20:42:27 -0400 Received: by mail-ua1-x92d.google.com with SMTP id a1e0cc1a2514c-977242349e3so308302241.0 for ; Fri, 24 Jul 2026 17:42:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784940144; x=1785544944; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=WkUJW2JSZjuyfLR5hXvDwjD55p0MwEChoFFvHHZw8BM=; b=YQC2RG8GSaECHV4EuvZzQ5wg2tj5b7MlTWkPLE2nrk/GaIevsFe75NpFG9VAtWLLa4 crHvNUvTTUL9MQoBKgy5DFMolU9h5J7WPcJ/sQ/yATGwQXSFK++1tAlPCFPpHs2hKdzi GXY2dmyRPL0XuYBzlOz+/B6l2g0pwzUSKhrs4bZy/uRbFdfAN3gMIE2g7b3ko5xU/+gP u1srE8bHYbTkq2xsFXE40dBHVXkpZhIJyENoDrTT4GAWVkvltfIc4dB2LlyZGsjN6tsg 3rdfKzwXyaWz4WDpob2M21a/sP/4QfCaoMANKkIDFyV6bE4QP/fbIP1hDIROcGGgUutj w4iQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784940144; x=1785544944; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=WkUJW2JSZjuyfLR5hXvDwjD55p0MwEChoFFvHHZw8BM=; b=Q/r4jlAX9wN8AggXZq2AghbuRrIs7QvQEuSJ1L62mXlEnoH0e8oay2yqN3FOCkLs4x L8yNcBvQ9BVpy/VRe54uDwh4IMsPhcQkSPo21UyU8HaDjJOiO+luhUyFbp25HLXQ69z+ Xy6HTB4Tzypb47l5AcBXrHtX3YFMt7Sn3z+Au1N1wVSQc9DlN6WSdq14oxCFNYFFhStb oZ72XkP0r+xz8h+Cqd80uuAnRiJidnqtwMW1ijKXh9ftyUw74tGxORcZgfhV2Xqr5ShZ Ya1bq0cztk1KNOOwNYFI01i5bq/0jLF/TdutLiOsqFImzzSc0e5k57SMTa45mUUVjvwn 9u1Q== X-Gm-Message-State: AOJu0Yx3CBEhIBze//tEZU+cn8rJduBEovEReWhxr2zWz6QowszadDDs LCZ7I2VA/6vQuSrkfGMxawjA9IVYfSL0h8XhoxFNl3X4Y9zprnvQV+ki X-Gm-Gg: AR+sD12paa/yA7KwPrv4k9R0jJaa+NFkUxgTVL3e8DFiepDL8F3pJDoSDj1vWk8XR9X 4Dyq064GmSoMDk4UNHACQhidK7mSU5vOPecdJ6CCwUYw8VC7lawEOYisTpHhJGVHS6dbCXKtA3f pe+QJsfVpab6Obw91ClDnZ6EpfTIY0YldV7prC9bPgQF/+CdRy4KwhFunRQNwokaZnOkuRBYNiT 9aJO77xv+88dT5KzUMbMy9Lv9HyDSf9DVnkbDDVbNaXDkx5KH7fo1RoOvcQnuOa/1FxxZRKGhI5 ZoGdmaRMEXAJ9AFjnfbSEL51QMenzTiLDUEbbAv1Ljf4nsEUCCoUcQsJnMjMJrFG8jyTid1NRGP tfhxKNZM/IoSS/MyskdZ+29DjbEgglpFoSJU33F2N3Ym2/exfmtC+072qJsU6+ZcdUvOlN4r1vZ S+Y4t5cO3dqdaubuuYVOUi6iGvBP0w1kJQ X-Received: by 2002:a05:6102:50aa:b0:739:4470:d8 with SMTP id ada2fe7eead31-75040dd067cmr694193137.26.1784940143695; Fri, 24 Jul 2026 17:42:23 -0700 (PDT) Received: from localhost.localdomain ([146.71.8.128]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-97774f12690sm930591241.12.2026.07.24.17.42.22 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Fri, 24 Jul 2026 17:42:23 -0700 (PDT) From: Marcelo Manzo To: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org, Peter Maydell , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Jason Wang , Sergey Kambalin , Sergey Kambalin , Marcelo Manzo Subject: [PATCH 02/19] hw/arm/bcm2838_pcie: add BCM2838 PCIe host Date: Fri, 24 Jul 2026 20:42:01 -0400 Message-ID: <20260725004219.66222-3-marcelomanzo@gmail.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20260725004219.66222-1-marcelomanzo@gmail.com> References: <20260725004219.66222-1-marcelomanzo@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=2607:f8b0:4864:20::92d; envelope-from=marcelomanzo@gmail.com; helo=mail-ua1-x92d.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 24 Jul 2026 21:00:27 -0400 X-BeenThere: qemu-arm@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-arm-bounces+qemu-arm=archiver.kernel.org@nongnu.org Sender: qemu-arm-bounces+qemu-arm=archiver.kernel.org@nongnu.org From: Sergey Kambalin Add the host-facing part of the BCM2838 PCIe Root Complex: config space read/write dispatch over the extended MMIO window, IRQ routing, and the PCI root bus registration that plugs the root port device (added in the previous patch) into it. Part of Sergey Kambalin's original Raspberry Pi 4B PCIe/GENET networking series (patchwork series 829638, posted to qemu-devel in 2024, never merged). Ported to current master by Marcelo Manzo: updated the class_init() signature and header include paths for API changes since the series was originally posted, with no functional change to the logic itself. Signed-off-by: Sergey Kambalin Signed-off-by: Marcelo Manzo --- hw/arm/bcm2838_pcie.c | 215 ++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 4 + include/hw/arm/bcm2838_pcie.h | 22 ++++ 3 files changed, 241 insertions(+) diff --git a/hw/arm/bcm2838_pcie.c b/hw/arm/bcm2838_pcie.c index 96a45c18..1596145b 100644 --- a/hw/arm/bcm2838_pcie.c +++ b/hw/arm/bcm2838_pcie.c @@ -17,6 +17,217 @@ #include "hw/arm/bcm2838_pcie.h" #include "trace.h" +static uint32_t bcm2838_pcie_config_read(PCIDevice *d, + uint32_t address, int len) +{ + return pci_default_read_config(d, address, len); +} + +static void bcm2838_pcie_config_write(PCIDevice *d, uint32_t addr, uint32_t val, + int len) +{ + return pci_default_write_config(d, addr, val, len); +} + +static uint64_t bcm2838_pcie_host_read(void *opaque, hwaddr offset, + unsigned size) { + hwaddr mmcfg_addr; + uint64_t value = ~0; + BCM2838PcieHostState *s = opaque; + PCIExpressHost *pcie_hb = PCIE_HOST_BRIDGE(s); + uint8_t *root_regs = s->root_port.regs; + uint32_t *cfg_idx = (uint32_t *)(root_regs + BCM2838_PCIE_EXT_CFG_INDEX + - PCIE_CONFIG_SPACE_SIZE); + + if (offset - PCIE_CONFIG_SPACE_SIZE + size <= sizeof(s->root_port.regs)) { + switch (offset) { + case BCM2838_PCIE_EXT_CFG_DATA + ... BCM2838_PCIE_EXT_CFG_DATA + PCIE_CONFIG_SPACE_SIZE - 1: + mmcfg_addr = *cfg_idx + | PCIE_MMCFG_CONFOFFSET(offset - BCM2838_PCIE_EXT_CFG_DATA); + value = pcie_hb->mmio.ops->read(opaque, mmcfg_addr, size); + break; + default: + memcpy(&value, root_regs + offset - PCIE_CONFIG_SPACE_SIZE, size); + } + } else { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n", + __func__, size, offset); + } + + trace_bcm2838_pcie_host_read(size, offset, value); + return value; +} + +static void bcm2838_pcie_host_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) { + hwaddr mmcfg_addr; + BCM2838PcieHostState *s = opaque; + PCIExpressHost *pcie_hb = PCIE_HOST_BRIDGE(s); + uint8_t *root_regs = s->root_port.regs; + uint32_t *cfg_idx = (uint32_t *)(root_regs + BCM2838_PCIE_EXT_CFG_INDEX + - PCIE_CONFIG_SPACE_SIZE); + + trace_bcm2838_pcie_host_write(size, offset, value); + + if (offset - PCIE_CONFIG_SPACE_SIZE + size <= sizeof(s->root_port.regs)) { + switch (offset) { + case BCM2838_PCIE_EXT_CFG_DATA + ... BCM2838_PCIE_EXT_CFG_DATA + PCIE_CONFIG_SPACE_SIZE - 1: + mmcfg_addr = *cfg_idx + | PCIE_MMCFG_CONFOFFSET(offset - BCM2838_PCIE_EXT_CFG_DATA); + pcie_hb->mmio.ops->write(opaque, mmcfg_addr, value, size); + break; + default: + memcpy(root_regs + offset - PCIE_CONFIG_SPACE_SIZE, &value, size); + } + } else { + qemu_log_mask( + LOG_GUEST_ERROR, + "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n", + __func__, size, offset); + } +} + +static const MemoryRegionOps bcm2838_pcie_host_ops = { + .read = bcm2838_pcie_host_read, + .write = bcm2838_pcie_host_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = {.max_access_size = sizeof(uint64_t)}, +}; + +int bcm2838_pcie_host_set_irq_num(BCM2838PcieHostState *s, int index, int spi) +{ + if (index >= BCM2838_PCIE_NUM_IRQS) { + return -EINVAL; + } + + s->irq_num[index] = spi; + return 0; +} + +static void bcm2838_pcie_host_set_irq(void *opaque, int irq_num, int level) +{ + BCM2838PcieHostState *s = opaque; + + qemu_set_irq(s->irq[irq_num], level); +} + +static PCIINTxRoute bcm2838_pcie_host_route_intx_pin_to_irq(void *opaque, + int pin) +{ + PCIINTxRoute route; + BCM2838PcieHostState *s = opaque; + + route.irq = s->irq_num[pin]; + route.mode = route.irq < 0 ? PCI_INTX_DISABLED : PCI_INTX_ENABLED; + + return route; +} + +static int bcm2838_pcie_host_map_irq(PCIDevice *pci_dev, int pin) +{ + return pin; +} + +static void bcm2838_pcie_host_realize(DeviceState *dev, Error **errp) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + BCM2838PcieHostState *s = BCM2838_PCIE_HOST(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + int i; + + memory_region_init_io(&s->cfg_regs, OBJECT(s), &bcm2838_pcie_host_ops, s, + "bcm2838_pcie_cfg_regs", BCM2838_PCIE_REGS_SIZE); + sysbus_init_mmio(sbd, &s->cfg_regs); + + /* + * The MemoryRegions io_mmio and io_ioport that we pass to + * pci_register_root_bus() are not the same as the MemoryRegions + * io_mmio_window and io_ioport_window that we expose as SysBus MRs. + * The difference is in the behavior of accesses to addresses where no PCI + * device has been mapped. + * + * io_mmio and io_ioport are the underlying PCI view of the PCI address + * space, and when a PCI device does a bus master access to a bad address + * this is reported back to it as a transaction failure. + * + * io_mmio_window and io_ioport_window implement "unmapped addresses read as + * -1 and ignore writes"; this is a traditional x86 PC behavior, which is + * not mandated properly by the PCI spec but expected by the majority of + * PCI-using guest software, including Linux. + * + * We implement it in the PCIe host controller, by providing the *_window + * MRs, which are containers with io ops that implement the 'background' + * behavior and which hold the real PCI MRs as sub-regions. + */ + memory_region_init(&s->io_mmio, OBJECT(s), "bcm2838_pcie_mmio", UINT64_MAX); + memory_region_init(&s->io_ioport, OBJECT(s), "bcm2838_pcie_ioport", + 64 * 1024); + + memory_region_init_io(&s->io_mmio_window, OBJECT(s), + &unassigned_io_ops, OBJECT(s), + "bcm2838_pcie_mmio_window", UINT64_MAX); + memory_region_init_io(&s->io_ioport_window, OBJECT(s), + &unassigned_io_ops, OBJECT(s), + "bcm2838_pcie_ioport_window", 64 * 1024); + + memory_region_add_subregion(&s->io_mmio_window, 0, &s->io_mmio); + memory_region_add_subregion(&s->io_ioport_window, 0, &s->io_ioport); + sysbus_init_mmio(sbd, &s->io_mmio_window); + sysbus_init_mmio(sbd, &s->io_ioport_window); + + for (i = 0; i < BCM2838_PCIE_NUM_IRQS; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + s->irq_num[i] = -1; + } + + pci->bus = pci_register_root_bus(dev, "pcie.0", bcm2838_pcie_host_set_irq, + bcm2838_pcie_host_map_irq, s, &s->io_mmio, + &s->io_ioport, 0, BCM2838_PCIE_NUM_IRQS, + TYPE_PCIE_BUS); + pci_bus_set_route_irq_fn(pci->bus, bcm2838_pcie_host_route_intx_pin_to_irq); + qdev_realize(DEVICE(&s->root_port), BUS(pci->bus), &error_fatal); +} + +static const char *bcm2838_pcie_host_root_bus_path(PCIHostState *host_bridge, + PCIBus *rootbus) +{ + return "0000:00"; +} + +static void bcm2838_pcie_host_class_init(ObjectClass *class, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); + + hc->root_bus_path = bcm2838_pcie_host_root_bus_path; + dc->realize = bcm2838_pcie_host_realize; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->fw_name = "pci"; +} + +static void bcm2838_pcie_host_initfn(Object *obj) +{ + BCM2838PcieHostState *s = BCM2838_PCIE_HOST(obj); + BCM2838PcieRootState *root = &s->root_port; + + object_initialize_child(obj, "root_port", root, TYPE_BCM2838_PCIE_ROOT); + qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_bit(DEVICE(root), "multifunction", false); +} + +static const TypeInfo bcm2838_pcie_host_info = { + .name = TYPE_BCM2838_PCIE_HOST, + .parent = TYPE_PCIE_HOST_BRIDGE, + .instance_size = sizeof(BCM2838PcieHostState), + .instance_init = bcm2838_pcie_host_initfn, + .class_init = bcm2838_pcie_host_class_init, +}; + /* * RC root part (D0:F0) */ @@ -62,6 +273,9 @@ static void bcm2838_pcie_root_class_init(ObjectClass *class, const void *data) k->device_id = BCM2838_PCIE_DEVICE_ID; k->revision = BCM2838_PCIE_REVISION; + k->config_read = bcm2838_pcie_config_read; + k->config_write = bcm2838_pcie_config_write; + rpc->exp_offset = BCM2838_PCIE_EXP_CAP_OFFSET; rpc->aer_offset = BCM2838_PCIE_AER_CAP_OFFSET; } @@ -77,6 +291,7 @@ static const TypeInfo bcm2838_pcie_root_info = { static void bcm2838_pcie_register(void) { type_register_static(&bcm2838_pcie_root_info); + type_register_static(&bcm2838_pcie_host_info); } type_init(bcm2838_pcie_register) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 1b16f710..579d4b12 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -96,3 +96,7 @@ z2_aer915_event(int8_t event, int8_t len) "i2c event =0x%x len=%d bytes" # bcm2838.c bcm2838_gic_set_irq(int irq, int level) "gic irq:%d lvl:%d" + +# bcm2838_pcie.c +bcm2838_pcie_host_read(unsigned int size, uint64_t offset, uint64_t value) "%u bytes @ 0x%04"PRIx64": 0x%016"PRIx64 +bcm2838_pcie_host_write(unsigned int size, uint64_t offset, uint64_t value) "%u bytes @ 0x%04"PRIx64": 0x%016"PRIx64 diff --git a/include/hw/arm/bcm2838_pcie.h b/include/hw/arm/bcm2838_pcie.h index fe7a6cff..18ab04f2 100644 --- a/include/hw/arm/bcm2838_pcie.h +++ b/include/hw/arm/bcm2838_pcie.h @@ -16,6 +16,9 @@ #include "hw/pci/pcie_port.h" #include "qom/object.h" +#define TYPE_BCM2838_PCIE_HOST "bcm2838-pcie-host" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2838PcieHostState, BCM2838_PCIE_HOST) + #define TYPE_BCM2838_PCIE_ROOT "bcm2838-pcie-root" OBJECT_DECLARE_TYPE(BCM2838PcieRootState, BCM2838PcieRootClass, BCM2838_PCIE_ROOT) @@ -50,4 +53,23 @@ struct BCM2838PcieRootClass { }; +struct BCM2838PcieHostState { + /*< private >*/ + PCIExpressHost parent_obj; + + /*< public >*/ + BCM2838PcieRootState root_port; + + MemoryRegion cfg_regs; + MemoryRegion io_ioport; + MemoryRegion io_mmio; + MemoryRegion io_ioport_window; + MemoryRegion io_mmio_window; + + qemu_irq irq[BCM2838_PCIE_NUM_IRQS]; + int irq_num[BCM2838_PCIE_NUM_IRQS]; +}; + +int bcm2838_pcie_host_set_irq_num(BCM2838PcieHostState *s, int index, int spi); + #endif /* BCM2838_PCIE_H */ -- 2.47.1