* [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700
@ 2025-09-11 7:24 Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 01/14] hw/pci/pci_ids Add PCI vendor ID for ASPEED Jamin Lin via
` (13 more replies)
0 siblings, 14 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
v1:
1. Add PCIe PHY, CFG, and MMIO window support for AST2600.
Note: Only supports RC_H.
2. Add PCIe PHY, CFG, and MMIO window support for AST2700.
Note: Supports 3 RCs.
v2:
1. Introduce a new root port device.
2. For AST2600 RC_H, add the root device at 80:00.0 and a root port at 80.08.0
to match the real hardware topology, allowing users to attach PCIe devices
at the root port.
3. For AST2700, add a root port at 00.00.0 for each PCIe root complex to match
the real hardware topology, allowing users to attach PCIe devices at the
root port.
Dependencies
QEMU version:
https://github.com/qemu/qemu/commit/6a9fa5ef3230a7d51e0d953a59ee9ef10af705b8
his patch series depends on the following patch series:
1. https://patchwork.kernel.org/project/qemu-devel/patch/20250902062550.3797040-1-jamin_lin@aspeedtech.com/
2. https://patchwork.kernel.org/project/qemu-devel/cover/20250904100556.1729604-1-kane_chen@aspeedtech.com/
Testing the PCIe Root Complex model with the e1000e PCIe device
AST2600 test environment
Test image: ASPEED SDK v09.07
Download
https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz
When booted, lspci should show one root device at 80:00.0 and a root
port at 80:08.0, matching the expected hardware topology:
```
root@ast2600-default:~# lspci
80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600
80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
```
The e1000e driver is included in this image. Attach the e1000e device on
bus pcie.0 with the following command line:
```
-device e1000e,netdev=net0,bus=pcie.0 \
-netdev user,id=net0,hostfwd=:127.0.0.1:3222-:22,hostname=qemu0 \
```
After boot, lspci should show the e1000e device enumerated at 81:00.0:
```
root@ast2600-default:~# lspci
80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600
80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
81:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
```
The e1000e driver should probe successfully, and a new Ethernet interface
should appear:
```
root@ast2600-default:~# ifconfig
eth4 Link encap:Ethernet HWaddr 52:54:00:12:34:5A
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::5054:ff:fe12:345a/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:25 errors:0 dropped:0 overruns:0 frame:0
TX packets:57 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5524 (5.3 KiB) TX bytes:8488 (8.2 KiB)
Interrupt:81 Memory:70040000-70060000
```
AST2700 test environment
Test image: ASPEED SDK v09.07
Download
https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2700-default-obmc.tar.gz
By default, the ASPEED SDK only enables PCIe root complex 2 (RC2). If you
want to test all three PCIe root complexes, please use the customized
image provided here:
https://github.com/jamin-aspeed/openbmc/releases/download/qemu-test-0907/ast2700-default-pcie-qemu.tar.xz
When booted, lspci should show one root port at 00:00.0 on the
pcie.2 bus. The domain for PCIe RC2 is 0002, which matches the
expected hardware topology:
```
root@ast2700-default:~# lspci
0002:00:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
```
The e1000e driver is included in this image. Attach the e1000e device to
bus pcie.2 using the following command line:
```
-device e1000e,netdev=net0,bus=pcie.2 \
-netdev user,id=net0,hostfwd=:127.0.0.1:3222-:22,hostname=qemu0 \
```
After boot, lspci should show the e1000e device enumerated at
0002:01:00.0:
```
root@ast2700-default:~# lspci
0002:00:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
0002:01:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
```
The e1000e driver should probe successfully, and a new Ethernet interface
should appear:
```
root@ast2700-default:~# ifconfig
eth2 Link encap:Ethernet HWaddr 52:54:00:12:34:56
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:21 errors:0 dropped:0 overruns:0 frame:0
TX packets:48 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5388 (5.2 KiB) TX bytes:8244 (8.0 KiB)
Interrupt:42 Memory:a0040000-a0060000
```
Jamin Lin (14):
hw/pci/pci_ids Add PCI vendor ID for ASPEED
hw/pci-host/aspeed: Add AST2600 PCIe PHY model
hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge
hw/pci-host/aspeed: Add AST2600 PCIe Root Device support
hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address
configurable
hw/pci-host/aspeed: Add MSI support and per-RC IOMMU address space
hw/arm/aspeed: Wire up PCIe devices in SoC model
hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only)
hw/pci-host/aspeed: Add AST2700 PCIe PHY
hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks
hw/pci-host/aspeed: Disable Root Device and place Root Port at 00:00.0
to AST2700
hw/arm/aspeed_ast27x0: Introduce 3 PCIe RCs for AST2700
tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
tests/functional/aarch64/aspeed_ast2700: add PCIe enumeration test
include/hw/arm/aspeed_soc.h | 14 +
include/hw/pci-host/aspeed_pcie.h | 138 +++
include/hw/pci/pci_ids.h | 2 +
hw/arm/aspeed_ast2600.c | 69 +-
hw/arm/aspeed_ast27x0.c | 61 +
hw/pci-host/aspeed_pcie.c | 1014 +++++++++++++++++
hw/arm/Kconfig | 3 +
hw/pci-host/Kconfig | 4 +
hw/pci-host/meson.build | 1 +
hw/pci-host/trace-events | 11 +
.../functional/aarch64/test_aspeed_ast2700.py | 7 +
.../aarch64/test_aspeed_ast2700fc.py | 6 +
tests/functional/arm/test_aspeed_ast2600.py | 10 +
13 files changed, 1337 insertions(+), 3 deletions(-)
create mode 100644 include/hw/pci-host/aspeed_pcie.h
create mode 100644 hw/pci-host/aspeed_pcie.c
--
2.43.0
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v2 01/14] hw/pci/pci_ids Add PCI vendor ID for ASPEED
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model Jamin Lin via
` (12 subsequent siblings)
13 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr,
Cédric Le Goater
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
---
include/hw/pci/pci_ids.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 33e2898be9..16034aaa2c 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -291,4 +291,6 @@
#define PCI_VENDOR_ID_NVIDIA 0x10de
+#define PCI_VENDOR_ID_ASPEED 0x1A03
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 01/14] hw/pci/pci_ids Add PCI vendor ID for ASPEED Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 9:20 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge Jamin Lin via
` (11 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
This patch introduces an initial ASPEED PCIe PHY/host controller model to
support the AST2600 SoC. It provides a simple register block with MMIO
read/write callbacks, integration into the build system, and trace events
for debugging.
Key changes:
1. PCIe PHY MMIO read/write callbacks
Implemented aspeed_pcie_phy_read() and aspeed_pcie_phy_write() to
handle 32-bit register accesses.
2. Build system and Kconfig integration
Added CONFIG_PCI_EXPRESS_ASPEED in hw/pci-host/Kconfig and meson
rules.
Updated ASPEED_SOC in hw/arm/Kconfig to imply PCI_DEVICES and select
PCI_EXPRESS_ASPEED.
3. Trace events for debug
New tracepoints aspeed_pcie_phy_read and aspeed_pcie_phy_write allow
monitoring MMIO accesses.
4. Register space and defaults (AST2600 reference)
Expose a 0x100 register space, as documented in the AST2600 datasheet.
On reset, set default values:
PEHR_ID: Vendor ID = ASPEED, Device ID = 0x1150
PEHR_CLASS_CODE = 0x06040006
PEHR_DATALINK = 0xD7040022
PEHR_LINK: bit[5] set to 1 to indicate link up.
This provides a skeleton device for the AST2600 platform. It enables
firmware to detect the PCIe link as up by default and allows future
extension.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 44 +++++++++
hw/pci-host/aspeed_pcie.c | 159 ++++++++++++++++++++++++++++++
hw/arm/Kconfig | 3 +
hw/pci-host/Kconfig | 4 +
hw/pci-host/meson.build | 1 +
hw/pci-host/trace-events | 4 +
6 files changed, 215 insertions(+)
create mode 100644 include/hw/pci-host/aspeed_pcie.h
create mode 100644 hw/pci-host/aspeed_pcie.c
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
new file mode 100644
index 0000000000..faf87073ab
--- /dev/null
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -0,0 +1,44 @@
+/*
+ * ASPEED PCIe Host Controller
+ *
+ * Copyright (C) 2025 ASPEED Technology Inc.
+ * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
+ *
+ * Jamin Lin <jamin_lin@aspeedtech.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This file is based on Cédric Le Goater's patch:
+ * "pci: Add Aspeed host bridge (WIP)"
+ * https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc189854f9d26df
+ *
+ * Modifications have been made to support the ASPEED AST2600 and AST2700
+ * platforms.
+ */
+
+#ifndef ASPEED_PCIE_H
+#define ASPEED_PCIE_H
+
+#include "hw/sysbus.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pcie_host.h"
+#include "qom/object.h"
+
+#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
+OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY);
+
+struct AspeedPCIEPhyState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+ uint32_t *regs;
+ uint32_t id;
+};
+
+struct AspeedPCIEPhyClass {
+ SysBusDeviceClass parent_class;
+
+ uint64_t nr_regs;
+};
+
+#endif /* ASPEED_PCIE_H */
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
new file mode 100644
index 0000000000..5584449b17
--- /dev/null
+++ b/hw/pci-host/aspeed_pcie.c
@@ -0,0 +1,159 @@
+/*
+ * ASPEED PCIe Host Controller
+ *
+ * Copyright (C) 2025 ASPEED Technology Inc.
+ * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
+ *
+ * Jamin Lin <jamin_lin@aspeedtech.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This file is based on Cédric Le Goater's patch:
+ * "pci: Add Aspeed host bridge (WIP)"
+ * https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc189854f9d26df
+ *
+ * Modifications have been made to support the ASPEED AST2600 and AST2700
+ * platforms.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/registerfields.h"
+#include "hw/irq.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci-host/aspeed_pcie.h"
+#include "hw/pci/msi.h"
+#include "trace.h"
+
+/*
+ * PCIe PHY
+ *
+ * PCIe Host Controller (PCIEH)
+ */
+
+/* AST2600 */
+REG32(PEHR_ID, 0x00)
+ FIELD(PEHR_ID, DEV, 16, 16)
+REG32(PEHR_CLASS_CODE, 0x04)
+REG32(PEHR_DATALINK, 0x10)
+REG32(PEHR_PROTECT, 0x7C)
+ FIELD(PEHR_PROTECT, LOCK, 0, 8)
+REG32(PEHR_LINK, 0xC0)
+ FIELD(PEHR_LINK, STS, 5, 1)
+
+#define ASPEED_PCIE_PHY_UNLOCK 0xA8
+
+static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
+ uint32_t reg = addr >> 2;
+ uint32_t value = 0;
+
+ value = s->regs[reg];
+
+ trace_aspeed_pcie_phy_read(s->id, addr, value);
+
+ return value;
+}
+
+static void aspeed_pcie_phy_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+ AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
+ uint32_t reg = addr >> 2;
+
+ trace_aspeed_pcie_phy_write(s->id, addr, data);
+
+ switch (reg) {
+ case R_PEHR_PROTECT:
+ data &= R_PEHR_PROTECT_LOCK_MASK;
+ s->regs[reg] = !!(data == ASPEED_PCIE_PHY_UNLOCK);
+ break;
+ default:
+ s->regs[reg] = data;
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_pcie_phy_ops = {
+ .read = aspeed_pcie_phy_read,
+ .write = aspeed_pcie_phy_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void aspeed_pcie_phy_reset(DeviceState *dev)
+{
+ AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
+ AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
+
+ memset(s->regs, 0, apc->nr_regs << 2);
+
+ s->regs[R_PEHR_ID] =
+ (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED;
+ s->regs[R_PEHR_CLASS_CODE] = 0x06040006;
+ s->regs[R_PEHR_DATALINK] = 0xD7040022;
+ s->regs[R_PEHR_LINK] = R_PEHR_LINK_STS_MASK;
+}
+
+static void aspeed_pcie_phy_realize(DeviceState *dev, Error **errp)
+{
+ AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
+ AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ g_autofree char *name;
+
+ s->regs = g_new(uint32_t, apc->nr_regs);
+ name = g_strdup_printf(TYPE_ASPEED_PCIE_PHY ".regs.%d", s->id);
+ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_pcie_phy_ops, s, name,
+ apc->nr_regs << 2);
+ sysbus_init_mmio(sbd, &s->mmio);
+}
+
+static void aspeed_pcie_phy_unrealize(DeviceState *dev)
+{
+ AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
+
+ g_free(s->regs);
+ s->regs = NULL;
+}
+
+static const Property aspeed_pcie_phy_props[] = {
+ DEFINE_PROP_UINT32("id", AspeedPCIEPhyState, id, 0),
+};
+
+static void aspeed_pcie_phy_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass);
+
+ dc->desc = "ASPEED PCIe Phy";
+ dc->realize = aspeed_pcie_phy_realize;
+ dc->unrealize = aspeed_pcie_phy_unrealize;
+ device_class_set_legacy_reset(dc, aspeed_pcie_phy_reset);
+ device_class_set_props(dc, aspeed_pcie_phy_props);
+
+ apc->nr_regs = 0x100 >> 2;
+}
+
+static const TypeInfo aspeed_pcie_phy_info = {
+ .name = TYPE_ASPEED_PCIE_PHY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedPCIEPhyState),
+ .class_init = aspeed_pcie_phy_class_init,
+ .class_size = sizeof(AspeedPCIEPhyClass),
+};
+
+static void aspeed_pcie_register_types(void)
+{
+ type_register_static(&aspeed_pcie_phy_info);
+}
+
+type_init(aspeed_pcie_register_types);
+
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 2aa4b5d778..83d2676f6c 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -544,6 +544,7 @@ config ASPEED_SOC
bool
default y
depends on TCG && ARM
+ imply PCI_DEVICES
select DS1338
select FTGMAC100
select I2C
@@ -564,6 +565,8 @@ config ASPEED_SOC
select MAX31785
select FSI_APB2OPB_ASPEED
select AT24C
+ select PCI_EXPRESS
+ select PCI_EXPRESS_ASPEED
config MPS2
bool
diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig
index 9824fa188d..8cbb8304a3 100644
--- a/hw/pci-host/Kconfig
+++ b/hw/pci-host/Kconfig
@@ -46,6 +46,10 @@ config PCI_I440FX
select PCI
select PAM
+config PCI_EXPRESS_ASPEED
+ bool
+ select PCI_EXPRESS
+
config PCI_EXPRESS_Q35
bool
select PCI_EXPRESS
diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build
index 937a0f72ac..86b754d0b0 100644
--- a/hw/pci-host/meson.build
+++ b/hw/pci-host/meson.build
@@ -2,6 +2,7 @@ pci_ss = ss.source_set()
pci_ss.add(when: 'CONFIG_PAM', if_true: files('pam.c'))
pci_ss.add(when: 'CONFIG_PCI_BONITO', if_true: files('bonito.c'))
pci_ss.add(when: 'CONFIG_GT64120', if_true: files('gt64120.c'))
+pci_ss.add(when: 'CONFIG_PCI_EXPRESS_ASPEED', if_true: files('aspeed_pcie.c'))
pci_ss.add(when: 'CONFIG_PCI_EXPRESS_DESIGNWARE', if_true: files('designware.c'))
pci_ss.add(when: 'CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', if_true: files('gpex.c'))
pci_ss.add(when: ['CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', 'CONFIG_ACPI'], if_true: files('gpex-acpi.c'))
diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
index 0a816b9aa1..3438516756 100644
--- a/hw/pci-host/trace-events
+++ b/hw/pci-host/trace-events
@@ -1,5 +1,9 @@
# See docs/devel/tracing.rst for syntax documentation.
+# aspeed_pcie.c
+aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
+aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
+
# bonito.c
bonito_spciconf_small_access(uint64_t addr, unsigned size) "PCI config address is smaller then 32-bit, addr: 0x%"PRIx64", size: %u"
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 01/14] hw/pci/pci_ids Add PCI vendor ID for ASPEED Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 16:51 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support Jamin Lin via
` (10 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Introduce PCIe config and host bridge model for the AST2600 platform.
This patch adds support for the H2X (AHB to PCIe Bus Bridge) controller
with a 0x100 byte register space. The register layout is shared between
two root complexes: 0x00–0x7f is common, 0x80–0xbf for RC_L, and 0xc0–0xff
for RC_H. Only RC_H is modeled in this implementation.
The RC_H bus uses bus numbers in the 0x80–0xff range instead of the
standard root bus 0x00. To allow the PCI subsystem to discover devices,
the host bridge logic remaps the root bus number back to 0x00 whenever the
configured bus number matches the "bus-nr" property.
New MMIO callbacks are added for the H2X config space:
- aspeed_pcie_cfg_read() and aspeed_pcie_cfg_write() handle register
accesses.
- aspeed_pcie_cfg_readwrite() provides configuration read/write support.
- aspeed_pcie_cfg_translate_write() handles PCIe byte-enable semantics for
write operations.
The reset handler initializes the H2X register block with default values
as defined in the AST2600 datasheet.
Additional changes:
- Implement ASPEED PCIe root complex (TYPE_ASPEED_PCIE_RC).
- Wire up interrupt propagation via aspeed_pcie_rc_set_irq().
- Add tracepoints for config read/write and INTx handling.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 58 ++++
hw/pci-host/aspeed_pcie.c | 422 ++++++++++++++++++++++++++++++
hw/pci-host/trace-events | 4 +
3 files changed, 484 insertions(+)
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
index faf87073ab..e2c5dc6f62 100644
--- a/include/hw/pci-host/aspeed_pcie.h
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -24,6 +24,64 @@
#include "hw/pci/pcie_host.h"
#include "qom/object.h"
+typedef struct AspeedPCIECfgTxDesc {
+ uint32_t desc0;
+ uint32_t desc1;
+ uint32_t desc2;
+ uint32_t desc3;
+ uint32_t wdata;
+ uint32_t rdata_reg;
+} AspeedPCIECfgTxDesc;
+
+typedef struct AspeedPCIERcRegs {
+ uint32_t int_en_reg;
+ uint32_t int_sts_reg;
+} AspeedPCIERcRegs;
+
+typedef struct AspeedPCIERegMap {
+ AspeedPCIERcRegs rc;
+} AspeedPCIERegMap;
+
+#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
+OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
+
+struct AspeedPCIERcState {
+ PCIExpressHost parent_obj;
+
+ MemoryRegion mmio_window;
+ MemoryRegion io_window;
+ MemoryRegion mmio;
+ MemoryRegion io;
+
+ uint32_t bus_nr;
+ char name[16];
+ qemu_irq irq;
+};
+
+/* Bridge between AHB bus and PCIe RC. */
+#define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
+OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass, ASPEED_PCIE_CFG);
+
+struct AspeedPCIECfgState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+ uint32_t *regs;
+ uint32_t id;
+
+ AspeedPCIERcState rc;
+};
+
+struct AspeedPCIECfgClass {
+ SysBusDeviceClass parent_class;
+
+ const AspeedPCIERegMap *reg_map;
+ const MemoryRegionOps *reg_ops;
+
+ uint64_t rc_bus_nr;
+ uint64_t nr_regs;
+};
+
#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY);
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index 5584449b17..9fb7c1ef67 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -27,6 +27,426 @@
#include "hw/pci/msi.h"
#include "trace.h"
+/*
+ * PCIe Root Complex (RC)
+ */
+
+static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level)
+{
+ AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
+ AspeedPCIECfgState *cfg =
+ container_of(rc, AspeedPCIECfgState, rc);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
+ const AspeedPCIERcRegs *rc_regs;
+ bool intx;
+
+ assert(irq < PCI_NUM_PINS);
+
+ rc_regs = &apc->reg_map->rc;
+
+ if (level) {
+ cfg->regs[rc_regs->int_sts_reg] |= BIT(irq);
+ } else {
+ cfg->regs[rc_regs->int_sts_reg] &= ~BIT(irq);
+ }
+
+ intx = !!(cfg->regs[rc_regs->int_sts_reg] & cfg->regs[rc_regs->int_en_reg]);
+ trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx);
+ qemu_set_irq(rc->irq, intx);
+}
+
+static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return irq_num % PCI_NUM_PINS;
+}
+
+static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
+{
+ PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
+ AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev);
+ AspeedPCIECfgState *cfg =
+ container_of(rc, AspeedPCIECfgState, rc);
+ PCIHostState *pci = PCI_HOST_BRIDGE(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ g_autofree char *name;
+
+ /* PCI configuration space */
+ pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
+ sysbus_init_mmio(sbd, &pex->mmio);
+
+ /* MMIO and IO region */
+ memory_region_init(&rc->mmio, OBJECT(rc), "mmio", UINT64_MAX);
+ memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000);
+
+ name = g_strdup_printf("pcie.%d.mmio_window", cfg->id);
+ memory_region_init_io(&rc->mmio_window, OBJECT(rc), &unassigned_io_ops,
+ OBJECT(rc), name, UINT64_MAX);
+ name = g_strdup_printf("pcie.%d.ioport_window", cfg->id);
+ memory_region_init_io(&rc->io_window, OBJECT(rc), &unassigned_io_ops,
+ OBJECT(rc), name, 0x10000);
+
+ memory_region_add_subregion(&rc->mmio_window, 0, &rc->mmio);
+ memory_region_add_subregion(&rc->io_window, 0, &rc->io);
+ sysbus_init_mmio(sbd, &rc->mmio_window);
+ sysbus_init_mmio(sbd, &rc->io_window);
+
+ sysbus_init_irq(sbd, &rc->irq);
+ name = g_strdup_printf("pcie.rc%d", cfg->id);
+ pci->bus = pci_register_root_bus(dev, name, aspeed_pcie_rc_set_irq,
+ aspeed_pcie_rc_map_irq, rc, &rc->mmio,
+ &rc->io, 0, 4, TYPE_PCIE_BUS);
+ pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
+}
+
+static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ AspeedPCIERcState *rc = ASPEED_PCIE_RC(host_bridge);
+ AspeedPCIECfgState *cfg =
+ container_of(rc, AspeedPCIECfgState, rc);
+
+ snprintf(rc->name, sizeof(rc->name), "%04x:%02x", cfg->id, rc->bus_nr);
+
+ return rc->name;
+}
+
+static const Property aspeed_pcie_rc_props[] = {
+ DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
+};
+
+static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
+{
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "ASPEED PCIe RC";
+ dc->realize = aspeed_pcie_rc_realize;
+ dc->fw_name = "pci";
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+
+ hc->root_bus_path = aspeed_pcie_rc_root_bus_path;
+ device_class_set_props(dc, aspeed_pcie_rc_props);
+
+ msi_nonbroken = true;
+}
+
+static const TypeInfo aspeed_pcie_rc_info = {
+ .name = TYPE_ASPEED_PCIE_RC,
+ .parent = TYPE_PCIE_HOST_BRIDGE,
+ .instance_size = sizeof(AspeedPCIERcState),
+ .class_init = aspeed_pcie_rc_class_init,
+};
+
+/*
+ * PCIe Config
+ *
+ * AHB to PCIe Bus Bridge (H2X)
+ *
+ * On the AST2600:
+ * NOTE: rc_l is not supported by this model.
+ * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
+ * - Registers 0x80 - 0xBF are specific to PCIe0.
+ * - Registers 0xC0 - 0xFF are specific to PCIe1.
+ */
+
+/* AST2600 */
+REG32(H2X_CTRL, 0x00)
+ FIELD(H2X_CTRL, CLEAR_RX, 4, 1)
+REG32(H2X_TX_CLEAR, 0x08)
+ FIELD(H2X_TX_CLEAR, IDLE, 0, 1)
+REG32(H2X_RDATA, 0x0C)
+REG32(H2X_TX_DESC0, 0x10)
+REG32(H2X_TX_DESC1, 0x14)
+REG32(H2X_TX_DESC2, 0x18)
+REG32(H2X_TX_DESC3, 0x1C)
+REG32(H2X_TX_DATA, 0x20)
+REG32(H2X_TX_STS, 0x24)
+ FIELD(H2X_TX_STS, IDLE, 31, 1)
+ FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1)
+ FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1)
+ FIELD(H2X_TX_STS, TRIG, 0, 1)
+REG32(H2X_RC_H_CTRL, 0xC0)
+REG32(H2X_RC_H_INT_EN, 0xC4)
+REG32(H2X_RC_H_INT_STS, 0xC8)
+ SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
+ SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
+REG32(H2X_RC_H_RDATA, 0xCC)
+
+#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
+#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
+#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
+#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
+
+#define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff)
+#define PCIE_CFG_BYTE_EN(x) ((x) & 0xf)
+
+static const AspeedPCIERegMap aspeed_regmap = {
+ .rc = {
+ .int_en_reg = R_H2X_RC_H_INT_EN,
+ .int_sts_reg = R_H2X_RC_H_INT_STS,
+ },
+};
+
+static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
+ uint32_t reg = addr >> 2;
+ uint32_t value = 0;
+
+ value = s->regs[reg];
+
+ trace_aspeed_pcie_cfg_read(s->id, addr, value);
+
+ return value;
+}
+
+static void aspeed_pcie_cfg_translate_write(uint8_t byte_en, uint32_t *addr,
+ uint64_t *val, int *len)
+{
+ uint64_t packed_val = 0;
+ int first_bit = -1;
+ int index = 0;
+ int i;
+
+ *len = ctpop8(byte_en);
+
+ if (*len == 0 || *len > 4) {
+ goto err;
+ }
+
+ /* Special case: full 4-byte write must be 4-byte aligned */
+ if (byte_en == 0x0f) {
+ if (*addr % 4 != 0) {
+ goto err;
+ }
+ *val = *val & 0xffffffff;
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (byte_en & (1 << i)) {
+ if (first_bit < 0) {
+ first_bit = i;
+ }
+ packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8);
+ index++;
+ }
+ }
+
+ *addr += first_bit;
+ *val = packed_val;
+
+ return;
+
+err:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable: 0x%x\n",
+ __func__, byte_en);
+}
+
+static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s,
+ const AspeedPCIECfgTxDesc *desc)
+{
+ AspeedPCIERcState *rc = &s->rc;
+ PCIHostState *pci;
+ uint32_t cfg_addr;
+ PCIDevice *pdev;
+ uint32_t offset;
+ uint8_t byte_en;
+ bool is_write;
+ uint8_t devfn;
+ uint64_t val;
+ uint8_t bus;
+ int len;
+
+ val = ~0;
+ is_write = !!(desc->desc0 & BIT(30));
+ cfg_addr = desc->desc2;
+
+ bus = (cfg_addr >> 24) & 0xff;
+ devfn = (cfg_addr >> 16) & 0xff;
+ offset = cfg_addr & 0xffc;
+
+ pci = PCI_HOST_BRIDGE(rc);
+
+ /*
+ * On the AST2600, the RC_H bus number ranges from 0x80 to 0xFF, and its
+ * root port uses bus number 0x80 instead of the standard 0x00. To locate
+ * the device at root port 0, remap bus number 0x80 to 0x00 so that the
+ * PCI subsystem can correctly discover the devices.
+ */
+ if (bus == rc->bus_nr) {
+ bus = 0;
+ }
+
+ pdev = pci_find_device(pci->bus, bus, devfn);
+ if (!pdev) {
+ s->regs[desc->rdata_reg] = ~0;
+ goto out;
+ }
+
+ switch (PCIE_CFG_FMTTYPE_MASK(desc->desc0)) {
+ case TLP_FMTTYPE_CFGWR0:
+ case TLP_FMTTYPE_CFGWR1:
+ byte_en = PCIE_CFG_BYTE_EN(desc->desc1);
+ val = desc->wdata;
+ aspeed_pcie_cfg_translate_write(byte_en, &offset, &val, &len);
+ pci_host_config_write_common(pdev, offset, pci_config_size(pdev),
+ val, len);
+ break;
+ case TLP_FMTTYPE_CFGRD0:
+ case TLP_FMTTYPE_CFGRD1:
+ val = pci_host_config_read_common(pdev, offset,
+ pci_config_size(pdev), 4);
+ s->regs[desc->rdata_reg] = val;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type. DESC0=0x%x\n",
+ __func__, desc->desc0);
+ }
+
+out:
+ trace_aspeed_pcie_cfg_rw(s->id, is_write ? "write" : "read", bus, devfn,
+ cfg_addr, val);
+}
+
+static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
+ AspeedPCIECfgTxDesc desc;
+ uint32_t reg = addr >> 2;
+ uint32_t rc_reg;
+
+ trace_aspeed_pcie_cfg_write(s->id, addr, data);
+
+ switch (reg) {
+ case R_H2X_CTRL:
+ if (data & R_H2X_CTRL_CLEAR_RX_MASK) {
+ s->regs[R_H2X_RDATA] = ~0;
+ }
+ break;
+ case R_H2X_TX_CLEAR:
+ if (data & R_H2X_TX_CLEAR_IDLE_MASK) {
+ s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_IDLE_MASK;
+ }
+ break;
+ case R_H2X_TX_STS:
+ if (data & R_H2X_TX_STS_TRIG_MASK) {
+ desc.desc0 = s->regs[R_H2X_TX_DESC0];
+ desc.desc1 = s->regs[R_H2X_TX_DESC1];
+ desc.desc2 = s->regs[R_H2X_TX_DESC2];
+ desc.desc3 = s->regs[R_H2X_TX_DESC3];
+ desc.wdata = s->regs[R_H2X_TX_DATA];
+ desc.rdata_reg = R_H2X_RC_H_RDATA;
+ aspeed_pcie_cfg_readwrite(s, &desc);
+ rc_reg = apc->reg_map->rc.int_sts_reg;
+ s->regs[rc_reg] |= H2X_RC_INT_INTDONE_MASK;
+ s->regs[R_H2X_TX_STS] |=
+ BIT(R_H2X_TX_STS_RC_H_TX_COMP_SHIFT);
+ s->regs[R_H2X_TX_STS] |= R_H2X_TX_STS_IDLE_MASK;
+ }
+ break;
+ /* preserve INTx status */
+ case R_H2X_RC_H_INT_STS:
+ if (data & H2X_RC_INT_INTDONE_MASK) {
+ s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_RC_H_TX_COMP_MASK;
+ }
+ s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK;
+ break;
+ default:
+ s->regs[reg] = data;
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_pcie_cfg_ops = {
+ .read = aspeed_pcie_cfg_read,
+ .write = aspeed_pcie_cfg_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void aspeed_pcie_cfg_instance_init(Object *obj)
+{
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj);
+
+ object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC);
+
+ return;
+}
+
+static void aspeed_pcie_cfg_reset(DeviceState *dev)
+{
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
+
+ memset(s->regs, 0, apc->nr_regs << 2);
+}
+
+static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
+ g_autofree char *name;
+
+ s->regs = g_new(uint32_t, apc->nr_regs);
+ name = g_strdup_printf(TYPE_ASPEED_PCIE_CFG ".regs.%d", s->id);
+ memory_region_init_io(&s->mmio, OBJECT(s), apc->reg_ops, s, name,
+ apc->nr_regs << 2);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ object_property_set_int(OBJECT(&s->rc), "bus-nr",
+ apc->rc_bus_nr,
+ &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
+ return;
+ }
+}
+
+static void aspeed_pcie_cfg_unrealize(DeviceState *dev)
+{
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
+
+ g_free(s->regs);
+ s->regs = NULL;
+}
+
+static const Property aspeed_pcie_cfg_props[] = {
+ DEFINE_PROP_UINT32("id", AspeedPCIECfgState, id, 0),
+};
+
+static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
+
+ dc->desc = "ASPEED PCIe Config";
+ dc->realize = aspeed_pcie_cfg_realize;
+ dc->unrealize = aspeed_pcie_cfg_unrealize;
+ device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset);
+ device_class_set_props(dc, aspeed_pcie_cfg_props);
+
+ apc->reg_ops = &aspeed_pcie_cfg_ops;
+ apc->reg_map = &aspeed_regmap;
+ apc->nr_regs = 0x100 >> 2;
+ apc->rc_bus_nr = 0x80;
+}
+
+static const TypeInfo aspeed_pcie_cfg_info = {
+ .name = TYPE_ASPEED_PCIE_CFG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = aspeed_pcie_cfg_instance_init,
+ .instance_size = sizeof(AspeedPCIECfgState),
+ .class_init = aspeed_pcie_cfg_class_init,
+ .class_size = sizeof(AspeedPCIECfgClass),
+};
+
/*
* PCIe PHY
*
@@ -152,6 +572,8 @@ static const TypeInfo aspeed_pcie_phy_info = {
static void aspeed_pcie_register_types(void)
{
+ type_register_static(&aspeed_pcie_rc_info);
+ type_register_static(&aspeed_pcie_cfg_info);
type_register_static(&aspeed_pcie_phy_info);
}
diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
index 3438516756..2584ea56e2 100644
--- a/hw/pci-host/trace-events
+++ b/hw/pci-host/trace-events
@@ -1,6 +1,10 @@
# See docs/devel/tracing.rst for syntax documentation.
# aspeed_pcie.c
+aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d: num %d set IRQ leve %d"
+aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
+aspeed_pcie_cfg_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
+aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t bus, uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64
aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (2 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 16:53 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable Jamin Lin via
` (9 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Introduce a PCIe Root Device for AST2600 platform.
The AST2600 root complex exposes a PCIe root device at bus 80, devfn 0.
This root device is implemented as a child of the PCIe RC and modeled
as a host bridge PCI function (class_id = PCI_CLASS_BRIDGE_HOST).
Key changes:
- Add a new device type "aspeed.pcie-root-device".
- Instantiate the root device as part of AspeedPCIERcState.
- Initialize it during RC realize() and attach it to the root bus.
- Mark the root device as non-user-creatable.
- Add RC boolean property "has-rd" to control whether the Root Device is
created (platforms can enable/disable it as needed).
Note: Only AST2600 implements this PCIe root device. AST2700 does not
provide one.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 11 +++++++
hw/pci-host/aspeed_pcie.c | 54 +++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+)
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
index e2c5dc6f62..e7c231e847 100644
--- a/include/hw/pci-host/aspeed_pcie.h
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -42,6 +42,13 @@ typedef struct AspeedPCIERegMap {
AspeedPCIERcRegs rc;
} AspeedPCIERegMap;
+#define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device"
+OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState, ASPEED_PCIE_ROOT_DEVICE);
+
+struct AspeedPCIERootDeviceState {
+ PCIBridge parent_obj;
+};
+
#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
@@ -55,7 +62,10 @@ struct AspeedPCIERcState {
uint32_t bus_nr;
char name[16];
+ bool has_rd;
qemu_irq irq;
+
+ AspeedPCIERootDeviceState root_device;
};
/* Bridge between AHB bus and PCIe RC. */
@@ -80,6 +90,7 @@ struct AspeedPCIECfgClass {
uint64_t rc_bus_nr;
uint64_t nr_regs;
+ bool rc_has_rd;
};
#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index 9fb7c1ef67..fa8854fe7a 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -27,6 +27,44 @@
#include "hw/pci/msi.h"
#include "trace.h"
+/*
+ * PCIe Root Device
+ * This device exists only on AST2600.
+ */
+
+static void aspeed_pcie_root_device_class_init(ObjectClass *klass,
+ const void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ dc->desc = "ASPEED PCIe Root Device";
+ k->vendor_id = PCI_VENDOR_ID_ASPEED;
+ k->device_id = 0x2600;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+ k->subsystem_vendor_id = k->vendor_id;
+ k->subsystem_id = k->device_id;
+ k->revision = 0;
+
+ /*
+ * PCI-facing part of the host bridge,
+ * not usable without the host-facing part
+ */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo aspeed_pcie_root_device_info = {
+ .name = TYPE_ASPEED_PCIE_ROOT_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(AspeedPCIERootDeviceState),
+ .class_init = aspeed_pcie_root_device_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
/*
* PCIe Root Complex (RC)
*/
@@ -96,6 +134,16 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
aspeed_pcie_rc_map_irq, rc, &rc->mmio,
&rc->io, 0, 4, TYPE_PCIE_BUS);
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
+
+ /* setup root device */
+ if (rc->has_rd) {
+ object_initialize_child(OBJECT(rc), "root_device", &rc->root_device,
+ TYPE_ASPEED_PCIE_ROOT_DEVICE);
+ qdev_prop_set_int32(DEVICE(&rc->root_device), "addr",
+ PCI_DEVFN(0, 0));
+ qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction", false);
+ qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus), &error_fatal);
+ }
}
static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
@@ -112,6 +160,7 @@ static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
static const Property aspeed_pcie_rc_props[] = {
DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
+ DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
};
static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
@@ -404,6 +453,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
object_property_set_int(OBJECT(&s->rc), "bus-nr",
apc->rc_bus_nr,
&error_abort);
+ object_property_set_bool(OBJECT(&s->rc), "has-rd",
+ apc->rc_has_rd,
+ &error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
return;
}
@@ -436,6 +488,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
apc->reg_map = &aspeed_regmap;
apc->nr_regs = 0x100 >> 2;
apc->rc_bus_nr = 0x80;
+ apc->rc_has_rd = true;
}
static const TypeInfo aspeed_pcie_cfg_info = {
@@ -573,6 +626,7 @@ static const TypeInfo aspeed_pcie_phy_info = {
static void aspeed_pcie_register_types(void)
{
type_register_static(&aspeed_pcie_rc_info);
+ type_register_static(&aspeed_pcie_root_device_info);
type_register_static(&aspeed_pcie_cfg_info);
type_register_static(&aspeed_pcie_phy_info);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (3 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 16:54 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 06/14] hw/pci-host/aspeed: Add MSI support and per-RC IOMMU address space Jamin Lin via
` (8 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Introduce an ASPEED PCIe Root Port and wire it under the RC. The root port
is modeled as TYPE_ASPEED_PCIE_ROOT_PORT (subclass of TYPE_PCIE_ROOT_PORT).
Key changes:
- Add TYPE_ASPEED_PCIE_ROOT_PORT (PCIESlot-based) with vendor/device IDs
and AER capability offset.
- Extend AspeedPCIERcState to embed a root_port instance and a
configurable rp_addr.
- Add "rp-addr" property to the RC to place the root port at a specific
devfn on the root bus.
- Set the root port's "chassis" property to ensure a unique chassis per RC.
- Extend AspeedPCIECfgClass with rc_rp_addr defaulting to PCI_DEVFN(8,0).
Rationale:
- AST2600 places the root port at 80:08.0 (bus 0x80, dev 8, fn 0).
- AST2700 must place the root port at 00:00.0, and it supports three RCs.
Each root port must therefore be uniquely identifiable; uses the
PCIe "chassis" ID for that.
- Providing a configurable "rp-addr" lets platforms select the correct
devfn per SoC family, while the "chassis" property ensures uniqueness
across multiple RC instances on AST2700.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 11 +++++++
hw/pci-host/aspeed_pcie.c | 48 +++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
index e7c231e847..4026d5b4c5 100644
--- a/include/hw/pci-host/aspeed_pcie.h
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -22,6 +22,7 @@
#include "hw/sysbus.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pcie_host.h"
+#include "hw/pci/pcie_port.h"
#include "qom/object.h"
typedef struct AspeedPCIECfgTxDesc {
@@ -42,6 +43,13 @@ typedef struct AspeedPCIERegMap {
AspeedPCIERcRegs rc;
} AspeedPCIERegMap;
+#define TYPE_ASPEED_PCIE_ROOT_PORT "aspeed.pcie-root-port"
+OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootPortState, ASPEED_PCIE_ROOT_PORT)
+
+typedef struct AspeedPCIERootPortState {
+ PCIESlot parent_obj;
+} AspeedPCIERootPortState;
+
#define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device"
OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState, ASPEED_PCIE_ROOT_DEVICE);
@@ -60,12 +68,14 @@ struct AspeedPCIERcState {
MemoryRegion mmio;
MemoryRegion io;
+ uint32_t rp_addr;
uint32_t bus_nr;
char name[16];
bool has_rd;
qemu_irq irq;
AspeedPCIERootDeviceState root_device;
+ AspeedPCIERootPortState root_port;
};
/* Bridge between AHB bus and PCIe RC. */
@@ -88,6 +98,7 @@ struct AspeedPCIECfgClass {
const AspeedPCIERegMap *reg_map;
const MemoryRegionOps *reg_ops;
+ uint32_t rc_rp_addr;
uint64_t rc_bus_nr;
uint64_t nr_regs;
bool rc_has_rd;
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index fa8854fe7a..3f04f0d379 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -23,6 +23,7 @@
#include "hw/registerfields.h"
#include "hw/irq.h"
#include "hw/pci/pci_host.h"
+#include "hw/pci/pcie_port.h"
#include "hw/pci-host/aspeed_pcie.h"
#include "hw/pci/msi.h"
#include "trace.h"
@@ -65,6 +66,32 @@ static const TypeInfo aspeed_pcie_root_device_info = {
},
};
+/*
+ * PCIe Root Port
+ */
+
+static void aspeed_pcie_root_port_class_init(ObjectClass *klass,
+ const void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
+
+ dc->desc = "ASPEED PCIe Root Port";
+ k->vendor_id = PCI_VENDOR_ID_ASPEED;
+ k->device_id = 0x1150;
+ dc->user_creatable = true;
+
+ rpc->aer_offset = 0x100;
+}
+
+static const TypeInfo aspeed_pcie_root_port_info = {
+ .name = TYPE_ASPEED_PCIE_ROOT_PORT,
+ .parent = TYPE_PCIE_ROOT_PORT,
+ .instance_size = sizeof(AspeedPCIERootPortState),
+ .class_init = aspeed_pcie_root_port_class_init,
+};
+
/*
* PCIe Root Complex (RC)
*/
@@ -144,6 +171,11 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction", false);
qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus), &error_fatal);
}
+
+ /* setup root port */
+ qdev_prop_set_int32(DEVICE(&rc->root_port), "addr", rc->rp_addr);
+ qdev_prop_set_uint16(DEVICE(&rc->root_port), "chassis", cfg->id);
+ qdev_realize(DEVICE(&rc->root_port), BUS(pci->bus), &error_fatal);
}
static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
@@ -158,9 +190,19 @@ static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
return rc->name;
}
+static void aspeed_pcie_rc_instance_init(Object *obj)
+{
+ AspeedPCIERcState *rc = ASPEED_PCIE_RC(obj);
+ AspeedPCIERootPortState *root_port = &rc->root_port;
+
+ object_initialize_child(obj, "root_port", root_port,
+ TYPE_ASPEED_PCIE_ROOT_PORT);
+}
+
static const Property aspeed_pcie_rc_props[] = {
DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
+ DEFINE_PROP_UINT32("rp-addr", AspeedPCIERcState, rp_addr, 0),
};
static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
@@ -183,6 +225,7 @@ static const TypeInfo aspeed_pcie_rc_info = {
.name = TYPE_ASPEED_PCIE_RC,
.parent = TYPE_PCIE_HOST_BRIDGE,
.instance_size = sizeof(AspeedPCIERcState),
+ .instance_init = aspeed_pcie_rc_instance_init,
.class_init = aspeed_pcie_rc_class_init,
};
@@ -456,6 +499,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
object_property_set_bool(OBJECT(&s->rc), "has-rd",
apc->rc_has_rd,
&error_abort);
+ object_property_set_int(OBJECT(&s->rc), "rp-addr",
+ apc->rc_rp_addr,
+ &error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
return;
}
@@ -489,6 +535,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
apc->nr_regs = 0x100 >> 2;
apc->rc_bus_nr = 0x80;
apc->rc_has_rd = true;
+ apc->rc_rp_addr = PCI_DEVFN(8, 0);
}
static const TypeInfo aspeed_pcie_cfg_info = {
@@ -627,6 +674,7 @@ static void aspeed_pcie_register_types(void)
{
type_register_static(&aspeed_pcie_rc_info);
type_register_static(&aspeed_pcie_root_device_info);
+ type_register_static(&aspeed_pcie_root_port_info);
type_register_static(&aspeed_pcie_cfg_info);
type_register_static(&aspeed_pcie_phy_info);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 06/14] hw/pci-host/aspeed: Add MSI support and per-RC IOMMU address space
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (4 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 07/14] hw/arm/aspeed: Wire up PCIe devices in SoC model Jamin Lin via
` (7 subsequent siblings)
13 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Add MSI support to the ASPEED PCIe RC/Config model and introduce a per-RC
"IOMMU root" address space to correctly route MSI writes.
On AST2700 all RCs use the same MSI address, and the MSI target is PCI
system memory (not normal DRAM). If the MSI window were mapped into real
system RAM, an endpoint's write could be observed by other RCs and
spuriously trigger their interrupts. To avoid this, each RC now owns an
isolated IOMMU root AddressSpace that contains a small MSI window and a
DRAM alias region for normal DMA.
The MSI window captures writes and asserts the RC IRQ. MSI status bits
are tracked in new H2X RC_H registers (R_H2X_RC_H_MSI_EN{0,1} and
R_H2X_RC_H_MSI_STS{0,1}). Clearing all status bits drops the IRQ. The
default MSI address is set to 0x1e77005c and can be overridden via the
msi-addr property.
This keeps MSI traffic contained within each RC while preserving normal
DMA to system DRAM. It enables correct MSI/MSI-X interrupt delivery when
multiple RCs use the same MSI target address.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 10 +++
hw/pci-host/aspeed_pcie.c | 132 ++++++++++++++++++++++++++++++
hw/pci-host/trace-events | 3 +
3 files changed, 145 insertions(+)
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
index 4026d5b4c5..37ac6531ee 100644
--- a/include/hw/pci-host/aspeed_pcie.h
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -37,6 +37,8 @@ typedef struct AspeedPCIECfgTxDesc {
typedef struct AspeedPCIERcRegs {
uint32_t int_en_reg;
uint32_t int_sts_reg;
+ uint32_t msi_sts0_reg;
+ uint32_t msi_sts1_reg;
} AspeedPCIERcRegs;
typedef struct AspeedPCIERegMap {
@@ -63,11 +65,18 @@ OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
struct AspeedPCIERcState {
PCIExpressHost parent_obj;
+ MemoryRegion iommu_root;
+ AddressSpace iommu_as;
+ MemoryRegion dram_alias;
+ MemoryRegion *dram_mr;
MemoryRegion mmio_window;
+ MemoryRegion msi_window;
MemoryRegion io_window;
MemoryRegion mmio;
MemoryRegion io;
+ uint64_t dram_base;
+ uint32_t msi_addr;
uint32_t rp_addr;
uint32_t bus_nr;
char name[16];
@@ -98,6 +107,7 @@ struct AspeedPCIECfgClass {
const AspeedPCIERegMap *reg_map;
const MemoryRegionOps *reg_ops;
+ uint32_t rc_msi_addr;
uint32_t rc_rp_addr;
uint64_t rc_bus_nr;
uint64_t nr_regs;
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index 3f04f0d379..ffe48b8068 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -96,6 +96,8 @@ static const TypeInfo aspeed_pcie_root_port_info = {
* PCIe Root Complex (RC)
*/
+#define ASPEED_PCIE_CFG_RC_MAX_MSI 64
+
static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level)
{
AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
@@ -125,6 +127,61 @@ static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num)
return irq_num % PCI_NUM_PINS;
}
+static void aspeed_pcie_rc_msi_notify(AspeedPCIERcState *rc, uint64_t data)
+{
+ AspeedPCIECfgState *cfg =
+ container_of(rc, AspeedPCIECfgState, rc);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
+ const AspeedPCIERcRegs *rc_regs;
+ uint32_t reg;
+
+ /* Written data is the HW IRQ number */
+ assert(data < ASPEED_PCIE_CFG_RC_MAX_MSI);
+
+ rc_regs = &apc->reg_map->rc;
+
+ reg = (data < 32) ? rc_regs->msi_sts0_reg : rc_regs->msi_sts1_reg;
+ cfg->regs[reg] |= BIT(data % 32);
+
+ trace_aspeed_pcie_rc_msi_set_irq(cfg->id, data, 1);
+ qemu_set_irq(rc->irq, 1);
+}
+
+static void aspeed_pcie_rc_msi_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+ AspeedPCIERcState *rc = ASPEED_PCIE_RC(opaque);
+ AspeedPCIECfgState *cfg =
+ container_of(rc, AspeedPCIECfgState, rc);
+
+ trace_aspeed_pcie_rc_msi_notify(cfg->id, addr + rc->msi_addr, data);
+ aspeed_pcie_rc_msi_notify(rc, data);
+}
+
+static const MemoryRegionOps aspeed_pcie_rc_msi_ops = {
+ .write = aspeed_pcie_rc_msi_write,
+ .read = NULL,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static AddressSpace *aspeed_pcie_rc_get_as(PCIBus *bus, void *opaque, int devfn)
+{
+ AspeedPCIERcState *rc = ASPEED_PCIE_RC(opaque);
+ return &rc->iommu_as;
+}
+
+static const PCIIOMMUOps aspeed_pcie_rc_iommu_ops = {
+ .get_address_space = aspeed_pcie_rc_get_as,
+};
+
static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
{
PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
@@ -162,6 +219,42 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
&rc->io, 0, 4, TYPE_PCIE_BUS);
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
+ /*
+ * PCIe memory view setup
+ *
+ * Background:
+ * - On AST2700, all Root Complexes use the same MSI address. This MSI
+ * address is not normal system RAM - it is a PCI system memory address.
+ * If we map the MSI/MSI-X window into real system memory, a write from
+ * one EP can be seen by all RCs and wrongly trigger interrupts on them.
+ *
+ * Design:
+ * - MSI/MSI-X here is just a placeholder address so RC and EP can talk.
+ * We make a separate MMIO space (iommu_root) for the MSI window so the
+ * writes stay local to each RC.
+ *
+ * DMA:
+ * - EPs still need access to real system memory for DMA. We add a DRAM
+ * alias in the PCI space so DMA works as expected.
+ */
+ name = g_strdup_printf("pcie.%d.iommu_root", cfg->id);
+ memory_region_init(&rc->iommu_root, OBJECT(rc), name, UINT64_MAX);
+ address_space_init(&rc->iommu_as, &rc->iommu_root, name);
+ /* setup MSI */
+ memory_region_init_io(&rc->msi_window, OBJECT(rc),
+ &aspeed_pcie_rc_msi_ops, rc,
+ "msi_window", 4);
+ memory_region_add_subregion(&rc->iommu_root, rc->msi_addr,
+ &rc->msi_window);
+ /* setup DRAM for DMA */
+ assert(rc->dram_mr != NULL);
+ name = g_strdup_printf("pcie.%d.dram_alias", cfg->id);
+ memory_region_init_alias(&rc->dram_alias, OBJECT(rc), name, rc->dram_mr,
+ 0, memory_region_size(rc->dram_mr));
+ memory_region_add_subregion(&rc->iommu_root, rc->dram_base,
+ &rc->dram_alias);
+ pci_setup_iommu(pci->bus, &aspeed_pcie_rc_iommu_ops, rc);
+
/* setup root device */
if (rc->has_rd) {
object_initialize_child(OBJECT(rc), "root_device", &rc->root_device,
@@ -203,6 +296,10 @@ static const Property aspeed_pcie_rc_props[] = {
DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
DEFINE_PROP_UINT32("rp-addr", AspeedPCIERcState, rp_addr, 0),
+ DEFINE_PROP_UINT32("msi-addr", AspeedPCIERcState, msi_addr, 0),
+ DEFINE_PROP_UINT64("dram-base", AspeedPCIERcState, dram_base, 0),
+ DEFINE_PROP_LINK("dram", AspeedPCIERcState, dram_mr, TYPE_MEMORY_REGION,
+ MemoryRegion *),
};
static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
@@ -263,6 +360,10 @@ REG32(H2X_RC_H_INT_STS, 0xC8)
SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
REG32(H2X_RC_H_RDATA, 0xCC)
+REG32(H2X_RC_H_MSI_EN0, 0xE0)
+REG32(H2X_RC_H_MSI_EN1, 0xE4)
+REG32(H2X_RC_H_MSI_STS0, 0xE8)
+REG32(H2X_RC_H_MSI_STS1, 0xEC)
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
@@ -276,6 +377,8 @@ static const AspeedPCIERegMap aspeed_regmap = {
.rc = {
.int_en_reg = R_H2X_RC_H_INT_EN,
.int_sts_reg = R_H2X_RC_H_INT_STS,
+ .msi_sts0_reg = R_H2X_RC_H_MSI_STS0,
+ .msi_sts1_reg = R_H2X_RC_H_MSI_STS1,
},
};
@@ -447,6 +550,29 @@ static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data,
}
s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK;
break;
+ /*
+ * These status registers are used for notify sources ISR are executed.
+ * If one source ISR is executed, it will clear one bit.
+ * If it clear all bits, it means to initialize this register status
+ * rather than sources ISR are executed.
+ */
+ case R_H2X_RC_H_MSI_STS0:
+ case R_H2X_RC_H_MSI_STS1:
+ if (data == 0) {
+ return ;
+ }
+
+ s->regs[reg] &= ~data;
+ if (data == 0xffffffff) {
+ return;
+ }
+
+ if (!s->regs[R_H2X_RC_H_MSI_STS0] &&
+ !s->regs[R_H2X_RC_H_MSI_STS1]) {
+ trace_aspeed_pcie_rc_msi_clear_irq(s->id, 0);
+ qemu_set_irq(s->rc.irq, 0);
+ }
+ break;
default:
s->regs[reg] = data;
break;
@@ -468,6 +594,8 @@ static void aspeed_pcie_cfg_instance_init(Object *obj)
AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj);
object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC);
+ object_property_add_alias(obj, "dram", OBJECT(&s->rc), "dram");
+ object_property_add_alias(obj, "dram-base", OBJECT(&s->rc), "dram-base");
return;
}
@@ -502,6 +630,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
object_property_set_int(OBJECT(&s->rc), "rp-addr",
apc->rc_rp_addr,
&error_abort);
+ object_property_set_int(OBJECT(&s->rc), "msi-addr",
+ apc->rc_msi_addr,
+ &error_abort);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
return;
}
@@ -533,6 +664,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
apc->reg_ops = &aspeed_pcie_cfg_ops;
apc->reg_map = &aspeed_regmap;
apc->nr_regs = 0x100 >> 2;
+ apc->rc_msi_addr = 0x1e77005C;
apc->rc_bus_nr = 0x80;
apc->rc_has_rd = true;
apc->rc_rp_addr = PCI_DEVFN(8, 0);
diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
index 2584ea56e2..a6fd88c2c4 100644
--- a/hw/pci-host/trace-events
+++ b/hw/pci-host/trace-events
@@ -2,6 +2,9 @@
# aspeed_pcie.c
aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d: num %d set IRQ leve %d"
+aspeed_pcie_rc_msi_notify(uint32_t id, uint64_t addr, uint64_t data) "%d: 0x%" PRIx64 " data 0x%" PRIx64
+aspeed_pcie_rc_msi_set_irq(uint32_t id, uint64_t unm, int level) "%d: num 0x%" PRIx64 " set IRQ level %d"
+aspeed_pcie_rc_msi_clear_irq(uint32_t id, int level) "%d: clear IRQ level %d"
aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
aspeed_pcie_cfg_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t bus, uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 07/14] hw/arm/aspeed: Wire up PCIe devices in SoC model
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (5 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 06/14] hw/pci-host/aspeed: Add MSI support and per-RC IOMMU address space Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 17:14 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only) Jamin Lin via
` (6 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Add PCIe controller and PHY instances to the Aspeed SoC state and device
enum. This prepares the SoC model to host PCIe Root Complexes and their
associated PHYs.
Although the AST2600 supports only a single Root Complex, the AST2700
provides three Root Complexes. For this reason, the model defines arrays
of three PCIe config/PHY objects and enumerates three PCIe device IDs so
that both SoCs can be represented consistently.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/arm/aspeed_soc.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index 217ef0eafd..79fe353f83 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -37,6 +37,7 @@
#include "qom/object.h"
#include "hw/misc/aspeed_lpc.h"
#include "hw/misc/unimp.h"
+#include "hw/pci-host/aspeed_pcie.h"
#include "hw/misc/aspeed_peci.h"
#include "hw/fsi/aspeed_apb2opb.h"
#include "hw/char/serial-mm.h"
@@ -49,6 +50,7 @@
#define ASPEED_MACS_NUM 4
#define ASPEED_UARTS_NUM 13
#define ASPEED_JTAG_NUM 2
+#define ASPEED_PCIE_NUM 3
struct AspeedSoCState {
DeviceState parent;
@@ -87,6 +89,8 @@ struct AspeedSoCState {
AspeedSDHCIState sdhci;
AspeedSDHCIState emmc;
AspeedLPCState lpc;
+ AspeedPCIECfgState pcie[ASPEED_PCIE_NUM];
+ AspeedPCIEPhyState pcie_phy[ASPEED_PCIE_NUM];
AspeedPECIState peci;
SerialMM uart[ASPEED_UARTS_NUM];
Clock *sysclk;
@@ -254,6 +258,15 @@ enum {
ASPEED_DEV_LPC,
ASPEED_DEV_IBT,
ASPEED_DEV_I2C,
+ ASPEED_DEV_PCIE0,
+ ASPEED_DEV_PCIE1,
+ ASPEED_DEV_PCIE2,
+ ASPEED_DEV_PCIE_PHY0,
+ ASPEED_DEV_PCIE_PHY1,
+ ASPEED_DEV_PCIE_PHY2,
+ ASPEED_DEV_PCIE_MMIO0,
+ ASPEED_DEV_PCIE_MMIO1,
+ ASPEED_DEV_PCIE_MMIO2,
ASPEED_DEV_PECI,
ASPEED_DEV_ETH1,
ASPEED_DEV_ETH2,
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only)
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (6 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 07/14] hw/arm/aspeed: Wire up PCIe devices in SoC model Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 17:19 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 09/14] hw/pci-host/aspeed: Add AST2700 PCIe PHY Jamin Lin via
` (5 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Wire up the PCIe Root Complex in the AST2600 SoC model.
According to the AST2600 firmware driver, only the RC_H controller is
supported. RC_H uses PCIe PHY1 at 0x1e6ed200 and the PCIe config (H2X)
register block at 0x1e770000. The RC_H MMIO window is mapped at
0x70000000–0x80000000. RC_L is not modeled. The RC_H interrupt is
wired to IRQ 168. Only RC_H is realized and connected to the SoC
interrupt controller.
The SoC integration initializes PCIe PHY1, instantiates a single RC
instance, wires its MMIO regions, and connects its interrupt. An alias
region is added to map the RC MMIO space into the guest physical address
space.
This provides enough functionality for firmware and guest drivers to
discover and use the AST2600 RC_H Root Complex while leaving RC_L
unimplemented.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
hw/arm/aspeed_ast2600.c | 69 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
index d12707f0ab..d521a1b4f0 100644
--- a/hw/arm/aspeed_ast2600.c
+++ b/hw/arm/aspeed_ast2600.c
@@ -48,11 +48,13 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
[ASPEED_DEV_XDMA] = 0x1E6E7000,
[ASPEED_DEV_ADC] = 0x1E6E9000,
[ASPEED_DEV_DP] = 0x1E6EB000,
+ [ASPEED_DEV_PCIE_PHY1] = 0x1E6ED200,
[ASPEED_DEV_SBC] = 0x1E6F2000,
[ASPEED_DEV_EMMC_BC] = 0x1E6f5000,
[ASPEED_DEV_VIDEO] = 0x1E700000,
[ASPEED_DEV_SDHCI] = 0x1E740000,
[ASPEED_DEV_EMMC] = 0x1E750000,
+ [ASPEED_DEV_PCIE0] = 0x1E770000,
[ASPEED_DEV_GPIO] = 0x1E780000,
[ASPEED_DEV_GPIO_1_8V] = 0x1E780800,
[ASPEED_DEV_RTC] = 0x1E781000,
@@ -79,6 +81,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
[ASPEED_DEV_FSI1] = 0x1E79B000,
[ASPEED_DEV_FSI2] = 0x1E79B100,
[ASPEED_DEV_I3C] = 0x1E7A0000,
+ [ASPEED_DEV_PCIE_MMIO1] = 0x70000000,
[ASPEED_DEV_SDRAM] = 0x80000000,
};
@@ -127,6 +130,7 @@ static const int aspeed_soc_ast2600_irqmap[] = {
[ASPEED_DEV_LPC] = 35,
[ASPEED_DEV_IBT] = 143,
[ASPEED_DEV_I2C] = 110, /* 110 -> 125 */
+ [ASPEED_DEV_PCIE0] = 168,
[ASPEED_DEV_PECI] = 38,
[ASPEED_DEV_ETH1] = 2,
[ASPEED_DEV_ETH2] = 3,
@@ -191,6 +195,10 @@ static void aspeed_soc_ast2600_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname);
object_initialize_child(obj, "i2c", &s->i2c, typename);
+ object_initialize_child(obj, "pcie-cfg", &s->pcie[0], TYPE_ASPEED_PCIE_CFG);
+ object_initialize_child(obj, "pcie-phy[*]", &s->pcie_phy[0],
+ TYPE_ASPEED_PCIE_PHY);
+
object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI);
snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
@@ -292,7 +300,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
AspeedSoCState *s = ASPEED_SOC(dev);
AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
qemu_irq irq;
- g_autofree char *sram_name = NULL;
+ g_autofree char *name = NULL;
+ MemoryRegion *mmio_alias;
+ MemoryRegion *mmio_mr;
/* Default boot region (SPI memory or ROMs) */
memory_region_init(&s->spi_boot_container, OBJECT(s),
@@ -362,8 +372,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
}
/* SRAM */
- sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index);
- if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size,
+ name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index);
+ if (!memory_region_init_ram(&s->sram, OBJECT(s), name, sc->sram_size,
errp)) {
return;
}
@@ -438,6 +448,59 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0,
aspeed_soc_get_irq(s, ASPEED_DEV_PECI));
+ /*
+ * PCIe Root Complex (RC)
+ *
+ * H2X register space (single block 0x00-0xFF):
+ * 0x00-0x7F : shared by RC_L (PCIe0) and RC_H (PCIe1)
+ * 0x80-0xBF : RC_L only
+ * 0xC0-0xFF : RC_H only
+ *
+ * Model scope / limitations:
+ * - Firmware supports RC_H only; this QEMU model does not support RC_L.
+ * - RC_H uses PHY1 and the MMIO window [0x70000000, 0x80000000]
+ * (aka MMIO1).
+ *
+ * Indexing convention (this model):
+ * - Expose a single logical instance at index 0.
+ * - pcie[0] -> hardware RC_H (PCIe1)
+ * - phy[0] -> hardware PHY1
+ * - mmio.0 -> guest address range MMIO1: 0x70000000-0x80000000
+ * - RC_L / PCIe0 is not created and mapped.
+ */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[0]), errp)) {
+ return;
+ }
+ aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[0]), 0,
+ sc->memmap[ASPEED_DEV_PCIE_PHY1]);
+
+ object_property_set_int(OBJECT(&s->pcie[0]), "dram-base",
+ sc->memmap[ASPEED_DEV_SDRAM],
+ &error_abort);
+ object_property_set_link(OBJECT(&s->pcie[0]), "dram", OBJECT(s->dram_mr),
+ &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[0]), errp)) {
+ return;
+ }
+ aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[0]), 0,
+ sc->memmap[ASPEED_DEV_PCIE0]);
+
+ irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore),
+ sc->irqmap[ASPEED_DEV_PCIE0]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[0].rc), 0, irq);
+
+ name = g_strdup_printf("aspeed.pcie-mmio.0");
+
+ mmio_alias = g_new0(MemoryRegion, 1);
+ mmio_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[0].rc), 1);
+
+ memory_region_init_alias(mmio_alias, OBJECT(&s->pcie[0].rc), name,
+ mmio_mr, sc->memmap[ASPEED_DEV_PCIE_MMIO1],
+ 0x10000000);
+ memory_region_add_subregion(s->memory,
+ sc->memmap[ASPEED_DEV_PCIE_MMIO1],
+ mmio_alias);
+
/* FMC, The number of CS is set at the board level */
object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr),
&error_abort);
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 09/14] hw/pci-host/aspeed: Add AST2700 PCIe PHY
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (7 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only) Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks Jamin Lin via
` (4 subsequent siblings)
13 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Introduce a PCIe Host Controller PHY model for AST2700. This adds an
AST2700 specific PHY type (TYPE_ASPEED_2700_PCIE_PHY) with a 0x800 byte
register space and link-status bits compatible with the firmware’s
expectations.
AST2700 provides three PCIe RCs; PCIe0 and PCIe1 are GEN4, PCIe2 is
GEN2. The PHY exposes:
PEHR_2700_LINK_GEN2 at 0x344, bit 18 indicates GEN2 link up
PEHR_2700_LINK_GEN4 at 0x358, bit 8 indicates GEN4 link up
In real hardware these GEN2/GEN4 link bits are mutually exclusive.
QEMU does not model GEN2 vs GEN4 signaling differences, so the reset
handler sets both bits to 1. This keeps the model simple and lets
firmware see the link as up; firmware will read the appropriate
register per RC port to infer the intended mode.
The header gains TYPE_ASPEED_2700_PCIE_PHY; the new class derives from
TYPE_ASPEED_PCIE_PHY, sets nr_regs to 0x800 >> 2, and installs an
AST2700 reset routine that programs the class code (0x06040011) and the
GEN2/GEN4 status bits.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 1 +
hw/pci-host/aspeed_pcie.c | 39 +++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
index 37ac6531ee..c0d46e6a03 100644
--- a/include/hw/pci-host/aspeed_pcie.h
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -115,6 +115,7 @@ struct AspeedPCIECfgClass {
};
#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
+#define TYPE_ASPEED_2700_PCIE_PHY TYPE_ASPEED_PCIE_PHY "-ast2700"
OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY);
struct AspeedPCIEPhyState {
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index ffe48b8068..bc491aa7bf 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -695,6 +695,12 @@ REG32(PEHR_PROTECT, 0x7C)
REG32(PEHR_LINK, 0xC0)
FIELD(PEHR_LINK, STS, 5, 1)
+/* AST2700 */
+REG32(PEHR_2700_LINK_GEN2, 0x344)
+ FIELD(PEHR_2700_LINK_GEN2, STS, 18, 1)
+REG32(PEHR_2700_LINK_GEN4, 0x358)
+ FIELD(PEHR_2700_LINK_GEN4, STS, 8, 1)
+
#define ASPEED_PCIE_PHY_UNLOCK 0xA8
static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr,
@@ -802,6 +808,38 @@ static const TypeInfo aspeed_pcie_phy_info = {
.class_size = sizeof(AspeedPCIEPhyClass),
};
+static void aspeed_2700_pcie_phy_reset(DeviceState *dev)
+{
+ AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
+ AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
+
+ memset(s->regs, 0, apc->nr_regs << 2);
+
+ s->regs[R_PEHR_ID] =
+ (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED;
+ s->regs[R_PEHR_CLASS_CODE] = 0x06040011;
+ s->regs[R_PEHR_2700_LINK_GEN2] = R_PEHR_2700_LINK_GEN2_STS_MASK;
+ s->regs[R_PEHR_2700_LINK_GEN4] = R_PEHR_2700_LINK_GEN4_STS_MASK;
+}
+
+static void aspeed_2700_pcie_phy_class_init(ObjectClass *klass,
+ const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass);
+
+ dc->desc = "ASPEED AST2700 PCIe Phy";
+ device_class_set_legacy_reset(dc, aspeed_2700_pcie_phy_reset);
+
+ apc->nr_regs = 0x800 >> 2;
+}
+
+static const TypeInfo aspeed_2700_pcie_phy_info = {
+ .name = TYPE_ASPEED_2700_PCIE_PHY,
+ .parent = TYPE_ASPEED_PCIE_PHY,
+ .class_init = aspeed_2700_pcie_phy_class_init,
+};
+
static void aspeed_pcie_register_types(void)
{
type_register_static(&aspeed_pcie_rc_info);
@@ -809,6 +847,7 @@ static void aspeed_pcie_register_types(void)
type_register_static(&aspeed_pcie_root_port_info);
type_register_static(&aspeed_pcie_cfg_info);
type_register_static(&aspeed_pcie_phy_info);
+ type_register_static(&aspeed_2700_pcie_phy_info);
}
type_init(aspeed_pcie_register_types);
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (8 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 09/14] hw/pci-host/aspeed: Add AST2700 PCIe PHY Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 17:23 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 11/14] hw/pci-host/aspeed: Disable Root Device and place Root Port at 00:00.0 to AST2700 Jamin Lin via
` (3 subsequent siblings)
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Introduce PCIe config (H2X) support for the AST2700 SoC.
Unlike the AST2600, the AST2700 provides three independent Root Complexes,
each with its own H2X (AHB to PCIe bridge) register block of size 0x100.
All RCs use the same MSI address (0x000000F0). The H2X block includes
two different access paths:
1. CFGI (internal bridge): used to access the host bridge itself, always
with BDF=0. The AST2700 controller simplifies the design by exposing
only one register (H2X_CFGI_TLP) with fields for ADDR[15:0], BEN[19:16],
and WR[20]. This is not a full TLP descriptor as in the external case.
For QEMU readability and code reuse, the model converts H2X_CFGI_TLP
into a standard TLP TX descriptor with BDF forced to 0 and then calls
the existing helpers aspeed_pcie_cfg_readwrite() and
aspeed_pcie_cfg_translate_write().
2. CFGE (external EP access): used to access external endpoints. The
AST2700 design provides H2X_CFGE_TLP1 and a small FIFO at H2X_CFGE_TLPN.
For reads, TX DESC0 is stored in TLP1 and DESC1/DESC2 in TLPN FIFO
slots. For writes, TX DESC0 is stored in TLP1, DESC1/DESC2 in TLPN
FIFO[0..1], and TX write data in TLPN FIFO[2].
The implementation extends AspeedPCIECfgState with a small FIFO and index,
wires up new register definitions for AST2700, and adds a specific ops
table and class (TYPE_ASPEED_2700_PCIE_CFG). The reset handler clears the
FIFO state. Interrupt and MSI status registers are also supported.
This provides enough modeling for firmware and drivers to use any of the
three PCIe RCs on AST2700 with their own dedicated H2X config window,
while reusing existing TLP decode helpers in QEMU.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/pci-host/aspeed_pcie.h | 3 +
hw/pci-host/aspeed_pcie.c | 158 ++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+)
diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
index c0d46e6a03..d7b4b66f75 100644
--- a/include/hw/pci-host/aspeed_pcie.h
+++ b/include/hw/pci-host/aspeed_pcie.h
@@ -89,6 +89,7 @@ struct AspeedPCIERcState {
/* Bridge between AHB bus and PCIe RC. */
#define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
+#define TYPE_ASPEED_2700_PCIE_CFG TYPE_ASPEED_PCIE_CFG "-ast2700"
OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass, ASPEED_PCIE_CFG);
struct AspeedPCIECfgState {
@@ -99,6 +100,8 @@ struct AspeedPCIECfgState {
uint32_t id;
AspeedPCIERcState rc;
+ uint32_t tlpn_fifo[3];
+ uint32_t tlpn_idx;
};
struct AspeedPCIECfgClass {
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index bc491aa7bf..4adf17e40b 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -336,6 +336,11 @@ static const TypeInfo aspeed_pcie_rc_info = {
* - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
* - Registers 0x80 - 0xBF are specific to PCIe0.
* - Registers 0xC0 - 0xFF are specific to PCIe1.
+ *
+ * On the AST2700:
+ * - The register range 0x00 - 0xFF is assigned to a single PCIe configuration.
+ * - There are three PCIe Root Complexes (RCs), each with its own dedicated H2X
+ * register set of size 0x100 (covering offsets 0x00 to 0xFF).
*/
/* AST2600 */
@@ -365,6 +370,31 @@ REG32(H2X_RC_H_MSI_EN1, 0xE4)
REG32(H2X_RC_H_MSI_STS0, 0xE8)
REG32(H2X_RC_H_MSI_STS1, 0xEC)
+/* AST2700 */
+REG32(H2X_CFGE_INT_STS, 0x08)
+ FIELD(H2X_CFGE_INT_STS, TX_IDEL, 0, 1)
+ FIELD(H2X_CFGE_INT_STS, RX_BUSY, 1, 1)
+REG32(H2X_CFGI_TLP, 0x20)
+ FIELD(H2X_CFGI_TLP, ADDR, 0, 16)
+ FIELD(H2X_CFGI_TLP, BEN, 16, 4)
+ FIELD(H2X_CFGI_TLP, WR, 20, 1)
+REG32(H2X_CFGI_WDATA, 0x24)
+REG32(H2X_CFGI_CTRL, 0x28)
+ FIELD(H2X_CFGI_CTRL, FIRE, 0, 1)
+REG32(H2X_CFGI_RDATA, 0x2C)
+REG32(H2X_CFGE_TLP1, 0x30)
+REG32(H2X_CFGE_TLPN, 0x34)
+REG32(H2X_CFGE_CTRL, 0x38)
+ FIELD(H2X_CFGE_CTRL, FIRE, 0, 1)
+REG32(H2X_CFGE_RDATA, 0x3C)
+REG32(H2X_INT_EN, 0x40)
+REG32(H2X_INT_STS, 0x48)
+ FIELD(H2X_INT_STS, INTX, 0, 4)
+REG32(H2X_MSI_EN0, 0x50)
+REG32(H2X_MSI_EN1, 0x54)
+REG32(H2X_MSI_STS0, 0x58)
+REG32(H2X_MSI_STS1, 0x5C)
+
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
@@ -382,6 +412,15 @@ static const AspeedPCIERegMap aspeed_regmap = {
},
};
+static const AspeedPCIERegMap aspeed_2700_regmap = {
+ .rc = {
+ .int_en_reg = R_H2X_INT_EN,
+ .int_sts_reg = R_H2X_INT_STS,
+ .msi_sts0_reg = R_H2X_MSI_STS0,
+ .msi_sts1_reg = R_H2X_MSI_STS1,
+ },
+};
+
static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
unsigned int size)
{
@@ -606,6 +645,8 @@ static void aspeed_pcie_cfg_reset(DeviceState *dev)
AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
memset(s->regs, 0, apc->nr_regs << 2);
+ memset(s->tlpn_fifo, 0, sizeof(s->tlpn_fifo));
+ s->tlpn_idx = 0;
}
static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
@@ -679,6 +720,122 @@ static const TypeInfo aspeed_pcie_cfg_info = {
.class_size = sizeof(AspeedPCIECfgClass),
};
+static void aspeed_2700_pcie_cfg_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned int size)
+{
+ AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
+ AspeedPCIECfgTxDesc desc;
+ uint32_t reg = addr >> 2;
+
+ trace_aspeed_pcie_cfg_write(s->id, addr, data);
+
+ switch (reg) {
+ case R_H2X_CFGE_INT_STS:
+ if (data & R_H2X_CFGE_INT_STS_TX_IDEL_MASK) {
+ s->regs[R_H2X_CFGE_INT_STS] &= ~R_H2X_CFGE_INT_STS_TX_IDEL_MASK;
+ }
+
+ if (data & R_H2X_CFGE_INT_STS_RX_BUSY_MASK) {
+ s->regs[R_H2X_CFGE_INT_STS] &= ~R_H2X_CFGE_INT_STS_RX_BUSY_MASK;
+ }
+ break;
+ case R_H2X_CFGI_CTRL:
+ if (data & R_H2X_CFGI_CTRL_FIRE_MASK) {
+ /*
+ * Internal access to bridge
+ * Type and BDF are 0
+ */
+ desc.desc0 = 0x04000001 |
+ (ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, WR) << 30);
+ desc.desc1 = 0x00401000 |
+ ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, BEN);
+ desc.desc2 = 0x00000000 |
+ ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, ADDR);
+ desc.wdata = s->regs[R_H2X_CFGI_WDATA];
+ desc.rdata_reg = R_H2X_CFGI_RDATA;
+ aspeed_pcie_cfg_readwrite(s, &desc);
+ }
+ break;
+ case R_H2X_CFGE_TLPN:
+ s->tlpn_fifo[s->tlpn_idx] = data;
+ s->tlpn_idx = (s->tlpn_idx + 1) % ARRAY_SIZE(s->tlpn_fifo);
+ break;
+ case R_H2X_CFGE_CTRL:
+ if (data & R_H2X_CFGE_CTRL_FIRE_MASK) {
+ desc.desc0 = s->regs[R_H2X_CFGE_TLP1];
+ desc.desc1 = s->tlpn_fifo[0];
+ desc.desc2 = s->tlpn_fifo[1];
+ desc.wdata = s->tlpn_fifo[2];
+ desc.rdata_reg = R_H2X_CFGE_RDATA;
+ aspeed_pcie_cfg_readwrite(s, &desc);
+ s->regs[R_H2X_CFGE_INT_STS] |= R_H2X_CFGE_INT_STS_TX_IDEL_MASK;
+ s->regs[R_H2X_CFGE_INT_STS] |= R_H2X_CFGE_INT_STS_RX_BUSY_MASK;
+ s->tlpn_idx = 0;
+ }
+ break;
+
+ case R_H2X_INT_STS:
+ s->regs[reg] &= ~data | R_H2X_INT_STS_INTX_MASK;
+ break;
+ /*
+ * These status registers are used for notify sources ISR are executed.
+ * If one source ISR is executed, it will clear one bit.
+ * If it clear all bits, it means to initialize this register status
+ * rather than sources ISR are executed.
+ */
+ case R_H2X_MSI_STS0:
+ case R_H2X_MSI_STS1:
+ if (data == 0) {
+ return ;
+ }
+
+ s->regs[reg] &= ~data;
+ if (data == 0xffffffff) {
+ return;
+ }
+
+ if (!s->regs[R_H2X_MSI_STS0] &&
+ !s->regs[R_H2X_MSI_STS1]) {
+ trace_aspeed_pcie_rc_msi_clear_irq(s->id, 0);
+ qemu_set_irq(s->rc.irq, 0);
+ }
+ break;
+ default:
+ s->regs[reg] = data;
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_2700_pcie_cfg_ops = {
+ .read = aspeed_pcie_cfg_read,
+ .write = aspeed_2700_pcie_cfg_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void aspeed_2700_pcie_cfg_class_init(ObjectClass *klass,
+ const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
+
+ dc->desc = "ASPEED 2700 PCIe Config";
+ apc->reg_ops = &aspeed_2700_pcie_cfg_ops;
+ apc->reg_map = &aspeed_2700_regmap;
+ apc->nr_regs = 0x100 >> 2;
+ apc->rc_msi_addr = 0x000000F0;
+ apc->rc_bus_nr = 0;
+}
+
+static const TypeInfo aspeed_2700_pcie_cfg_info = {
+ .name = TYPE_ASPEED_2700_PCIE_CFG,
+ .parent = TYPE_ASPEED_PCIE_CFG,
+ .class_init = aspeed_2700_pcie_cfg_class_init,
+};
+
/*
* PCIe PHY
*
@@ -846,6 +1003,7 @@ static void aspeed_pcie_register_types(void)
type_register_static(&aspeed_pcie_root_device_info);
type_register_static(&aspeed_pcie_root_port_info);
type_register_static(&aspeed_pcie_cfg_info);
+ type_register_static(&aspeed_2700_pcie_cfg_info);
type_register_static(&aspeed_pcie_phy_info);
type_register_static(&aspeed_2700_pcie_phy_info);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 11/14] hw/pci-host/aspeed: Disable Root Device and place Root Port at 00:00.0 to AST2700
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (9 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 12/14] hw/arm/aspeed_ast27x0: Introduce 3 PCIe RCs for AST2700 Jamin Lin via
` (2 subsequent siblings)
13 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
AST2700 does not implement a PCIe Root Device; each RC exposes a single
PCIe Root Port at devfn 0:0.0.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
hw/pci-host/aspeed_pcie.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
index 4adf17e40b..5579e55900 100644
--- a/hw/pci-host/aspeed_pcie.c
+++ b/hw/pci-host/aspeed_pcie.c
@@ -828,6 +828,8 @@ static void aspeed_2700_pcie_cfg_class_init(ObjectClass *klass,
apc->nr_regs = 0x100 >> 2;
apc->rc_msi_addr = 0x000000F0;
apc->rc_bus_nr = 0;
+ apc->rc_has_rd = false;
+ apc->rc_rp_addr = PCI_DEVFN(0, 0);
}
static const TypeInfo aspeed_2700_pcie_cfg_info = {
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 12/14] hw/arm/aspeed_ast27x0: Introduce 3 PCIe RCs for AST2700
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (10 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 11/14] hw/pci-host/aspeed: Disable Root Device and place Root Port at 00:00.0 to AST2700 Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 14/14] tests/functional/aarch64/aspeed_ast2700: " Jamin Lin via
13 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Add PCIe Root Complex support to the AST2700 SoC model.
The AST2700 A1 silicon revision provides three PCIe Root Complexes:
PCIe0 with its PHY at 0x12C15000, config (H2X) block at 0x120E0000,
MMIO window at 0x60000000, and GIC IRQ 56.
PCIe1 with its PHY at 0x12C15800, config (H2X) block at 0x120F0000,
MMIO window at 0x80000000, and GIC IRQ 57.
PCIe2 with its PHY at 0x14C1C000, config (H2X) block at 0x140D0000,
MMIO window at 0xA0000000, and IRQ routed through INTC4 bit 31
mapped to GIC IRQ 196.
Each RC instantiates a PHY device, a PCIe config (H2X) bridge, and an MMIO
alias region. The per-RC MMIO alias size is 0x20000000. The AST2700 A0
silicon revision does not support PCIe Root Complexes, so pcie_num is set
to 0 in that variant.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
include/hw/arm/aspeed_soc.h | 1 +
hw/arm/aspeed_ast27x0.c | 61 +++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index 79fe353f83..070e2b49c5 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -185,6 +185,7 @@ struct AspeedSoCClass {
uint32_t silicon_rev;
uint64_t sram_size;
uint64_t secsram_size;
+ int pcie_num;
int spis_num;
int ehcis_num;
int wdts_num;
diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c
index 6aa3841b69..48296397ae 100644
--- a/hw/arm/aspeed_ast27x0.c
+++ b/hw/arm/aspeed_ast27x0.c
@@ -38,6 +38,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = {
[ASPEED_DEV_EHCI2] = 0x12063000,
[ASPEED_DEV_HACE] = 0x12070000,
[ASPEED_DEV_EMMC] = 0x12090000,
+ [ASPEED_DEV_PCIE0] = 0x120E0000,
+ [ASPEED_DEV_PCIE1] = 0x120F0000,
[ASPEED_DEV_INTC] = 0x12100000,
[ASPEED_GIC_DIST] = 0x12200000,
[ASPEED_GIC_REDIST] = 0x12280000,
@@ -45,6 +47,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = {
[ASPEED_DEV_SCU] = 0x12C02000,
[ASPEED_DEV_RTC] = 0x12C0F000,
[ASPEED_DEV_TIMER1] = 0x12C10000,
+ [ASPEED_DEV_PCIE_PHY0] = 0x12C15000,
+ [ASPEED_DEV_PCIE_PHY1] = 0x12C15800,
[ASPEED_DEV_SLI] = 0x12C17000,
[ASPEED_DEV_UART4] = 0x12C1A000,
[ASPEED_DEV_IOMEM1] = 0x14000000,
@@ -59,6 +63,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = {
[ASPEED_DEV_ETH2] = 0x14060000,
[ASPEED_DEV_ETH3] = 0x14070000,
[ASPEED_DEV_SDHCI] = 0x14080000,
+ [ASPEED_DEV_PCIE2] = 0x140D0000,
[ASPEED_DEV_EHCI3] = 0x14121000,
[ASPEED_DEV_EHCI4] = 0x14123000,
[ASPEED_DEV_ADC] = 0x14C00000,
@@ -66,6 +71,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = {
[ASPEED_DEV_GPIO] = 0x14C0B000,
[ASPEED_DEV_I2C] = 0x14C0F000,
[ASPEED_DEV_INTCIO] = 0x14C18000,
+ [ASPEED_DEV_PCIE_PHY2] = 0x14C1C000,
[ASPEED_DEV_SLIIO] = 0x14C1E000,
[ASPEED_DEV_VUART] = 0x14C30000,
[ASPEED_DEV_UART0] = 0x14C33000,
@@ -81,6 +87,9 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = {
[ASPEED_DEV_UART11] = 0x14C33A00,
[ASPEED_DEV_UART12] = 0x14C33B00,
[ASPEED_DEV_WDT] = 0x14C37000,
+ [ASPEED_DEV_PCIE_MMIO0] = 0x60000000,
+ [ASPEED_DEV_PCIE_MMIO1] = 0x80000000,
+ [ASPEED_DEV_PCIE_MMIO2] = 0xA0000000,
[ASPEED_DEV_SPI_BOOT] = 0x100000000,
[ASPEED_DEV_LTPI] = 0x300000000,
[ASPEED_DEV_SDRAM] = 0x400000000,
@@ -156,6 +165,8 @@ static const int aspeed_soc_ast2700a1_irqmap[] = {
[ASPEED_DEV_DP] = 28,
[ASPEED_DEV_EHCI1] = 33,
[ASPEED_DEV_EHCI2] = 37,
+ [ASPEED_DEV_PCIE0] = 56,
+ [ASPEED_DEV_PCIE1] = 57,
[ASPEED_DEV_LPC] = 192,
[ASPEED_DEV_IBT] = 192,
[ASPEED_DEV_KCS] = 192,
@@ -166,6 +177,7 @@ static const int aspeed_soc_ast2700a1_irqmap[] = {
[ASPEED_DEV_WDT] = 195,
[ASPEED_DEV_PWM] = 195,
[ASPEED_DEV_I3C] = 195,
+ [ASPEED_DEV_PCIE2] = 196,
[ASPEED_DEV_UART0] = 196,
[ASPEED_DEV_UART1] = 196,
[ASPEED_DEV_UART2] = 196,
@@ -233,6 +245,7 @@ static const int ast2700_gic132_gic196_intcmap[] = {
[ASPEED_DEV_UART12] = 18,
[ASPEED_DEV_EHCI3] = 28,
[ASPEED_DEV_EHCI4] = 29,
+ [ASPEED_DEV_PCIE2] = 31,
};
/* GICINT 133 */
@@ -519,6 +532,17 @@ static void aspeed_soc_ast2700_init(Object *obj)
snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname);
object_initialize_child(obj, "hace", &s->hace, typename);
+
+ for (i = 0; i < sc->pcie_num; i++) {
+ snprintf(typename, sizeof(typename), "aspeed.pcie-phy-%s", socname);
+ object_initialize_child(obj, "pcie-phy[*]", &s->pcie_phy[i], typename);
+ object_property_set_int(OBJECT(&s->pcie_phy[i]), "id", i, &error_abort);
+
+ snprintf(typename, sizeof(typename), "aspeed.pcie-cfg-%s", socname);
+ object_initialize_child(obj, "pcie-cfg[*]", &s->pcie[i], typename);
+ object_property_set_int(OBJECT(&s->pcie[i]), "id", i, &error_abort);
+ }
+
object_initialize_child(obj, "dpmcu", &s->dpmcu,
TYPE_UNIMPLEMENTED_DEVICE);
object_initialize_child(obj, "ltpi", &s->ltpi,
@@ -619,6 +643,8 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp)
AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc[0]);
AspeedINTCClass *icio = ASPEED_INTC_GET_CLASS(&a->intc[1]);
g_autofree char *name = NULL;
+ MemoryRegion *mmio_alias;
+ MemoryRegion *mmio_mr;
qemu_irq irq;
/* Default boot region (SPI memory or ROMs) */
@@ -936,6 +962,39 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0,
aspeed_soc_get_irq(s, ASPEED_DEV_HACE));
+ /* PCIe */
+ for (i = 0; i < sc->pcie_num; i++) {
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[i]), errp)) {
+ return;
+ }
+ aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[i]), 0,
+ sc->memmap[ASPEED_DEV_PCIE_PHY0 + i]);
+
+ object_property_set_int(OBJECT(&s->pcie[i]), "dram-base",
+ sc->memmap[ASPEED_DEV_SDRAM],
+ &error_abort);
+ object_property_set_link(OBJECT(&s->pcie[i]), "dram",
+ OBJECT(s->dram_mr), &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[i]), errp)) {
+ return;
+ }
+ aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[i]), 0,
+ sc->memmap[ASPEED_DEV_PCIE0 + i]);
+ irq = aspeed_soc_get_irq(s, ASPEED_DEV_PCIE0 + i);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[i].rc), 0, irq);
+
+ name = g_strdup_printf("aspeed.pcie-mmio.%d", i);
+ mmio_alias = g_new0(MemoryRegion, 1);
+ mmio_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[i].rc), 1);
+
+ memory_region_init_alias(mmio_alias, OBJECT(&s->pcie[i].rc), name,
+ mmio_mr, sc->memmap[ASPEED_DEV_PCIE_MMIO0 + i],
+ 0x20000000);
+ memory_region_add_subregion(s->memory,
+ sc->memmap[ASPEED_DEV_PCIE_MMIO0 + i],
+ mmio_alias);
+ }
+
aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu),
"aspeed.dpmcu",
sc->memmap[ASPEED_DEV_DPMCU],
@@ -974,6 +1033,7 @@ static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const void *data)
sc->valid_cpu_types = valid_cpu_types;
sc->silicon_rev = AST2700_A0_SILICON_REV;
sc->sram_size = 0x20000;
+ sc->pcie_num = 0;
sc->spis_num = 3;
sc->ehcis_num = 2;
sc->wdts_num = 8;
@@ -1002,6 +1062,7 @@ static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const void *data)
sc->valid_cpu_types = valid_cpu_types;
sc->silicon_rev = AST2700_A1_SILICON_REV;
sc->sram_size = 0x20000;
+ sc->pcie_num = 3;
sc->spis_num = 3;
sc->ehcis_num = 4;
sc->wdts_num = 8;
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (11 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 12/14] hw/arm/aspeed_ast27x0: Introduce 3 PCIe RCs for AST2700 Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
2025-09-15 17:21 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 14/14] tests/functional/aarch64/aspeed_ast2700: " Jamin Lin via
13 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Extend the AST2600 functional tests with a PCIe enumeration check.
This patch introduces a new helper "do_ast2600_pcie_test()" that runs
"lspci" on the emulated system and verifies the presence of the expected
PCIe devices:
- 80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600
- 80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
tests/functional/arm/test_aspeed_ast2600.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py
index 129695ca4e..f97fbbaa11 100755
--- a/tests/functional/arm/test_aspeed_ast2600.py
+++ b/tests/functional/arm/test_aspeed_ast2600.py
@@ -101,6 +101,14 @@ def test_arm_ast2600_evb_buildroot_tpm(self):
'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz',
'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba')
+ def do_ast2600_pcie_test(self):
+ exec_command_and_wait_for_pattern(self,
+ 'lspci -s 80:00.0',
+ '80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600')
+ exec_command_and_wait_for_pattern(self,
+ 'lspci -s 80:08.0',
+ '80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge')
+
def test_arm_ast2600_evb_sdk(self):
self.set_machine('ast2600-evb')
@@ -136,5 +144,7 @@ def test_arm_ast2600_evb_sdk(self):
exec_command_and_wait_for_pattern(self,
'/sbin/hwclock -f /dev/rtc1', year)
+ self.do_ast2600_pcie_test()
+
if __name__ == '__main__':
AspeedTest.main()
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH v2 14/14] tests/functional/aarch64/aspeed_ast2700: add PCIe enumeration test
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
` (12 preceding siblings ...)
2025-09-11 7:24 ` [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test Jamin Lin via
@ 2025-09-11 7:24 ` Jamin Lin via
13 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin via @ 2025-09-11 7:24 UTC (permalink / raw)
To: Paolo Bonzini, Peter Maydell, Cédric Le Goater, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: jamin_lin, troy_lee, nabihestefan, wuhaotsh, titusr
Add PCIe enumeration checks for AST2700 and AST2700fc functional tests.
This patch introduces a helper "do_ast2700_pcie_test()" that runs
"lspci -s 0002:00:00.0" on the emulated system and verifies the
expected PCIe device is present:
- 0002:00:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI
Bridge
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
tests/functional/aarch64/test_aspeed_ast2700.py | 7 +++++++
tests/functional/aarch64/test_aspeed_ast2700fc.py | 6 ++++++
2 files changed, 13 insertions(+)
diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py
index a3db267294..de3ef7d83b 100755
--- a/tests/functional/aarch64/test_aspeed_ast2700.py
+++ b/tests/functional/aarch64/test_aspeed_ast2700.py
@@ -69,6 +69,11 @@ def do_ast2700_i2c_test(self):
exec_command_and_wait_for_pattern(self,
'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000')
+ def do_ast2700_pcie_test(self):
+ exec_command_and_wait_for_pattern(self,
+ 'lspci -s 0002:00:00.0',
+ '0002:00:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge')
+
def start_ast2700_test(self, name):
num_cpu = 4
uboot_size = os.path.getsize(self.scratch_file(name,
@@ -130,6 +135,7 @@ def test_aarch64_ast2700a1_evb_sdk_v09_06(self):
self.start_ast2700_test('ast2700-default')
self.verify_openbmc_boot_and_login('ast2700-default')
self.do_ast2700_i2c_test()
+ self.do_ast2700_pcie_test()
def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_07(self):
self.set_machine('ast2700a1-evb')
@@ -139,6 +145,7 @@ def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_07(self):
self.verify_vbootrom_firmware_flow()
self.verify_openbmc_boot_and_login('ast2700-default')
self.do_ast2700_i2c_test()
+ self.do_ast2700_pcie_test()
if __name__ == '__main__':
QemuSystemTest.main()
diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py
index b85370e182..8ba2ddab0d 100755
--- a/tests/functional/aarch64/test_aspeed_ast2700fc.py
+++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py
@@ -49,6 +49,11 @@ def do_ast2700_i2c_test(self):
exec_command_and_wait_for_pattern(self,
'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000')
+ def do_ast2700_pcie_test(self):
+ exec_command_and_wait_for_pattern(self,
+ 'lspci -s 0002:00:00.0',
+ '0002:00:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge')
+
def do_ast2700fc_ssp_test(self):
self.vm.shutdown()
self.vm.set_console(console_index=1)
@@ -128,6 +133,7 @@ def test_aarch64_ast2700fc_sdk_v09_06(self):
self.start_ast2700fc_test('ast2700-default')
self.verify_openbmc_boot_and_login('ast2700-default')
self.do_ast2700_i2c_test()
+ self.do_ast2700_pcie_test()
self.do_ast2700fc_ssp_test()
self.do_ast2700fc_tsp_test()
--
2.43.0
^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model
2025-09-11 7:24 ` [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model Jamin Lin via
@ 2025-09-15 9:20 ` Cédric Le Goater
2025-09-16 5:32 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 9:20 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
Hello Jamin,
On 9/11/25 09:24, Jamin Lin wrote:
> This patch introduces an initial ASPEED PCIe PHY/host controller model to
> support the AST2600 SoC. It provides a simple register block with MMIO
> read/write callbacks, integration into the build system, and trace events
> for debugging.
>
> Key changes:
>
> 1. PCIe PHY MMIO read/write callbacks
> Implemented aspeed_pcie_phy_read() and aspeed_pcie_phy_write() to
> handle 32-bit register accesses.
>
> 2. Build system and Kconfig integration
> Added CONFIG_PCI_EXPRESS_ASPEED in hw/pci-host/Kconfig and meson
> rules.
> Updated ASPEED_SOC in hw/arm/Kconfig to imply PCI_DEVICES and select
> PCI_EXPRESS_ASPEED.
>
> 3. Trace events for debug
> New tracepoints aspeed_pcie_phy_read and aspeed_pcie_phy_write allow
> monitoring MMIO accesses.
>
> 4. Register space and defaults (AST2600 reference)
> Expose a 0x100 register space, as documented in the AST2600 datasheet.
> On reset, set default values:
> PEHR_ID: Vendor ID = ASPEED, Device ID = 0x1150
> PEHR_CLASS_CODE = 0x06040006
> PEHR_DATALINK = 0xD7040022
> PEHR_LINK: bit[5] set to 1 to indicate link up.
>
> This provides a skeleton device for the AST2600 platform. It enables
> firmware to detect the PCIe link as up by default and allows future
> extension.
There, you could say : based on previous work from me, plus add
a summary of the modifications that have been made.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
> include/hw/pci-host/aspeed_pcie.h | 44 +++++++++
> hw/pci-host/aspeed_pcie.c | 159 ++++++++++++++++++++++++++++++
> hw/arm/Kconfig | 3 +
> hw/pci-host/Kconfig | 4 +
> hw/pci-host/meson.build | 1 +
> hw/pci-host/trace-events | 4 +
> 6 files changed, 215 insertions(+)
> create mode 100644 include/hw/pci-host/aspeed_pcie.h
> create mode 100644 hw/pci-host/aspeed_pcie.c
>
> diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
> new file mode 100644
> index 0000000000..faf87073ab
> --- /dev/null
> +++ b/include/hw/pci-host/aspeed_pcie.h
> @@ -0,0 +1,44 @@
> +/*
> + * ASPEED PCIe Host Controller
> + *
> + * Copyright (C) 2025 ASPEED Technology Inc.
> + * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
> + *
> + * Jamin Lin <jamin_lin@aspeedtech.com>
I would add an "Authors" section and add our two names under it.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This file is based on Cédric Le Goater's patch:
> + * "pci: Add Aspeed host bridge (WIP)"
> + * https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc189854f9d26df
I don't think adding a reference on a github commit has much value
since I could remove the branch, which I do regularly. So, I would
remove it and simply keep the "Based on previous work from ... "
boiler plate.
The rest looks good.
> + * Modifications have been made to support the ASPEED AST2600 and AST2700
> + * platforms.
> + */
> +
> +#ifndef ASPEED_PCIE_H
> +#define ASPEED_PCIE_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/pci/pci_bridge.h"
> +#include "hw/pci/pcie_host.h"
> +#include "qom/object.h"
> +
> +#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> +OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY);
> +
> +struct AspeedPCIEPhyState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion mmio;
> + uint32_t *regs;
> + uint32_t id;
> +};
> +
> +struct AspeedPCIEPhyClass {
> + SysBusDeviceClass parent_class;
> +
> + uint64_t nr_regs;
> +};
> +
> +#endif /* ASPEED_PCIE_H */
> diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> new file mode 100644
> index 0000000000..5584449b17
> --- /dev/null
> +++ b/hw/pci-host/aspeed_pcie.c
> @@ -0,0 +1,159 @@
> +/*
> + * ASPEED PCIe Host Controller
> + *
> + * Copyright (C) 2025 ASPEED Technology Inc.
> + * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
> + *
> + * Jamin Lin <jamin_lin@aspeedtech.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * This file is based on Cédric Le Goater's patch:
> + * "pci: Add Aspeed host bridge (WIP)"
> + * https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc189854f9d26df
> + *
> + * Modifications have been made to support the ASPEED AST2600 and AST2700
> + * platforms.
Same comment
The rest looks good.
Thanks,
C.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/registerfields.h"
> +#include "hw/irq.h"
> +#include "hw/pci/pci_host.h"
> +#include "hw/pci-host/aspeed_pcie.h"
> +#include "hw/pci/msi.h"
> +#include "trace.h"
> +
> +/*
> + * PCIe PHY
> + *
> + * PCIe Host Controller (PCIEH)
> + */
> +
> +/* AST2600 */
> +REG32(PEHR_ID, 0x00)
> + FIELD(PEHR_ID, DEV, 16, 16)
> +REG32(PEHR_CLASS_CODE, 0x04)
> +REG32(PEHR_DATALINK, 0x10)
> +REG32(PEHR_PROTECT, 0x7C)
> + FIELD(PEHR_PROTECT, LOCK, 0, 8)
> +REG32(PEHR_LINK, 0xC0)
> + FIELD(PEHR_LINK, STS, 5, 1)
> +
> +#define ASPEED_PCIE_PHY_UNLOCK 0xA8
> +
> +static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr,
> + unsigned int size)
> +{
> + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
> + uint32_t reg = addr >> 2;
> + uint32_t value = 0;
> +
> + value = s->regs[reg];
> +
> + trace_aspeed_pcie_phy_read(s->id, addr, value);
> +
> + return value;
> +}
> +
> +static void aspeed_pcie_phy_write(void *opaque, hwaddr addr, uint64_t data,
> + unsigned int size)
> +{
> + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
> + uint32_t reg = addr >> 2;
> +
> + trace_aspeed_pcie_phy_write(s->id, addr, data);
> +
> + switch (reg) {
> + case R_PEHR_PROTECT:
> + data &= R_PEHR_PROTECT_LOCK_MASK;
> + s->regs[reg] = !!(data == ASPEED_PCIE_PHY_UNLOCK);
> + break;
> + default:
> + s->regs[reg] = data;
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps aspeed_pcie_phy_ops = {
> + .read = aspeed_pcie_phy_read,
> + .write = aspeed_pcie_phy_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4,
> + },
> +};
> +
> +static void aspeed_pcie_phy_reset(DeviceState *dev)
> +{
> + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
> + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
> +
> + memset(s->regs, 0, apc->nr_regs << 2);
> +
> + s->regs[R_PEHR_ID] =
> + (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED;
> + s->regs[R_PEHR_CLASS_CODE] = 0x06040006;
> + s->regs[R_PEHR_DATALINK] = 0xD7040022;
> + s->regs[R_PEHR_LINK] = R_PEHR_LINK_STS_MASK;
> +}
> +
> +static void aspeed_pcie_phy_realize(DeviceState *dev, Error **errp)
> +{
> + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
> + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> + g_autofree char *name;
> +
> + s->regs = g_new(uint32_t, apc->nr_regs);
> + name = g_strdup_printf(TYPE_ASPEED_PCIE_PHY ".regs.%d", s->id);
> + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_pcie_phy_ops, s, name,
> + apc->nr_regs << 2);
> + sysbus_init_mmio(sbd, &s->mmio);
> +}
> +
> +static void aspeed_pcie_phy_unrealize(DeviceState *dev)
> +{
> + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
> +
> + g_free(s->regs);
> + s->regs = NULL;
> +}
> +
> +static const Property aspeed_pcie_phy_props[] = {
> + DEFINE_PROP_UINT32("id", AspeedPCIEPhyState, id, 0),
> +};
> +
> +static void aspeed_pcie_phy_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass);
> +
> + dc->desc = "ASPEED PCIe Phy";
> + dc->realize = aspeed_pcie_phy_realize;
> + dc->unrealize = aspeed_pcie_phy_unrealize;
> + device_class_set_legacy_reset(dc, aspeed_pcie_phy_reset);
> + device_class_set_props(dc, aspeed_pcie_phy_props);
> +
> + apc->nr_regs = 0x100 >> 2;
> +}
> +
> +static const TypeInfo aspeed_pcie_phy_info = {
> + .name = TYPE_ASPEED_PCIE_PHY,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(AspeedPCIEPhyState),
> + .class_init = aspeed_pcie_phy_class_init,
> + .class_size = sizeof(AspeedPCIEPhyClass),
> +};
> +
> +static void aspeed_pcie_register_types(void)
> +{
> + type_register_static(&aspeed_pcie_phy_info);
> +}
> +
> +type_init(aspeed_pcie_register_types);
> +
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 2aa4b5d778..83d2676f6c 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -544,6 +544,7 @@ config ASPEED_SOC
> bool
> default y
> depends on TCG && ARM
> + imply PCI_DEVICES
> select DS1338
> select FTGMAC100
> select I2C
> @@ -564,6 +565,8 @@ config ASPEED_SOC
> select MAX31785
> select FSI_APB2OPB_ASPEED
> select AT24C
> + select PCI_EXPRESS
> + select PCI_EXPRESS_ASPEED
>
> config MPS2
> bool
> diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig
> index 9824fa188d..8cbb8304a3 100644
> --- a/hw/pci-host/Kconfig
> +++ b/hw/pci-host/Kconfig
> @@ -46,6 +46,10 @@ config PCI_I440FX
> select PCI
> select PAM
>
> +config PCI_EXPRESS_ASPEED
> + bool
> + select PCI_EXPRESS
> +
> config PCI_EXPRESS_Q35
> bool
> select PCI_EXPRESS
> diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build
> index 937a0f72ac..86b754d0b0 100644
> --- a/hw/pci-host/meson.build
> +++ b/hw/pci-host/meson.build
> @@ -2,6 +2,7 @@ pci_ss = ss.source_set()
> pci_ss.add(when: 'CONFIG_PAM', if_true: files('pam.c'))
> pci_ss.add(when: 'CONFIG_PCI_BONITO', if_true: files('bonito.c'))
> pci_ss.add(when: 'CONFIG_GT64120', if_true: files('gt64120.c'))
> +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_ASPEED', if_true: files('aspeed_pcie.c'))
> pci_ss.add(when: 'CONFIG_PCI_EXPRESS_DESIGNWARE', if_true: files('designware.c'))
> pci_ss.add(when: 'CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', if_true: files('gpex.c'))
> pci_ss.add(when: ['CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', 'CONFIG_ACPI'], if_true: files('gpex-acpi.c'))
> diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
> index 0a816b9aa1..3438516756 100644
> --- a/hw/pci-host/trace-events
> +++ b/hw/pci-host/trace-events
> @@ -1,5 +1,9 @@
> # See docs/devel/tracing.rst for syntax documentation.
>
> +# aspeed_pcie.c
> +aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> +aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> +
> # bonito.c
> bonito_spciconf_small_access(uint64_t addr, unsigned size) "PCI config address is smaller then 32-bit, addr: 0x%"PRIx64", size: %u"
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge
2025-09-11 7:24 ` [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge Jamin Lin via
@ 2025-09-15 16:51 ` Cédric Le Goater
2025-09-17 1:45 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 16:51 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Introduce PCIe config and host bridge model for the AST2600 platform.
>
> This patch adds support for the H2X (AHB to PCIe Bus Bridge) controller
> with a 0x100 byte register space. The register layout is shared between
> two root complexes: 0x00–0x7f is common, 0x80–0xbf for RC_L, and 0xc0–0xff
> for RC_H. Only RC_H is modeled in this implementation.
>
> The RC_H bus uses bus numbers in the 0x80–0xff range instead of the
> standard root bus 0x00. To allow the PCI subsystem to discover devices,
> the host bridge logic remaps the root bus number back to 0x00 whenever the
> configured bus number matches the "bus-nr" property.
>
> New MMIO callbacks are added for the H2X config space:
> - aspeed_pcie_cfg_read() and aspeed_pcie_cfg_write() handle register
> accesses.
> - aspeed_pcie_cfg_readwrite() provides configuration read/write support.
> - aspeed_pcie_cfg_translate_write() handles PCIe byte-enable semantics for
> write operations.
>
> The reset handler initializes the H2X register block with default values
> as defined in the AST2600 datasheet.
>
> Additional changes:
> - Implement ASPEED PCIe root complex (TYPE_ASPEED_PCIE_RC).
> - Wire up interrupt propagation via aspeed_pcie_rc_set_irq().
> - Add tracepoints for config read/write and INTx handling.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
> include/hw/pci-host/aspeed_pcie.h | 58 ++++
> hw/pci-host/aspeed_pcie.c | 422 ++++++++++++++++++++++++++++++
> hw/pci-host/trace-events | 4 +
> 3 files changed, 484 insertions(+)
>
> diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
> index faf87073ab..e2c5dc6f62 100644
> --- a/include/hw/pci-host/aspeed_pcie.h
> +++ b/include/hw/pci-host/aspeed_pcie.h
> @@ -24,6 +24,64 @@
> #include "hw/pci/pcie_host.h"
> #include "qom/object.h"
>
> +typedef struct AspeedPCIECfgTxDesc {
> + uint32_t desc0;
> + uint32_t desc1;
> + uint32_t desc2;
> + uint32_t desc3;
> + uint32_t wdata;
> + uint32_t rdata_reg;
> +} AspeedPCIECfgTxDesc;
> +
> +typedef struct AspeedPCIERcRegs {
> + uint32_t int_en_reg;
> + uint32_t int_sts_reg;
> +} AspeedPCIERcRegs;
> +
> +typedef struct AspeedPCIERegMap {
> + AspeedPCIERcRegs rc;
> +} AspeedPCIERegMap;
> +
> +#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
> +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
> +
> +struct AspeedPCIERcState {
> + PCIExpressHost parent_obj;
> +
> + MemoryRegion mmio_window;
> + MemoryRegion io_window;
> + MemoryRegion mmio;
> + MemoryRegion io;
> +
> + uint32_t bus_nr;
> + char name[16];
> + qemu_irq irq;
> +};
> +
> +/* Bridge between AHB bus and PCIe RC. */
> +#define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
> +OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass, ASPEED_PCIE_CFG);
> +
> +struct AspeedPCIECfgState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion mmio;
> + uint32_t *regs;
> + uint32_t id;
> +
> + AspeedPCIERcState rc;
> +};
> +
> +struct AspeedPCIECfgClass {
> + SysBusDeviceClass parent_class;
> +
> + const AspeedPCIERegMap *reg_map;
> + const MemoryRegionOps *reg_ops;
> +
> + uint64_t rc_bus_nr;
> + uint64_t nr_regs;
> +};
> +
> #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass, ASPEED_PCIE_PHY);
>
> diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> index 5584449b17..9fb7c1ef67 100644
> --- a/hw/pci-host/aspeed_pcie.c
> +++ b/hw/pci-host/aspeed_pcie.c
> @@ -27,6 +27,426 @@
> #include "hw/pci/msi.h"
> #include "trace.h"
>
> +/*
> + * PCIe Root Complex (RC)
> + */
> +
> +static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level)
> +{
> + AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
> + AspeedPCIECfgState *cfg =
> + container_of(rc, AspeedPCIECfgState, rc);
> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
> + const AspeedPCIERcRegs *rc_regs;
I suggest you cache &apc->reg_map->rc under AspeedPCIECfgState as
an attribute (at realize time). This will ease reading the code
and improve performance.
> + bool intx;
> +
> + assert(irq < PCI_NUM_PINS);
> +
> + rc_regs = &apc->reg_map->rc;
> +
> + if (level) {
> + cfg->regs[rc_regs->int_sts_reg] |= BIT(irq);
> + } else {
> + cfg->regs[rc_regs->int_sts_reg] &= ~BIT(irq);
> + }
> +
> + intx = !!(cfg->regs[rc_regs->int_sts_reg] & cfg->regs[rc_regs->int_en_reg]);
> + trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx);
> + qemu_set_irq(rc->irq, intx);
> +}
> +
> +static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num)
> +{
> + return irq_num % PCI_NUM_PINS;
> +}
> +
> +static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
> +{
> + PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
> + AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev);
> + AspeedPCIECfgState *cfg =
> + container_of(rc, AspeedPCIECfgState, rc);
> + PCIHostState *pci = PCI_HOST_BRIDGE(dev);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> + g_autofree char *name;
> +
> + /* PCI configuration space */
> + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
> + sysbus_init_mmio(sbd, &pex->mmio);
> +
> + /* MMIO and IO region */
> + memory_region_init(&rc->mmio, OBJECT(rc), "mmio", UINT64_MAX);
> + memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000);
> +
> + name = g_strdup_printf("pcie.%d.mmio_window", cfg->id);
> + memory_region_init_io(&rc->mmio_window, OBJECT(rc), &unassigned_io_ops,
> + OBJECT(rc), name, UINT64_MAX);
> + name = g_strdup_printf("pcie.%d.ioport_window", cfg->id);
> + memory_region_init_io(&rc->io_window, OBJECT(rc), &unassigned_io_ops,
> + OBJECT(rc), name, 0x10000);
> +
> + memory_region_add_subregion(&rc->mmio_window, 0, &rc->mmio);
> + memory_region_add_subregion(&rc->io_window, 0, &rc->io);
> + sysbus_init_mmio(sbd, &rc->mmio_window);
> + sysbus_init_mmio(sbd, &rc->io_window);
> +
> + sysbus_init_irq(sbd, &rc->irq);
> + name = g_strdup_printf("pcie.rc%d", cfg->id);
> + pci->bus = pci_register_root_bus(dev, name, aspeed_pcie_rc_set_irq,
> + aspeed_pcie_rc_map_irq, rc, &rc->mmio,
> + &rc->io, 0, 4, TYPE_PCIE_BUS);
> + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
> +}
> +
> +static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
> + PCIBus *rootbus)
> +{
> + AspeedPCIERcState *rc = ASPEED_PCIE_RC(host_bridge);
> + AspeedPCIECfgState *cfg =
> + container_of(rc, AspeedPCIECfgState, rc);
> +
> + snprintf(rc->name, sizeof(rc->name), "%04x:%02x", cfg->id, rc->bus_nr);
> +
> + return rc->name;
> +}
> +
> +static const Property aspeed_pcie_rc_props[] = {
> + DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
> +};
> +
> +static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
> +{
> + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->desc = "ASPEED PCIe RC";
> + dc->realize = aspeed_pcie_rc_realize;
> + dc->fw_name = "pci";
> + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +
> + hc->root_bus_path = aspeed_pcie_rc_root_bus_path;
> + device_class_set_props(dc, aspeed_pcie_rc_props);
> +
> + msi_nonbroken = true;
> +}
> +
> +static const TypeInfo aspeed_pcie_rc_info = {
> + .name = TYPE_ASPEED_PCIE_RC,
> + .parent = TYPE_PCIE_HOST_BRIDGE,
> + .instance_size = sizeof(AspeedPCIERcState),
> + .class_init = aspeed_pcie_rc_class_init,
> +};
> +
> +/*
> + * PCIe Config
> + *
> + * AHB to PCIe Bus Bridge (H2X)
> + *
> + * On the AST2600:
> + * NOTE: rc_l is not supported by this model.
> + * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
> + * - Registers 0x80 - 0xBF are specific to PCIe0.
> + * - Registers 0xC0 - 0xFF are specific to PCIe1.
> + */
> +
> +/* AST2600 */
> +REG32(H2X_CTRL, 0x00)
> + FIELD(H2X_CTRL, CLEAR_RX, 4, 1)
> +REG32(H2X_TX_CLEAR, 0x08)
> + FIELD(H2X_TX_CLEAR, IDLE, 0, 1)
> +REG32(H2X_RDATA, 0x0C)
> +REG32(H2X_TX_DESC0, 0x10)
> +REG32(H2X_TX_DESC1, 0x14)
> +REG32(H2X_TX_DESC2, 0x18)
> +REG32(H2X_TX_DESC3, 0x1C)
> +REG32(H2X_TX_DATA, 0x20)
> +REG32(H2X_TX_STS, 0x24)
> + FIELD(H2X_TX_STS, IDLE, 31, 1)
> + FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1)
> + FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1)
> + FIELD(H2X_TX_STS, TRIG, 0, 1)
> +REG32(H2X_RC_H_CTRL, 0xC0)
> +REG32(H2X_RC_H_INT_EN, 0xC4)
> +REG32(H2X_RC_H_INT_STS, 0xC8)
> + SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
> + SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
> +REG32(H2X_RC_H_RDATA, 0xCC)
> +
> +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
> +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
> +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
> +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
> +
> +#define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff)
> +#define PCIE_CFG_BYTE_EN(x) ((x) & 0xf)
> +
> +static const AspeedPCIERegMap aspeed_regmap = {
> + .rc = {
> + .int_en_reg = R_H2X_RC_H_INT_EN,
> + .int_sts_reg = R_H2X_RC_H_INT_STS,
> + },
> +};
> +
> +static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
> + unsigned int size)
> +{
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> + uint32_t reg = addr >> 2;
> + uint32_t value = 0;
> +
> + value = s->regs[reg];
> +
> + trace_aspeed_pcie_cfg_read(s->id, addr, value);
> +
> + return value;
> +}
> +
> +static void aspeed_pcie_cfg_translate_write(uint8_t byte_en, uint32_t *addr,
> + uint64_t *val, int *len)
> +{
> + uint64_t packed_val = 0;
> + int first_bit = -1;
> + int index = 0;
> + int i;
> +
> + *len = ctpop8(byte_en);
> +
> + if (*len == 0 || *len > 4) {
> + goto err;
> + }
> +
> + /* Special case: full 4-byte write must be 4-byte aligned */
> + if (byte_en == 0x0f) {
> + if (*addr % 4 != 0) {
This is an aligment issue to be reported as such and not with
"invalid byte enable"
I think you should remove the "goto err" and generate a
LOG_GUEST_ERROR instead.
> + goto err;
> + }
> + *val = *val & 0xffffffff;
> + return;
> + }
> +
> + for (i = 0; i < 4; i++) {
> + if (byte_en & (1 << i)) {
> + if (first_bit < 0) {
> + first_bit = i;
> + }
> + packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8);
> + index++;> + }
> + }
> +
> + *addr += first_bit;
> + *val = packed_val;
> +
> + return;
> +
> +err:
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable: 0x%x\n",
> + __func__, byte_en);
> +}
> +
> +static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s,
> + const AspeedPCIECfgTxDesc *desc)
> +{
> + AspeedPCIERcState *rc = &s->rc;
> + PCIHostState *pci;
> + uint32_t cfg_addr;
> + PCIDevice *pdev;
> + uint32_t offset;
> + uint8_t byte_en;
> + bool is_write;
> + uint8_t devfn;
> + uint64_t val;
> + uint8_t bus;
> + int len;
> +
> + val = ~0;
> + is_write = !!(desc->desc0 & BIT(30));
> + cfg_addr = desc->desc2;
hmm, what about endianess ?
> + bus = (cfg_addr >> 24) & 0xff;
> + devfn = (cfg_addr >> 16) & 0xff;
> + offset = cfg_addr & 0xffc;
> +
> + pci = PCI_HOST_BRIDGE(rc);
> +
> + /*
> + * On the AST2600, the RC_H bus number ranges from 0x80 to 0xFF, and its
> + * root port uses bus number 0x80 instead of the standard 0x00. To locate
> + * the device at root port 0, remap bus number 0x80 to 0x00 so that the
> + * PCI subsystem can correctly discover the devices.
> + */
> + if (bus == rc->bus_nr) {
> + bus = 0;
> + }
> +
> + pdev = pci_find_device(pci->bus, bus, devfn);
> + if (!pdev) {
> + s->regs[desc->rdata_reg] = ~0;
> + goto out;
> + }
> +
> + switch (PCIE_CFG_FMTTYPE_MASK(desc->desc0)) {
> + case TLP_FMTTYPE_CFGWR0:
> + case TLP_FMTTYPE_CFGWR1:
> + byte_en = PCIE_CFG_BYTE_EN(desc->desc1);
> + val = desc->wdata;
what about endianess ?
> + aspeed_pcie_cfg_translate_write(byte_en, &offset, &val, &len);
> + pci_host_config_write_common(pdev, offset, pci_config_size(pdev),
> + val, len);
> + break;
> + case TLP_FMTTYPE_CFGRD0:
> + case TLP_FMTTYPE_CFGRD1:
> + val = pci_host_config_read_common(pdev, offset,
> + pci_config_size(pdev), 4);
> + s->regs[desc->rdata_reg] = val;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type. DESC0=0x%x\n",
> + __func__, desc->desc0);
> + }
> +
> +out:
> + trace_aspeed_pcie_cfg_rw(s->id, is_write ? "write" : "read", bus, devfn,
> + cfg_addr, val);
> +}
> +
> +static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t data,
> + unsigned int size)
> +{
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> + AspeedPCIECfgTxDesc desc;
> + uint32_t reg = addr >> 2;
> + uint32_t rc_reg;
> +
> + trace_aspeed_pcie_cfg_write(s->id, addr, data);
> +
> + switch (reg) {
> + case R_H2X_CTRL:
> + if (data & R_H2X_CTRL_CLEAR_RX_MASK) {
> + s->regs[R_H2X_RDATA] = ~0;
> + }
> + break;
> + case R_H2X_TX_CLEAR:
> + if (data & R_H2X_TX_CLEAR_IDLE_MASK) {
> + s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_IDLE_MASK;
> + }
> + break;
> + case R_H2X_TX_STS:
> + if (data & R_H2X_TX_STS_TRIG_MASK) {
> + desc.desc0 = s->regs[R_H2X_TX_DESC0];
> + desc.desc1 = s->regs[R_H2X_TX_DESC1];
> + desc.desc2 = s->regs[R_H2X_TX_DESC2];
> + desc.desc3 = s->regs[R_H2X_TX_DESC3];
> + desc.wdata = s->regs[R_H2X_TX_DATA];
> + desc.rdata_reg = R_H2X_RC_H_RDATA;
endianess.
Thanks,
C.
> + aspeed_pcie_cfg_readwrite(s, &desc);
> + rc_reg = apc->reg_map->rc.int_sts_reg;
> + s->regs[rc_reg] |= H2X_RC_INT_INTDONE_MASK;
> + s->regs[R_H2X_TX_STS] |=
> + BIT(R_H2X_TX_STS_RC_H_TX_COMP_SHIFT);
> + s->regs[R_H2X_TX_STS] |= R_H2X_TX_STS_IDLE_MASK;
> + }
> + break;
> + /* preserve INTx status */
> + case R_H2X_RC_H_INT_STS:
> + if (data & H2X_RC_INT_INTDONE_MASK) {
> + s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_RC_H_TX_COMP_MASK;
> + }
> + s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK;
> + break;
> + default:
> + s->regs[reg] = data;
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps aspeed_pcie_cfg_ops = {
> + .read = aspeed_pcie_cfg_read,
> + .write = aspeed_pcie_cfg_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4,
> + },
> +};
> +
> +static void aspeed_pcie_cfg_instance_init(Object *obj)
> +{
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj);
> +
> + object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC);
> +
> + return;
> +}
> +
> +static void aspeed_pcie_cfg_reset(DeviceState *dev)
> +{
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> +
> + memset(s->regs, 0, apc->nr_regs << 2);
> +}
> +
> +static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
> +{
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> + g_autofree char *name;
> +
> + s->regs = g_new(uint32_t, apc->nr_regs);
> + name = g_strdup_printf(TYPE_ASPEED_PCIE_CFG ".regs.%d", s->id);
> + memory_region_init_io(&s->mmio, OBJECT(s), apc->reg_ops, s, name,
> + apc->nr_regs << 2);
> + sysbus_init_mmio(sbd, &s->mmio);
> +
> + object_property_set_int(OBJECT(&s->rc), "bus-nr",
> + apc->rc_bus_nr,
> + &error_abort);
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> + return;
> + }
> +}
> +
> +static void aspeed_pcie_cfg_unrealize(DeviceState *dev)
> +{
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> +
> + g_free(s->regs);
> + s->regs = NULL;
> +}
> +
> +static const Property aspeed_pcie_cfg_props[] = {
> + DEFINE_PROP_UINT32("id", AspeedPCIECfgState, id, 0),
> +};
> +
> +static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
> +
> + dc->desc = "ASPEED PCIe Config";
> + dc->realize = aspeed_pcie_cfg_realize;
> + dc->unrealize = aspeed_pcie_cfg_unrealize;
> + device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset);
> + device_class_set_props(dc, aspeed_pcie_cfg_props);
> +
> + apc->reg_ops = &aspeed_pcie_cfg_ops;
> + apc->reg_map = &aspeed_regmap;
> + apc->nr_regs = 0x100 >> 2;
> + apc->rc_bus_nr = 0x80;
> +}
> +
> +static const TypeInfo aspeed_pcie_cfg_info = {
> + .name = TYPE_ASPEED_PCIE_CFG,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_init = aspeed_pcie_cfg_instance_init,
> + .instance_size = sizeof(AspeedPCIECfgState),
> + .class_init = aspeed_pcie_cfg_class_init,
> + .class_size = sizeof(AspeedPCIECfgClass),
> +};
> +
> /*
> * PCIe PHY
> *
> @@ -152,6 +572,8 @@ static const TypeInfo aspeed_pcie_phy_info = {
>
> static void aspeed_pcie_register_types(void)
> {
> + type_register_static(&aspeed_pcie_rc_info);
> + type_register_static(&aspeed_pcie_cfg_info);
> type_register_static(&aspeed_pcie_phy_info);
> }
>
> diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
> index 3438516756..2584ea56e2 100644
> --- a/hw/pci-host/trace-events
> +++ b/hw/pci-host/trace-events
> @@ -1,6 +1,10 @@
> # See docs/devel/tracing.rst for syntax documentation.
>
> # aspeed_pcie.c
> +aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d: num %d set IRQ leve %d"
> +aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> +aspeed_pcie_cfg_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> +aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t bus, uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64
> aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
>
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support
2025-09-11 7:24 ` [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support Jamin Lin via
@ 2025-09-15 16:53 ` Cédric Le Goater
2025-09-16 2:42 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 16:53 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Introduce a PCIe Root Device for AST2600 platform.
>
> The AST2600 root complex exposes a PCIe root device at bus 80, devfn 0.
> This root device is implemented as a child of the PCIe RC and modeled
> as a host bridge PCI function (class_id = PCI_CLASS_BRIDGE_HOST).
>
> Key changes:
> - Add a new device type "aspeed.pcie-root-device".
> - Instantiate the root device as part of AspeedPCIERcState.
> - Initialize it during RC realize() and attach it to the root bus.
> - Mark the root device as non-user-creatable.
> - Add RC boolean property "has-rd" to control whether the Root Device is
> created (platforms can enable/disable it as needed).
>
> Note: Only AST2600 implements this PCIe root device. AST2700 does not
> provide one.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
> include/hw/pci-host/aspeed_pcie.h | 11 +++++++
> hw/pci-host/aspeed_pcie.c | 54 +++++++++++++++++++++++++++++++
> 2 files changed, 65 insertions(+)
>
> diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
> index e2c5dc6f62..e7c231e847 100644
> --- a/include/hw/pci-host/aspeed_pcie.h
> +++ b/include/hw/pci-host/aspeed_pcie.h
> @@ -42,6 +42,13 @@ typedef struct AspeedPCIERegMap {
> AspeedPCIERcRegs rc;
> } AspeedPCIERegMap;
>
> +#define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device"
> +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState, ASPEED_PCIE_ROOT_DEVICE);
> +
> +struct AspeedPCIERootDeviceState {
> + PCIBridge parent_obj;
> +};
> +
> #define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
> OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
>
> @@ -55,7 +62,10 @@ struct AspeedPCIERcState {
>
> uint32_t bus_nr;
> char name[16];
> + bool has_rd;
> qemu_irq irq;
> +
> + AspeedPCIERootDeviceState root_device;
> };
>
> /* Bridge between AHB bus and PCIe RC. */
> @@ -80,6 +90,7 @@ struct AspeedPCIECfgClass {
>
> uint64_t rc_bus_nr;
> uint64_t nr_regs;
> + bool rc_has_rd;
> };
>
> #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> index 9fb7c1ef67..fa8854fe7a 100644
> --- a/hw/pci-host/aspeed_pcie.c
> +++ b/hw/pci-host/aspeed_pcie.c
> @@ -27,6 +27,44 @@
> #include "hw/pci/msi.h"
> #include "trace.h"
>
> +/*
> + * PCIe Root Device
> + * This device exists only on AST2600.
> + */
> +
> +static void aspeed_pcie_root_device_class_init(ObjectClass *klass,
> + const void *data)
> +{
> + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> + dc->desc = "ASPEED PCIe Root Device";
> + k->vendor_id = PCI_VENDOR_ID_ASPEED;
> + k->device_id = 0x2600;
> + k->class_id = PCI_CLASS_BRIDGE_HOST;
> + k->subsystem_vendor_id = k->vendor_id;
> + k->subsystem_id = k->device_id;
> + k->revision = 0;
> +
> + /*
> + * PCI-facing part of the host bridge,
> + * not usable without the host-facing part
> + */
> + dc->user_creatable = false;
> +}
> +
> +static const TypeInfo aspeed_pcie_root_device_info = {
> + .name = TYPE_ASPEED_PCIE_ROOT_DEVICE,
> + .parent = TYPE_PCI_DEVICE,
> + .instance_size = sizeof(AspeedPCIERootDeviceState),
> + .class_init = aspeed_pcie_root_device_class_init,
> + .interfaces = (const InterfaceInfo[]) {
> + { INTERFACE_CONVENTIONAL_PCI_DEVICE },
> + { },
> + },
> +};
> +
> /*
> * PCIe Root Complex (RC)
> */
> @@ -96,6 +134,16 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
> aspeed_pcie_rc_map_irq, rc, &rc->mmio,
> &rc->io, 0, 4, TYPE_PCIE_BUS);
> pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
> +
> + /* setup root device */
> + if (rc->has_rd) {
> + object_initialize_child(OBJECT(rc), "root_device", &rc->root_device,
> + TYPE_ASPEED_PCIE_ROOT_DEVICE);
> + qdev_prop_set_int32(DEVICE(&rc->root_device), "addr",
> + PCI_DEVFN(0, 0));
> + qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction", false);
> + qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus), &error_fatal);
why not pass 'errp' instead ?
> + }
> }
>
> static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
> @@ -112,6 +160,7 @@ static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
>
> static const Property aspeed_pcie_rc_props[] = {
> DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
> + DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
> };
>
> static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
> @@ -404,6 +453,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
> object_property_set_int(OBJECT(&s->rc), "bus-nr",
> apc->rc_bus_nr,
> &error_abort);
> + object_property_set_bool(OBJECT(&s->rc), "has-rd",
> + apc->rc_has_rd,
> + &error_abort);
> if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> return;
> }
> @@ -436,6 +488,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
> apc->reg_map = &aspeed_regmap;
> apc->nr_regs = 0x100 >> 2;
> apc->rc_bus_nr = 0x80;
> + apc->rc_has_rd = true;
> }
>
> static const TypeInfo aspeed_pcie_cfg_info = {
> @@ -573,6 +626,7 @@ static const TypeInfo aspeed_pcie_phy_info = {
> static void aspeed_pcie_register_types(void)
> {
> type_register_static(&aspeed_pcie_rc_info);
> + type_register_static(&aspeed_pcie_root_device_info);
> type_register_static(&aspeed_pcie_cfg_info);
> type_register_static(&aspeed_pcie_phy_info);
> }
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable
2025-09-11 7:24 ` [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable Jamin Lin via
@ 2025-09-15 16:54 ` Cédric Le Goater
2025-09-16 2:51 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 16:54 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Introduce an ASPEED PCIe Root Port and wire it under the RC. The root port
> is modeled as TYPE_ASPEED_PCIE_ROOT_PORT (subclass of TYPE_PCIE_ROOT_PORT).
>
> Key changes:
> - Add TYPE_ASPEED_PCIE_ROOT_PORT (PCIESlot-based) with vendor/device IDs
> and AER capability offset.
> - Extend AspeedPCIERcState to embed a root_port instance and a
> configurable rp_addr.
> - Add "rp-addr" property to the RC to place the root port at a specific
> devfn on the root bus.
> - Set the root port's "chassis" property to ensure a unique chassis per RC.
> - Extend AspeedPCIECfgClass with rc_rp_addr defaulting to PCI_DEVFN(8,0).
>
> Rationale:
> - AST2600 places the root port at 80:08.0 (bus 0x80, dev 8, fn 0).
> - AST2700 must place the root port at 00:00.0, and it supports three RCs.
> Each root port must therefore be uniquely identifiable; uses the
> PCIe "chassis" ID for that.
> - Providing a configurable "rp-addr" lets platforms select the correct
> devfn per SoC family, while the "chassis" property ensures uniqueness
> across multiple RC instances on AST2700.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
> include/hw/pci-host/aspeed_pcie.h | 11 +++++++
> hw/pci-host/aspeed_pcie.c | 48 +++++++++++++++++++++++++++++++
> 2 files changed, 59 insertions(+)
>
> diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
> index e7c231e847..4026d5b4c5 100644
> --- a/include/hw/pci-host/aspeed_pcie.h
> +++ b/include/hw/pci-host/aspeed_pcie.h
> @@ -22,6 +22,7 @@
> #include "hw/sysbus.h"
> #include "hw/pci/pci_bridge.h"
> #include "hw/pci/pcie_host.h"
> +#include "hw/pci/pcie_port.h"
> #include "qom/object.h"
>
> typedef struct AspeedPCIECfgTxDesc {
> @@ -42,6 +43,13 @@ typedef struct AspeedPCIERegMap {
> AspeedPCIERcRegs rc;
> } AspeedPCIERegMap;
>
> +#define TYPE_ASPEED_PCIE_ROOT_PORT "aspeed.pcie-root-port"
> +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootPortState, ASPEED_PCIE_ROOT_PORT)
> +
> +typedef struct AspeedPCIERootPortState {
> + PCIESlot parent_obj;
> +} AspeedPCIERootPortState;
> +
> #define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device"
> OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState, ASPEED_PCIE_ROOT_DEVICE);
>
> @@ -60,12 +68,14 @@ struct AspeedPCIERcState {
> MemoryRegion mmio;
> MemoryRegion io;
>
> + uint32_t rp_addr;
> uint32_t bus_nr;
> char name[16];
> bool has_rd;
> qemu_irq irq;
>
> AspeedPCIERootDeviceState root_device;
> + AspeedPCIERootPortState root_port;
> };
>
> /* Bridge between AHB bus and PCIe RC. */
> @@ -88,6 +98,7 @@ struct AspeedPCIECfgClass {
> const AspeedPCIERegMap *reg_map;
> const MemoryRegionOps *reg_ops;
>
> + uint32_t rc_rp_addr;
> uint64_t rc_bus_nr;
> uint64_t nr_regs;
> bool rc_has_rd;
> diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> index fa8854fe7a..3f04f0d379 100644
> --- a/hw/pci-host/aspeed_pcie.c
> +++ b/hw/pci-host/aspeed_pcie.c
> @@ -23,6 +23,7 @@
> #include "hw/registerfields.h"
> #include "hw/irq.h"
> #include "hw/pci/pci_host.h"
> +#include "hw/pci/pcie_port.h"
> #include "hw/pci-host/aspeed_pcie.h"
> #include "hw/pci/msi.h"
> #include "trace.h"
> @@ -65,6 +66,32 @@ static const TypeInfo aspeed_pcie_root_device_info = {
> },
> };
>
> +/*
> + * PCIe Root Port
> + */
> +
> +static void aspeed_pcie_root_port_class_init(ObjectClass *klass,
> + const void *data)
> +{
> + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
> +
> + dc->desc = "ASPEED PCIe Root Port";
> + k->vendor_id = PCI_VENDOR_ID_ASPEED;
> + k->device_id = 0x1150;
> + dc->user_creatable = true;
> +
> + rpc->aer_offset = 0x100;
> +}
> +
> +static const TypeInfo aspeed_pcie_root_port_info = {
> + .name = TYPE_ASPEED_PCIE_ROOT_PORT,
> + .parent = TYPE_PCIE_ROOT_PORT,
> + .instance_size = sizeof(AspeedPCIERootPortState),
> + .class_init = aspeed_pcie_root_port_class_init,
> +};
> +
> /*
> * PCIe Root Complex (RC)
> */
> @@ -144,6 +171,11 @@ static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp)
> qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction", false);
> qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus), &error_fatal);
> }
> +
> + /* setup root port */
> + qdev_prop_set_int32(DEVICE(&rc->root_port), "addr", rc->rp_addr);
> + qdev_prop_set_uint16(DEVICE(&rc->root_port), "chassis", cfg->id);
> + qdev_realize(DEVICE(&rc->root_port), BUS(pci->bus), &error_fatal);
errp ?
C.
> }
>
> static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
> @@ -158,9 +190,19 @@ static const char *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
> return rc->name;
> }
>
> +static void aspeed_pcie_rc_instance_init(Object *obj)
> +{
> + AspeedPCIERcState *rc = ASPEED_PCIE_RC(obj);
> + AspeedPCIERootPortState *root_port = &rc->root_port;
> +
> + object_initialize_child(obj, "root_port", root_port,
> + TYPE_ASPEED_PCIE_ROOT_PORT);
> +}
> +
> static const Property aspeed_pcie_rc_props[] = {
> DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
> DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
> + DEFINE_PROP_UINT32("rp-addr", AspeedPCIERcState, rp_addr, 0),
> };
>
> static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void *data)
> @@ -183,6 +225,7 @@ static const TypeInfo aspeed_pcie_rc_info = {
> .name = TYPE_ASPEED_PCIE_RC,
> .parent = TYPE_PCIE_HOST_BRIDGE,
> .instance_size = sizeof(AspeedPCIERcState),
> + .instance_init = aspeed_pcie_rc_instance_init,
> .class_init = aspeed_pcie_rc_class_init,
> };
>
> @@ -456,6 +499,9 @@ static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
> object_property_set_bool(OBJECT(&s->rc), "has-rd",
> apc->rc_has_rd,
> &error_abort);
> + object_property_set_int(OBJECT(&s->rc), "rp-addr",
> + apc->rc_rp_addr,
> + &error_abort);
> if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> return;
> }
> @@ -489,6 +535,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void *data)
> apc->nr_regs = 0x100 >> 2;
> apc->rc_bus_nr = 0x80;
> apc->rc_has_rd = true;
> + apc->rc_rp_addr = PCI_DEVFN(8, 0);
> }
>
> static const TypeInfo aspeed_pcie_cfg_info = {
> @@ -627,6 +674,7 @@ static void aspeed_pcie_register_types(void)
> {
> type_register_static(&aspeed_pcie_rc_info);
> type_register_static(&aspeed_pcie_root_device_info);
> + type_register_static(&aspeed_pcie_root_port_info);
> type_register_static(&aspeed_pcie_cfg_info);
> type_register_static(&aspeed_pcie_phy_info);
> }
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 07/14] hw/arm/aspeed: Wire up PCIe devices in SoC model
2025-09-11 7:24 ` [PATCH v2 07/14] hw/arm/aspeed: Wire up PCIe devices in SoC model Jamin Lin via
@ 2025-09-15 17:14 ` Cédric Le Goater
0 siblings, 0 replies; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 17:14 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Add PCIe controller and PHY instances to the Aspeed SoC state and device
> enum. This prepares the SoC model to host PCIe Root Complexes and their
> associated PHYs.
>
> Although the AST2600 supports only a single Root Complex, the AST2700
> provides three Root Complexes. For this reason, the model defines arrays
> of three PCIe config/PHY objects and enumerates three PCIe device IDs so
> that both SoCs can be represented consistently.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Thanks,
C.
> ---
> include/hw/arm/aspeed_soc.h | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
> index 217ef0eafd..79fe353f83 100644
> --- a/include/hw/arm/aspeed_soc.h
> +++ b/include/hw/arm/aspeed_soc.h
> @@ -37,6 +37,7 @@
> #include "qom/object.h"
> #include "hw/misc/aspeed_lpc.h"
> #include "hw/misc/unimp.h"
> +#include "hw/pci-host/aspeed_pcie.h"
> #include "hw/misc/aspeed_peci.h"
> #include "hw/fsi/aspeed_apb2opb.h"
> #include "hw/char/serial-mm.h"
> @@ -49,6 +50,7 @@
> #define ASPEED_MACS_NUM 4
> #define ASPEED_UARTS_NUM 13
> #define ASPEED_JTAG_NUM 2
> +#define ASPEED_PCIE_NUM 3
>
> struct AspeedSoCState {
> DeviceState parent;
> @@ -87,6 +89,8 @@ struct AspeedSoCState {
> AspeedSDHCIState sdhci;
> AspeedSDHCIState emmc;
> AspeedLPCState lpc;
> + AspeedPCIECfgState pcie[ASPEED_PCIE_NUM];
> + AspeedPCIEPhyState pcie_phy[ASPEED_PCIE_NUM];
> AspeedPECIState peci;
> SerialMM uart[ASPEED_UARTS_NUM];
> Clock *sysclk;
> @@ -254,6 +258,15 @@ enum {
> ASPEED_DEV_LPC,
> ASPEED_DEV_IBT,
> ASPEED_DEV_I2C,
> + ASPEED_DEV_PCIE0,
> + ASPEED_DEV_PCIE1,
> + ASPEED_DEV_PCIE2,
> + ASPEED_DEV_PCIE_PHY0,
> + ASPEED_DEV_PCIE_PHY1,
> + ASPEED_DEV_PCIE_PHY2,
> + ASPEED_DEV_PCIE_MMIO0,
> + ASPEED_DEV_PCIE_MMIO1,
> + ASPEED_DEV_PCIE_MMIO2,
> ASPEED_DEV_PECI,
> ASPEED_DEV_ETH1,
> ASPEED_DEV_ETH2,
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only)
2025-09-11 7:24 ` [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only) Jamin Lin via
@ 2025-09-15 17:19 ` Cédric Le Goater
2025-09-16 3:41 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 17:19 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Wire up the PCIe Root Complex in the AST2600 SoC model.
>
> According to the AST2600 firmware driver, only the RC_H controller is
> supported. RC_H uses PCIe PHY1 at 0x1e6ed200 and the PCIe config (H2X)
> register block at 0x1e770000. The RC_H MMIO window is mapped at
> 0x70000000–0x80000000. RC_L is not modeled. The RC_H interrupt is
> wired to IRQ 168. Only RC_H is realized and connected to the SoC
> interrupt controller.
>
> The SoC integration initializes PCIe PHY1, instantiates a single RC
> instance, wires its MMIO regions, and connects its interrupt. An alias
> region is added to map the RC MMIO space into the guest physical address
> space.
>
> This provides enough functionality for firmware and guest drivers to
> discover and use the AST2600 RC_H Root Complex while leaving RC_L
> unimplemented.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
> hw/arm/aspeed_ast2600.c | 69 +++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 66 insertions(+), 3 deletions(-)
>
> diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
> index d12707f0ab..d521a1b4f0 100644
> --- a/hw/arm/aspeed_ast2600.c
> +++ b/hw/arm/aspeed_ast2600.c
> @@ -48,11 +48,13 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
> [ASPEED_DEV_XDMA] = 0x1E6E7000,
> [ASPEED_DEV_ADC] = 0x1E6E9000,
> [ASPEED_DEV_DP] = 0x1E6EB000,
> + [ASPEED_DEV_PCIE_PHY1] = 0x1E6ED200,
> [ASPEED_DEV_SBC] = 0x1E6F2000,
> [ASPEED_DEV_EMMC_BC] = 0x1E6f5000,
> [ASPEED_DEV_VIDEO] = 0x1E700000,
> [ASPEED_DEV_SDHCI] = 0x1E740000,
> [ASPEED_DEV_EMMC] = 0x1E750000,
> + [ASPEED_DEV_PCIE0] = 0x1E770000,
> [ASPEED_DEV_GPIO] = 0x1E780000,
> [ASPEED_DEV_GPIO_1_8V] = 0x1E780800,
> [ASPEED_DEV_RTC] = 0x1E781000,
> @@ -79,6 +81,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
> [ASPEED_DEV_FSI1] = 0x1E79B000,
> [ASPEED_DEV_FSI2] = 0x1E79B100,
> [ASPEED_DEV_I3C] = 0x1E7A0000,
> + [ASPEED_DEV_PCIE_MMIO1] = 0x70000000,
> [ASPEED_DEV_SDRAM] = 0x80000000,
> };
>
> @@ -127,6 +130,7 @@ static const int aspeed_soc_ast2600_irqmap[] = {
> [ASPEED_DEV_LPC] = 35,
> [ASPEED_DEV_IBT] = 143,
> [ASPEED_DEV_I2C] = 110, /* 110 -> 125 */
> + [ASPEED_DEV_PCIE0] = 168,
> [ASPEED_DEV_PECI] = 38,
> [ASPEED_DEV_ETH1] = 2,
> [ASPEED_DEV_ETH2] = 3,
> @@ -191,6 +195,10 @@ static void aspeed_soc_ast2600_init(Object *obj)
> snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname);
> object_initialize_child(obj, "i2c", &s->i2c, typename);
>
> + object_initialize_child(obj, "pcie-cfg", &s->pcie[0], TYPE_ASPEED_PCIE_CFG);
> + object_initialize_child(obj, "pcie-phy[*]", &s->pcie_phy[0],
> + TYPE_ASPEED_PCIE_PHY);
> +
> object_initialize_child(obj, "peci", &s->peci, TYPE_ASPEED_PECI);
>
> snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
> @@ -292,7 +300,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
> AspeedSoCState *s = ASPEED_SOC(dev);
> AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
> qemu_irq irq;
> - g_autofree char *sram_name = NULL;
> + g_autofree char *name = NULL;
> + MemoryRegion *mmio_alias;
> + MemoryRegion *mmio_mr;
>
> /* Default boot region (SPI memory or ROMs) */
> memory_region_init(&s->spi_boot_container, OBJECT(s),
> @@ -362,8 +372,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
> }
>
> /* SRAM */
> - sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index);
> - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size,
> + name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index);
> + if (!memory_region_init_ram(&s->sram, OBJECT(s), name, sc->sram_size,
> errp)) {
> return;
> }
> @@ -438,6 +448,59 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
> sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0,
> aspeed_soc_get_irq(s, ASPEED_DEV_PECI));
>
> + /*
> + * PCIe Root Complex (RC)
> + *
> + * H2X register space (single block 0x00-0xFF):
> + * 0x00-0x7F : shared by RC_L (PCIe0) and RC_H (PCIe1)
> + * 0x80-0xBF : RC_L only
> + * 0xC0-0xFF : RC_H only
> + *
> + * Model scope / limitations:
> + * - Firmware supports RC_H only; this QEMU model does not support RC_L.
> + * - RC_H uses PHY1 and the MMIO window [0x70000000, 0x80000000]
> + * (aka MMIO1).
> + *
> + * Indexing convention (this model):
> + * - Expose a single logical instance at index 0.
> + * - pcie[0] -> hardware RC_H (PCIe1)
> + * - phy[0] -> hardware PHY1
> + * - mmio.0 -> guest address range MMIO1: 0x70000000-0x80000000
> + * - RC_L / PCIe0 is not created and mapped.
> + */
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[0]), errp)) {
> + return;
> + }
> + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[0]), 0,
> + sc->memmap[ASPEED_DEV_PCIE_PHY1]);
> +
> + object_property_set_int(OBJECT(&s->pcie[0]), "dram-base",
> + sc->memmap[ASPEED_DEV_SDRAM],
> + &error_abort);
> + object_property_set_link(OBJECT(&s->pcie[0]), "dram", OBJECT(s->dram_mr),
> + &error_abort);
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[0]), errp)) {
> + return;
> + }
> + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[0]), 0,
> + sc->memmap[ASPEED_DEV_PCIE0]);
> +
> + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore),
> + sc->irqmap[ASPEED_DEV_PCIE0]);
> + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[0].rc), 0, irq);
> +
> + name = g_strdup_printf("aspeed.pcie-mmio.0");
> +
> + mmio_alias = g_new0(MemoryRegion, 1);
> + mmio_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[0].rc), 1);
> +
> + memory_region_init_alias(mmio_alias, OBJECT(&s->pcie[0].rc), name,
> + mmio_mr, sc->memmap[ASPEED_DEV_PCIE_MMIO1],
> + 0x10000000);
> + memory_region_add_subregion(s->memory,
> + sc->memmap[ASPEED_DEV_PCIE_MMIO1],
> + mmio_alias);
> +
Could you please introduce a sub routine to realize the PCIe RC model ?
name it aspeed_soc_ast2600_pcie_realize() maybe.
Thanks,
C.
> /* FMC, The number of CS is set at the board level */
> object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr),
> &error_abort);
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
2025-09-11 7:24 ` [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test Jamin Lin via
@ 2025-09-15 17:21 ` Cédric Le Goater
2025-09-16 7:30 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 17:21 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Extend the AST2600 functional tests with a PCIe enumeration check.
>
> This patch introduces a new helper "do_ast2600_pcie_test()" that runs
> "lspci" on the emulated system and verifies the presence of the expected
> PCIe devices:
>
> - 80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600
> - 80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> ---
> tests/functional/arm/test_aspeed_ast2600.py | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py
> index 129695ca4e..f97fbbaa11 100755
> --- a/tests/functional/arm/test_aspeed_ast2600.py
> +++ b/tests/functional/arm/test_aspeed_ast2600.py
> @@ -101,6 +101,14 @@ def test_arm_ast2600_evb_buildroot_tpm(self):
> 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz',
> 'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba')
>
> + def do_ast2600_pcie_test(self):
> + exec_command_and_wait_for_pattern(self,
> + 'lspci -s 80:00.0',
> + '80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600')
> + exec_command_and_wait_for_pattern(self,
> + 'lspci -s 80:08.0',
> + '80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge')
> +
I would add '-device e1000e,netdev=net1,bus=pcie.0 -netdev user,id=net1' and
check that an IP address is assigned to the interface.
Thanks,
C.
> def test_arm_ast2600_evb_sdk(self):
> self.set_machine('ast2600-evb')
>
> @@ -136,5 +144,7 @@ def test_arm_ast2600_evb_sdk(self):
> exec_command_and_wait_for_pattern(self,
> '/sbin/hwclock -f /dev/rtc1', year)
>
> + self.do_ast2600_pcie_test()
> +
> if __name__ == '__main__':
> AspeedTest.main()
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks
2025-09-11 7:24 ` [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks Jamin Lin via
@ 2025-09-15 17:23 ` Cédric Le Goater
2025-09-17 1:48 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-15 17:23 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: troy_lee, nabihestefan, wuhaotsh, titusr
On 9/11/25 09:24, Jamin Lin wrote:
> Introduce PCIe config (H2X) support for the AST2700 SoC.
>
> Unlike the AST2600, the AST2700 provides three independent Root Complexes,
> each with its own H2X (AHB to PCIe bridge) register block of size 0x100.
> All RCs use the same MSI address (0x000000F0). The H2X block includes
> two different access paths:
>
> 1. CFGI (internal bridge): used to access the host bridge itself, always
> with BDF=0. The AST2700 controller simplifies the design by exposing
> only one register (H2X_CFGI_TLP) with fields for ADDR[15:0], BEN[19:16],
> and WR[20]. This is not a full TLP descriptor as in the external case.
> For QEMU readability and code reuse, the model converts H2X_CFGI_TLP
> into a standard TLP TX descriptor with BDF forced to 0 and then calls
> the existing helpers aspeed_pcie_cfg_readwrite() and
> aspeed_pcie_cfg_translate_write().
>
> 2. CFGE (external EP access): used to access external endpoints. The
> AST2700 design provides H2X_CFGE_TLP1 and a small FIFO at H2X_CFGE_TLPN.
> For reads, TX DESC0 is stored in TLP1 and DESC1/DESC2 in TLPN FIFO
> slots. For writes, TX DESC0 is stored in TLP1, DESC1/DESC2 in TLPN
> FIFO[0..1], and TX write data in TLPN FIFO[2].
>
> The implementation extends AspeedPCIECfgState with a small FIFO and index,
> wires up new register definitions for AST2700, and adds a specific ops
> table and class (TYPE_ASPEED_2700_PCIE_CFG). The reset handler clears the
> FIFO state. Interrupt and MSI status registers are also supported.
>
> This provides enough modeling for firmware and drivers to use any of the
> three PCIe RCs on AST2700 with their own dedicated H2X config window,
> while reusing existing TLP decode helpers in QEMU.
>
> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
I thinks this implementation has endianess issues when accessing the
descriptors.
C.
> ---
> include/hw/pci-host/aspeed_pcie.h | 3 +
> hw/pci-host/aspeed_pcie.c | 158 ++++++++++++++++++++++++++++++
> 2 files changed, 161 insertions(+)
>
> diff --git a/include/hw/pci-host/aspeed_pcie.h b/include/hw/pci-host/aspeed_pcie.h
> index c0d46e6a03..d7b4b66f75 100644
> --- a/include/hw/pci-host/aspeed_pcie.h
> +++ b/include/hw/pci-host/aspeed_pcie.h
> @@ -89,6 +89,7 @@ struct AspeedPCIERcState {
>
> /* Bridge between AHB bus and PCIe RC. */
> #define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
> +#define TYPE_ASPEED_2700_PCIE_CFG TYPE_ASPEED_PCIE_CFG "-ast2700"
> OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass, ASPEED_PCIE_CFG);
>
> struct AspeedPCIECfgState {
> @@ -99,6 +100,8 @@ struct AspeedPCIECfgState {
> uint32_t id;
>
> AspeedPCIERcState rc;
> + uint32_t tlpn_fifo[3];
> + uint32_t tlpn_idx;
> };
>
> struct AspeedPCIECfgClass {
> diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> index bc491aa7bf..4adf17e40b 100644
> --- a/hw/pci-host/aspeed_pcie.c
> +++ b/hw/pci-host/aspeed_pcie.c
> @@ -336,6 +336,11 @@ static const TypeInfo aspeed_pcie_rc_info = {
> * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
> * - Registers 0x80 - 0xBF are specific to PCIe0.
> * - Registers 0xC0 - 0xFF are specific to PCIe1.
> + *
> + * On the AST2700:
> + * - The register range 0x00 - 0xFF is assigned to a single PCIe configuration.
> + * - There are three PCIe Root Complexes (RCs), each with its own dedicated H2X
> + * register set of size 0x100 (covering offsets 0x00 to 0xFF).
> */
>
> /* AST2600 */
> @@ -365,6 +370,31 @@ REG32(H2X_RC_H_MSI_EN1, 0xE4)
> REG32(H2X_RC_H_MSI_STS0, 0xE8)
> REG32(H2X_RC_H_MSI_STS1, 0xEC)
>
> +/* AST2700 */
> +REG32(H2X_CFGE_INT_STS, 0x08)
> + FIELD(H2X_CFGE_INT_STS, TX_IDEL, 0, 1)
> + FIELD(H2X_CFGE_INT_STS, RX_BUSY, 1, 1)
> +REG32(H2X_CFGI_TLP, 0x20)
> + FIELD(H2X_CFGI_TLP, ADDR, 0, 16)
> + FIELD(H2X_CFGI_TLP, BEN, 16, 4)
> + FIELD(H2X_CFGI_TLP, WR, 20, 1)
> +REG32(H2X_CFGI_WDATA, 0x24)
> +REG32(H2X_CFGI_CTRL, 0x28)
> + FIELD(H2X_CFGI_CTRL, FIRE, 0, 1)
> +REG32(H2X_CFGI_RDATA, 0x2C)
> +REG32(H2X_CFGE_TLP1, 0x30)
> +REG32(H2X_CFGE_TLPN, 0x34)
> +REG32(H2X_CFGE_CTRL, 0x38)
> + FIELD(H2X_CFGE_CTRL, FIRE, 0, 1)
> +REG32(H2X_CFGE_RDATA, 0x3C)
> +REG32(H2X_INT_EN, 0x40)
> +REG32(H2X_INT_STS, 0x48)
> + FIELD(H2X_INT_STS, INTX, 0, 4)
> +REG32(H2X_MSI_EN0, 0x50)
> +REG32(H2X_MSI_EN1, 0x54)
> +REG32(H2X_MSI_STS0, 0x58)
> +REG32(H2X_MSI_STS1, 0x5C)
> +
> #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
> #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
> #define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
> @@ -382,6 +412,15 @@ static const AspeedPCIERegMap aspeed_regmap = {
> },
> };
>
> +static const AspeedPCIERegMap aspeed_2700_regmap = {
> + .rc = {
> + .int_en_reg = R_H2X_INT_EN,
> + .int_sts_reg = R_H2X_INT_STS,
> + .msi_sts0_reg = R_H2X_MSI_STS0,
> + .msi_sts1_reg = R_H2X_MSI_STS1,
> + },
> +};
> +
> static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
> unsigned int size)
> {
> @@ -606,6 +645,8 @@ static void aspeed_pcie_cfg_reset(DeviceState *dev)
> AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
>
> memset(s->regs, 0, apc->nr_regs << 2);
> + memset(s->tlpn_fifo, 0, sizeof(s->tlpn_fifo));
> + s->tlpn_idx = 0;
> }
>
> static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
> @@ -679,6 +720,122 @@ static const TypeInfo aspeed_pcie_cfg_info = {
> .class_size = sizeof(AspeedPCIECfgClass),
> };
>
> +static void aspeed_2700_pcie_cfg_write(void *opaque, hwaddr addr,
> + uint64_t data, unsigned int size)
> +{
> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> + AspeedPCIECfgTxDesc desc;
> + uint32_t reg = addr >> 2;
> +
> + trace_aspeed_pcie_cfg_write(s->id, addr, data);
> +
> + switch (reg) {
> + case R_H2X_CFGE_INT_STS:
> + if (data & R_H2X_CFGE_INT_STS_TX_IDEL_MASK) {
> + s->regs[R_H2X_CFGE_INT_STS] &= ~R_H2X_CFGE_INT_STS_TX_IDEL_MASK;
> + }
> +
> + if (data & R_H2X_CFGE_INT_STS_RX_BUSY_MASK) {
> + s->regs[R_H2X_CFGE_INT_STS] &= ~R_H2X_CFGE_INT_STS_RX_BUSY_MASK;
> + }
> + break;
> + case R_H2X_CFGI_CTRL:
> + if (data & R_H2X_CFGI_CTRL_FIRE_MASK) {
> + /*
> + * Internal access to bridge
> + * Type and BDF are 0
> + */
> + desc.desc0 = 0x04000001 |
> + (ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, WR) << 30);
> + desc.desc1 = 0x00401000 |
> + ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, BEN);
> + desc.desc2 = 0x00000000 |
> + ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, ADDR);
> + desc.wdata = s->regs[R_H2X_CFGI_WDATA];
> + desc.rdata_reg = R_H2X_CFGI_RDATA;
> + aspeed_pcie_cfg_readwrite(s, &desc);
> + }
> + break;
> + case R_H2X_CFGE_TLPN:
> + s->tlpn_fifo[s->tlpn_idx] = data;
> + s->tlpn_idx = (s->tlpn_idx + 1) % ARRAY_SIZE(s->tlpn_fifo);
> + break;
> + case R_H2X_CFGE_CTRL:
> + if (data & R_H2X_CFGE_CTRL_FIRE_MASK) {
> + desc.desc0 = s->regs[R_H2X_CFGE_TLP1];
> + desc.desc1 = s->tlpn_fifo[0];
> + desc.desc2 = s->tlpn_fifo[1];
> + desc.wdata = s->tlpn_fifo[2];
> + desc.rdata_reg = R_H2X_CFGE_RDATA;
> + aspeed_pcie_cfg_readwrite(s, &desc);
> + s->regs[R_H2X_CFGE_INT_STS] |= R_H2X_CFGE_INT_STS_TX_IDEL_MASK;
> + s->regs[R_H2X_CFGE_INT_STS] |= R_H2X_CFGE_INT_STS_RX_BUSY_MASK;
> + s->tlpn_idx = 0;
> + }
> + break;
> +
> + case R_H2X_INT_STS:
> + s->regs[reg] &= ~data | R_H2X_INT_STS_INTX_MASK;
> + break;
> + /*
> + * These status registers are used for notify sources ISR are executed.
> + * If one source ISR is executed, it will clear one bit.
> + * If it clear all bits, it means to initialize this register status
> + * rather than sources ISR are executed.
> + */
> + case R_H2X_MSI_STS0:
> + case R_H2X_MSI_STS1:
> + if (data == 0) {
> + return ;
> + }
> +
> + s->regs[reg] &= ~data;
> + if (data == 0xffffffff) {
> + return;
> + }
> +
> + if (!s->regs[R_H2X_MSI_STS0] &&
> + !s->regs[R_H2X_MSI_STS1]) {
> + trace_aspeed_pcie_rc_msi_clear_irq(s->id, 0);
> + qemu_set_irq(s->rc.irq, 0);
> + }
> + break;
> + default:
> + s->regs[reg] = data;
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps aspeed_2700_pcie_cfg_ops = {
> + .read = aspeed_pcie_cfg_read,
> + .write = aspeed_2700_pcie_cfg_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4,
> + },
> +};
> +
> +static void aspeed_2700_pcie_cfg_class_init(ObjectClass *klass,
> + const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
> +
> + dc->desc = "ASPEED 2700 PCIe Config";
> + apc->reg_ops = &aspeed_2700_pcie_cfg_ops;
> + apc->reg_map = &aspeed_2700_regmap;
> + apc->nr_regs = 0x100 >> 2;
> + apc->rc_msi_addr = 0x000000F0;
> + apc->rc_bus_nr = 0;
> +}
> +
> +static const TypeInfo aspeed_2700_pcie_cfg_info = {
> + .name = TYPE_ASPEED_2700_PCIE_CFG,
> + .parent = TYPE_ASPEED_PCIE_CFG,
> + .class_init = aspeed_2700_pcie_cfg_class_init,
> +};
> +
> /*
> * PCIe PHY
> *
> @@ -846,6 +1003,7 @@ static void aspeed_pcie_register_types(void)
> type_register_static(&aspeed_pcie_root_device_info);
> type_register_static(&aspeed_pcie_root_port_info);
> type_register_static(&aspeed_pcie_cfg_info);
> + type_register_static(&aspeed_2700_pcie_cfg_info);
> type_register_static(&aspeed_pcie_phy_info);
> type_register_static(&aspeed_2700_pcie_phy_info);
> }
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support
2025-09-15 16:53 ` [SPAM] " Cédric Le Goater
@ 2025-09-16 2:42 ` Jamin Lin
0 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin @ 2025-09-16 2:42 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: Re: [SPAM] [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe
> Root Device support
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > Introduce a PCIe Root Device for AST2600 platform.
> >
> > The AST2600 root complex exposes a PCIe root device at bus 80, devfn 0.
> > This root device is implemented as a child of the PCIe RC and modeled
> > as a host bridge PCI function (class_id = PCI_CLASS_BRIDGE_HOST).
> >
> > Key changes:
> > - Add a new device type "aspeed.pcie-root-device".
> > - Instantiate the root device as part of AspeedPCIERcState.
> > - Initialize it during RC realize() and attach it to the root bus.
> > - Mark the root device as non-user-creatable.
> > - Add RC boolean property "has-rd" to control whether the Root Device is
> > created (platforms can enable/disable it as needed).
> >
> > Note: Only AST2600 implements this PCIe root device. AST2700 does not
> > provide one.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> > include/hw/pci-host/aspeed_pcie.h | 11 +++++++
> > hw/pci-host/aspeed_pcie.c | 54
> +++++++++++++++++++++++++++++++
> > 2 files changed, 65 insertions(+)
> >
> > diff --git a/include/hw/pci-host/aspeed_pcie.h
> > b/include/hw/pci-host/aspeed_pcie.h
> > index e2c5dc6f62..e7c231e847 100644
> > --- a/include/hw/pci-host/aspeed_pcie.h
> > +++ b/include/hw/pci-host/aspeed_pcie.h
> > @@ -42,6 +42,13 @@ typedef struct AspeedPCIERegMap {
> > AspeedPCIERcRegs rc;
> > } AspeedPCIERegMap;
> >
> > +#define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device"
> > +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState,
> > +ASPEED_PCIE_ROOT_DEVICE);
> > +
> > +struct AspeedPCIERootDeviceState {
> > + PCIBridge parent_obj;
> > +};
> > +
> > #define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
> > OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
> >
> > @@ -55,7 +62,10 @@ struct AspeedPCIERcState {
> >
> > uint32_t bus_nr;
> > char name[16];
> > + bool has_rd;
> > qemu_irq irq;
> > +
> > + AspeedPCIERootDeviceState root_device;
> > };
> >
> > /* Bridge between AHB bus and PCIe RC. */ @@ -80,6 +90,7 @@ struct
> > AspeedPCIECfgClass {
> >
> > uint64_t rc_bus_nr;
> > uint64_t nr_regs;
> > + bool rc_has_rd;
> > };
> >
> > #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> > diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> > index 9fb7c1ef67..fa8854fe7a 100644
> > --- a/hw/pci-host/aspeed_pcie.c
> > +++ b/hw/pci-host/aspeed_pcie.c
> > @@ -27,6 +27,44 @@
> > #include "hw/pci/msi.h"
> > #include "trace.h"
> >
> > +/*
> > + * PCIe Root Device
> > + * This device exists only on AST2600.
> > + */
> > +
> > +static void aspeed_pcie_root_device_class_init(ObjectClass *klass,
> > + const void *data)
> {
> > + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> > + dc->desc = "ASPEED PCIe Root Device";
> > + k->vendor_id = PCI_VENDOR_ID_ASPEED;
> > + k->device_id = 0x2600;
> > + k->class_id = PCI_CLASS_BRIDGE_HOST;
> > + k->subsystem_vendor_id = k->vendor_id;
> > + k->subsystem_id = k->device_id;
> > + k->revision = 0;
> > +
> > + /*
> > + * PCI-facing part of the host bridge,
> > + * not usable without the host-facing part
> > + */
> > + dc->user_creatable = false;
> > +}
> > +
> > +static const TypeInfo aspeed_pcie_root_device_info = {
> > + .name = TYPE_ASPEED_PCIE_ROOT_DEVICE,
> > + .parent = TYPE_PCI_DEVICE,
> > + .instance_size = sizeof(AspeedPCIERootDeviceState),
> > + .class_init = aspeed_pcie_root_device_class_init,
> > + .interfaces = (const InterfaceInfo[]) {
> > + { INTERFACE_CONVENTIONAL_PCI_DEVICE },
> > + { },
> > + },
> > +};
> > +
> > /*
> > * PCIe Root Complex (RC)
> > */
> > @@ -96,6 +134,16 @@ static void aspeed_pcie_rc_realize(DeviceState *dev,
> Error **errp)
> > aspeed_pcie_rc_map_irq, rc,
> &rc->mmio,
> > &rc->io, 0, 4,
> TYPE_PCIE_BUS);
> > pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
> > +
> > + /* setup root device */
> > + if (rc->has_rd) {
> > + object_initialize_child(OBJECT(rc), "root_device",
> &rc->root_device,
> > +
> TYPE_ASPEED_PCIE_ROOT_DEVICE);
> > + qdev_prop_set_int32(DEVICE(&rc->root_device), "addr",
> > + PCI_DEVFN(0, 0));
> > + qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction",
> false);
> > + qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus),
> > + &error_fatal);
>
>
> why not pass 'errp' instead ?
>
Thanks for your review and suggestion.
Will fix it.
Jamin
>
> > + }
> > }
> >
> > static const char *aspeed_pcie_rc_root_bus_path(PCIHostState
> > *host_bridge, @@ -112,6 +160,7 @@ static const char
> > *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
> >
> > static const Property aspeed_pcie_rc_props[] = {
> > DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
> > + DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
> > };
> >
> > static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void
> > *data) @@ -404,6 +453,9 @@ static void
> aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
> > object_property_set_int(OBJECT(&s->rc), "bus-nr",
> > apc->rc_bus_nr,
> > &error_abort);
> > + object_property_set_bool(OBJECT(&s->rc), "has-rd",
> > + apc->rc_has_rd,
> > + &error_abort);
> > if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> > return;
> > }
> > @@ -436,6 +488,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass
> *klass, const void *data)
> > apc->reg_map = &aspeed_regmap;
> > apc->nr_regs = 0x100 >> 2;
> > apc->rc_bus_nr = 0x80;
> > + apc->rc_has_rd = true;
> > }
> >
> > static const TypeInfo aspeed_pcie_cfg_info = { @@ -573,6 +626,7 @@
> > static const TypeInfo aspeed_pcie_phy_info = {
> > static void aspeed_pcie_register_types(void)
> > {
> > type_register_static(&aspeed_pcie_rc_info);
> > + type_register_static(&aspeed_pcie_root_device_info);
> > type_register_static(&aspeed_pcie_cfg_info);
> > type_register_static(&aspeed_pcie_phy_info);
> > }
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable
2025-09-15 16:54 ` [SPAM] " Cédric Le Goater
@ 2025-09-16 2:51 ` Jamin Lin
0 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin @ 2025-09-16 2:51 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: Re: [SPAM] [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe
> Root Port and make address configurable
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > Introduce an ASPEED PCIe Root Port and wire it under the RC. The root
> > port is modeled as TYPE_ASPEED_PCIE_ROOT_PORT (subclass of
> TYPE_PCIE_ROOT_PORT).
> >
> > Key changes:
> > - Add TYPE_ASPEED_PCIE_ROOT_PORT (PCIESlot-based) with vendor/device
> IDs
> > and AER capability offset.
> > - Extend AspeedPCIERcState to embed a root_port instance and a
> > configurable rp_addr.
> > - Add "rp-addr" property to the RC to place the root port at a specific
> > devfn on the root bus.
> > - Set the root port's "chassis" property to ensure a unique chassis per RC.
> > - Extend AspeedPCIECfgClass with rc_rp_addr defaulting to PCI_DEVFN(8,0).
> >
> > Rationale:
> > - AST2600 places the root port at 80:08.0 (bus 0x80, dev 8, fn 0).
> > - AST2700 must place the root port at 00:00.0, and it supports three RCs.
> > Each root port must therefore be uniquely identifiable; uses the
> > PCIe "chassis" ID for that.
> > - Providing a configurable "rp-addr" lets platforms select the correct
> > devfn per SoC family, while the "chassis" property ensures uniqueness
> > across multiple RC instances on AST2700.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> > include/hw/pci-host/aspeed_pcie.h | 11 +++++++
> > hw/pci-host/aspeed_pcie.c | 48
> +++++++++++++++++++++++++++++++
> > 2 files changed, 59 insertions(+)
> >
> > diff --git a/include/hw/pci-host/aspeed_pcie.h
> > b/include/hw/pci-host/aspeed_pcie.h
> > index e7c231e847..4026d5b4c5 100644
> > --- a/include/hw/pci-host/aspeed_pcie.h
> > +++ b/include/hw/pci-host/aspeed_pcie.h
> > @@ -22,6 +22,7 @@
> > #include "hw/sysbus.h"
> > #include "hw/pci/pci_bridge.h"
> > #include "hw/pci/pcie_host.h"
> > +#include "hw/pci/pcie_port.h"
> > #include "qom/object.h"
> >
> > typedef struct AspeedPCIECfgTxDesc { @@ -42,6 +43,13 @@ typedef
> > struct AspeedPCIERegMap {
> > AspeedPCIERcRegs rc;
> > } AspeedPCIERegMap;
> >
> > +#define TYPE_ASPEED_PCIE_ROOT_PORT "aspeed.pcie-root-port"
> > +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootPortState,
> > +ASPEED_PCIE_ROOT_PORT)
> > +
> > +typedef struct AspeedPCIERootPortState {
> > + PCIESlot parent_obj;
> > +} AspeedPCIERootPortState;
> > +
> > #define TYPE_ASPEED_PCIE_ROOT_DEVICE "aspeed.pcie-root-device"
> > OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERootDeviceState,
> > ASPEED_PCIE_ROOT_DEVICE);
> >
> > @@ -60,12 +68,14 @@ struct AspeedPCIERcState {
> > MemoryRegion mmio;
> > MemoryRegion io;
> >
> > + uint32_t rp_addr;
> > uint32_t bus_nr;
> > char name[16];
> > bool has_rd;
> > qemu_irq irq;
> >
> > AspeedPCIERootDeviceState root_device;
> > + AspeedPCIERootPortState root_port;
> > };
> >
> > /* Bridge between AHB bus and PCIe RC. */ @@ -88,6 +98,7 @@ struct
> > AspeedPCIECfgClass {
> > const AspeedPCIERegMap *reg_map;
> > const MemoryRegionOps *reg_ops;
> >
> > + uint32_t rc_rp_addr;
> > uint64_t rc_bus_nr;
> > uint64_t nr_regs;
> > bool rc_has_rd;
> > diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> > index fa8854fe7a..3f04f0d379 100644
> > --- a/hw/pci-host/aspeed_pcie.c
> > +++ b/hw/pci-host/aspeed_pcie.c
> > @@ -23,6 +23,7 @@
> > #include "hw/registerfields.h"
> > #include "hw/irq.h"
> > #include "hw/pci/pci_host.h"
> > +#include "hw/pci/pcie_port.h"
> > #include "hw/pci-host/aspeed_pcie.h"
> > #include "hw/pci/msi.h"
> > #include "trace.h"
> > @@ -65,6 +66,32 @@ static const TypeInfo aspeed_pcie_root_device_info =
> {
> > },
> > };
> >
> > +/*
> > + * PCIe Root Port
> > + */
> > +
> > +static void aspeed_pcie_root_port_class_init(ObjectClass *klass,
> > + const void *data) {
> > + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > + PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
> > +
> > + dc->desc = "ASPEED PCIe Root Port";
> > + k->vendor_id = PCI_VENDOR_ID_ASPEED;
> > + k->device_id = 0x1150;
> > + dc->user_creatable = true;
> > +
> > + rpc->aer_offset = 0x100;
> > +}
> > +
> > +static const TypeInfo aspeed_pcie_root_port_info = {
> > + .name = TYPE_ASPEED_PCIE_ROOT_PORT,
> > + .parent = TYPE_PCIE_ROOT_PORT,
> > + .instance_size = sizeof(AspeedPCIERootPortState),
> > + .class_init = aspeed_pcie_root_port_class_init, };
> > +
> > /*
> > * PCIe Root Complex (RC)
> > */
> > @@ -144,6 +171,11 @@ static void aspeed_pcie_rc_realize(DeviceState
> *dev, Error **errp)
> > qdev_prop_set_bit(DEVICE(&rc->root_device), "multifunction",
> false);
> > qdev_realize(DEVICE(&rc->root_device), BUS(pci->bus),
> &error_fatal);
> > }
> > +
> > + /* setup root port */
> > + qdev_prop_set_int32(DEVICE(&rc->root_port), "addr", rc->rp_addr);
> > + qdev_prop_set_uint16(DEVICE(&rc->root_port), "chassis", cfg->id);
> > + qdev_realize(DEVICE(&rc->root_port), BUS(pci->bus),
> > + &error_fatal);
>
> errp ?
>
Thanks for your review and suggestion.
Will fix it.
Jamin
> C.
>
>
> > }
> >
> > static const char *aspeed_pcie_rc_root_bus_path(PCIHostState
> > *host_bridge, @@ -158,9 +190,19 @@ static const char
> *aspeed_pcie_rc_root_bus_path(PCIHostState *host_bridge,
> > return rc->name;
> > }
> >
> > +static void aspeed_pcie_rc_instance_init(Object *obj) {
> > + AspeedPCIERcState *rc = ASPEED_PCIE_RC(obj);
> > + AspeedPCIERootPortState *root_port = &rc->root_port;
> > +
> > + object_initialize_child(obj, "root_port", root_port,
> > + TYPE_ASPEED_PCIE_ROOT_PORT); }
> > +
> > static const Property aspeed_pcie_rc_props[] = {
> > DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0),
> > DEFINE_PROP_BOOL("has-rd", AspeedPCIERcState, has_rd, 0),
> > + DEFINE_PROP_UINT32("rp-addr", AspeedPCIERcState, rp_addr, 0),
> > };
> >
> > static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void
> > *data) @@ -183,6 +225,7 @@ static const TypeInfo aspeed_pcie_rc_info = {
> > .name = TYPE_ASPEED_PCIE_RC,
> > .parent = TYPE_PCIE_HOST_BRIDGE,
> > .instance_size = sizeof(AspeedPCIERcState),
> > + .instance_init = aspeed_pcie_rc_instance_init,
> > .class_init = aspeed_pcie_rc_class_init,
> > };
> >
> > @@ -456,6 +499,9 @@ static void aspeed_pcie_cfg_realize(DeviceState
> *dev, Error **errp)
> > object_property_set_bool(OBJECT(&s->rc), "has-rd",
> > apc->rc_has_rd,
> > &error_abort);
> > + object_property_set_int(OBJECT(&s->rc), "rp-addr",
> > + apc->rc_rp_addr,
> > + &error_abort);
> > if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> > return;
> > }
> > @@ -489,6 +535,7 @@ static void aspeed_pcie_cfg_class_init(ObjectClass
> *klass, const void *data)
> > apc->nr_regs = 0x100 >> 2;
> > apc->rc_bus_nr = 0x80;
> > apc->rc_has_rd = true;
> > + apc->rc_rp_addr = PCI_DEVFN(8, 0);
> > }
> >
> > static const TypeInfo aspeed_pcie_cfg_info = { @@ -627,6 +674,7 @@
> > static void aspeed_pcie_register_types(void)
> > {
> > type_register_static(&aspeed_pcie_rc_info);
> > type_register_static(&aspeed_pcie_root_device_info);
> > + type_register_static(&aspeed_pcie_root_port_info);
> > type_register_static(&aspeed_pcie_cfg_info);
> > type_register_static(&aspeed_pcie_phy_info);
> > }
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only)
2025-09-15 17:19 ` [SPAM] " Cédric Le Goater
@ 2025-09-16 3:41 ` Jamin Lin
0 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin @ 2025-09-16 3:41 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: Re: [SPAM] [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC
> support (RC_H only)
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > Wire up the PCIe Root Complex in the AST2600 SoC model.
> >
> > According to the AST2600 firmware driver, only the RC_H controller is
> > supported. RC_H uses PCIe PHY1 at 0x1e6ed200 and the PCIe config (H2X)
> > register block at 0x1e770000. The RC_H MMIO window is mapped at
> > 0x70000000–0x80000000. RC_L is not modeled. The RC_H interrupt is
> > wired to IRQ 168. Only RC_H is realized and connected to the SoC
> > interrupt controller.
> >
> > The SoC integration initializes PCIe PHY1, instantiates a single RC
> > instance, wires its MMIO regions, and connects its interrupt. An alias
> > region is added to map the RC MMIO space into the guest physical
> > address space.
> >
> > This provides enough functionality for firmware and guest drivers to
> > discover and use the AST2600 RC_H Root Complex while leaving RC_L
> > unimplemented.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> > hw/arm/aspeed_ast2600.c | 69
> +++++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 66 insertions(+), 3 deletions(-)
> >
> > diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index
> > d12707f0ab..d521a1b4f0 100644
> > --- a/hw/arm/aspeed_ast2600.c
> > +++ b/hw/arm/aspeed_ast2600.c
> > @@ -48,11 +48,13 @@ static const hwaddr aspeed_soc_ast2600_memmap[]
> = {
> > [ASPEED_DEV_XDMA] = 0x1E6E7000,
> > [ASPEED_DEV_ADC] = 0x1E6E9000,
> > [ASPEED_DEV_DP] = 0x1E6EB000,
> > + [ASPEED_DEV_PCIE_PHY1] = 0x1E6ED200,
> > [ASPEED_DEV_SBC] = 0x1E6F2000,
> > [ASPEED_DEV_EMMC_BC] = 0x1E6f5000,
> > [ASPEED_DEV_VIDEO] = 0x1E700000,
> > [ASPEED_DEV_SDHCI] = 0x1E740000,
> > [ASPEED_DEV_EMMC] = 0x1E750000,
> > + [ASPEED_DEV_PCIE0] = 0x1E770000,
> > [ASPEED_DEV_GPIO] = 0x1E780000,
> > [ASPEED_DEV_GPIO_1_8V] = 0x1E780800,
> > [ASPEED_DEV_RTC] = 0x1E781000,
> > @@ -79,6 +81,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] =
> {
> > [ASPEED_DEV_FSI1] = 0x1E79B000,
> > [ASPEED_DEV_FSI2] = 0x1E79B100,
> > [ASPEED_DEV_I3C] = 0x1E7A0000,
> > + [ASPEED_DEV_PCIE_MMIO1] = 0x70000000,
> > [ASPEED_DEV_SDRAM] = 0x80000000,
> > };
> >
> > @@ -127,6 +130,7 @@ static const int aspeed_soc_ast2600_irqmap[] = {
> > [ASPEED_DEV_LPC] = 35,
> > [ASPEED_DEV_IBT] = 143,
> > [ASPEED_DEV_I2C] = 110, /* 110 -> 125 */
> > + [ASPEED_DEV_PCIE0] = 168,
> > [ASPEED_DEV_PECI] = 38,
> > [ASPEED_DEV_ETH1] = 2,
> > [ASPEED_DEV_ETH2] = 3,
> > @@ -191,6 +195,10 @@ static void aspeed_soc_ast2600_init(Object *obj)
> > snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname);
> > object_initialize_child(obj, "i2c", &s->i2c, typename);
> >
> > + object_initialize_child(obj, "pcie-cfg", &s->pcie[0],
> TYPE_ASPEED_PCIE_CFG);
> > + object_initialize_child(obj, "pcie-phy[*]", &s->pcie_phy[0],
> > + TYPE_ASPEED_PCIE_PHY);
> > +
> > object_initialize_child(obj, "peci", &s->peci,
> > TYPE_ASPEED_PECI);
> >
> > snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
> > @@ -292,7 +300,9 @@ static void aspeed_soc_ast2600_realize(DeviceState
> *dev, Error **errp)
> > AspeedSoCState *s = ASPEED_SOC(dev);
> > AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
> > qemu_irq irq;
> > - g_autofree char *sram_name = NULL;
> > + g_autofree char *name = NULL;
> > + MemoryRegion *mmio_alias;
> > + MemoryRegion *mmio_mr;
> >
> > /* Default boot region (SPI memory or ROMs) */
> > memory_region_init(&s->spi_boot_container, OBJECT(s), @@ -362,8
> > +372,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error
> **errp)
> > }
> >
> > /* SRAM */
> > - sram_name = g_strdup_printf("aspeed.sram.%d",
> CPU(&a->cpu[0])->cpu_index);
> > - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name,
> sc->sram_size,
> > + name = g_strdup_printf("aspeed.sram.%d",
> CPU(&a->cpu[0])->cpu_index);
> > + if (!memory_region_init_ram(&s->sram, OBJECT(s), name,
> > + sc->sram_size,
> > errp)) {
> > return;
> > }
> > @@ -438,6 +448,59 @@ static void
> aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
> > sysbus_connect_irq(SYS_BUS_DEVICE(&s->peci), 0,
> > aspeed_soc_get_irq(s, ASPEED_DEV_PECI));
> >
> > + /*
> > + * PCIe Root Complex (RC)
> > + *
> > + * H2X register space (single block 0x00-0xFF):
> > + * 0x00-0x7F : shared by RC_L (PCIe0) and RC_H (PCIe1)
> > + * 0x80-0xBF : RC_L only
> > + * 0xC0-0xFF : RC_H only
> > + *
> > + * Model scope / limitations:
> > + * - Firmware supports RC_H only; this QEMU model does not
> support RC_L.
> > + * - RC_H uses PHY1 and the MMIO window [0x70000000,
> 0x80000000]
> > + * (aka MMIO1).
> > + *
> > + * Indexing convention (this model):
> > + * - Expose a single logical instance at index 0.
> > + * - pcie[0] -> hardware RC_H (PCIe1)
> > + * - phy[0] -> hardware PHY1
> > + * - mmio.0 -> guest address range MMIO1:
> 0x70000000-0x80000000
> > + * - RC_L / PCIe0 is not created and mapped.
> > + */
> > + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy[0]), errp)) {
> > + return;
> > + }
> > + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie_phy[0]), 0,
> > + sc->memmap[ASPEED_DEV_PCIE_PHY1]);
> > +
> > + object_property_set_int(OBJECT(&s->pcie[0]), "dram-base",
> > + sc->memmap[ASPEED_DEV_SDRAM],
> > + &error_abort);
> > + object_property_set_link(OBJECT(&s->pcie[0]), "dram",
> OBJECT(s->dram_mr),
> > + &error_abort);
> > + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie[0]), errp)) {
> > + return;
> > + }
> > + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->pcie[0]), 0,
> > + sc->memmap[ASPEED_DEV_PCIE0]);
> > +
> > + irq = qdev_get_gpio_in(DEVICE(&a->a7mpcore),
> > + sc->irqmap[ASPEED_DEV_PCIE0]);
> > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie[0].rc), 0, irq);
> > +
> > + name = g_strdup_printf("aspeed.pcie-mmio.0");
> > +
> > + mmio_alias = g_new0(MemoryRegion, 1);
> > + mmio_mr =
> sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pcie[0].rc),
> > + 1);
> > +
> > + memory_region_init_alias(mmio_alias, OBJECT(&s->pcie[0].rc), name,
> > + mmio_mr,
> sc->memmap[ASPEED_DEV_PCIE_MMIO1],
> > + 0x10000000);
> > + memory_region_add_subregion(s->memory,
> > +
> sc->memmap[ASPEED_DEV_PCIE_MMIO1],
> > + mmio_alias);
> > +
>
> Could you please introduce a sub routine to realize the PCIe RC model ?
> name it aspeed_soc_ast2600_pcie_realize() maybe.
>
Thanks for your review and suggestion.
Will do.
Jamin
>
> Thanks,
>
> C.
>
>
>
>
> > /* FMC, The number of CS is set at the board level */
> > object_property_set_link(OBJECT(&s->fmc), "dram",
> OBJECT(s->dram_mr),
> > &error_abort);
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model
2025-09-15 9:20 ` [SPAM] " Cédric Le Goater
@ 2025-09-16 5:32 ` Jamin Lin
0 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin @ 2025-09-16 5:32 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: Re: [SPAM] [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe
> PHY model
>
> Hello Jamin,
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > This patch introduces an initial ASPEED PCIe PHY/host controller model
> > to support the AST2600 SoC. It provides a simple register block with
> > MMIO read/write callbacks, integration into the build system, and
> > trace events for debugging.
> >
> > Key changes:
> >
> > 1. PCIe PHY MMIO read/write callbacks
> > Implemented aspeed_pcie_phy_read() and aspeed_pcie_phy_write() to
> > handle 32-bit register accesses.
> >
> > 2. Build system and Kconfig integration
> > Added CONFIG_PCI_EXPRESS_ASPEED in hw/pci-host/Kconfig and
> meson
> > rules.
> > Updated ASPEED_SOC in hw/arm/Kconfig to imply PCI_DEVICES and
> select
> > PCI_EXPRESS_ASPEED.
> >
> > 3. Trace events for debug
> > New tracepoints aspeed_pcie_phy_read and aspeed_pcie_phy_write
> allow
> > monitoring MMIO accesses.
> >
> > 4. Register space and defaults (AST2600 reference)
> > Expose a 0x100 register space, as documented in the AST2600
> datasheet.
> > On reset, set default values:
> > PEHR_ID: Vendor ID = ASPEED, Device ID = 0x1150
> > PEHR_CLASS_CODE = 0x06040006
> > PEHR_DATALINK = 0xD7040022
> > PEHR_LINK: bit[5] set to 1 to indicate link up.
> >
> > This provides a skeleton device for the AST2600 platform. It enables
> > firmware to detect the PCIe link as up by default and allows future
> > extension.
>
> There, you could say : based on previous work from me, plus add a summary of
> the modifications that have been made.
>
Thanks for your review and suggestion.
I will add the following messages in this commit log
This commit is the starting point of the series to introduce ASPEED PCIe
Root Complex (RC) support. Based on previous work from Cédric Le Goater,
the following commits in this series extend and refine the implementation:
- Add a PCIe Root Port so that devices can be attached without requiring an
extra bridge.
- Restrict the Root Port device instantiation to the AST2600 platform.
- Integrate aspeed_cfg_translate_write() to support both AST2600 and AST2700.
- Add MSI support and a preliminary RC IOMMU address space.
- Fix issues with MSI interrupt clearing.
- Extend support to the AST2700 SoC.
- Drop the AST2600 RC_L support.
- Introduce PCIe RC functional tests covering both AST2600 and AST2700.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> > include/hw/pci-host/aspeed_pcie.h | 44 +++++++++
> > hw/pci-host/aspeed_pcie.c | 159
> ++++++++++++++++++++++++++++++
> > hw/arm/Kconfig | 3 +
> > hw/pci-host/Kconfig | 4 +
> > hw/pci-host/meson.build | 1 +
> > hw/pci-host/trace-events | 4 +
> > 6 files changed, 215 insertions(+)
> > create mode 100644 include/hw/pci-host/aspeed_pcie.h
> > create mode 100644 hw/pci-host/aspeed_pcie.c
> >
> > diff --git a/include/hw/pci-host/aspeed_pcie.h
> > b/include/hw/pci-host/aspeed_pcie.h
> > new file mode 100644
> > index 0000000000..faf87073ab
> > --- /dev/null
> > +++ b/include/hw/pci-host/aspeed_pcie.h
> > @@ -0,0 +1,44 @@
> > +/*
> > + * ASPEED PCIe Host Controller
> > + *
> > + * Copyright (C) 2025 ASPEED Technology Inc.
> > + * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
> > + *
> > + * Jamin Lin <jamin_lin@aspeedtech.com>
>
> I would add an "Authors" section and add our two names under it.
>
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * This file is based on Cédric Le Goater's patch:
> > + * "pci: Add Aspeed host bridge (WIP)"
> > + *
> > +
> https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc1
> > + 89854f9d26df
>
> I don't think adding a reference on a github commit has much value since I
> could remove the branch, which I do regularly. So, I would remove it and
> simply keep the "Based on previous work from ... "
> boiler plate.
>
Thanks for your review and suggestion.
LICENSE HEADER will be updated as following.
/*
* ASPEED PCIe Host Controller
*
* Copyright (C) 2025 ASPEED Technology Inc.
* Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
*
* Authors:
* Cédric Le Goater <clg@kaod.org>
* Jamin Lin <jamin_lin@aspeedtech.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Based on previous work from Cédric Le Goater.
* Modifications extend support for the ASPEED AST2600 and AST2700 platforms.
*/
> The rest looks good.
>
> > + * Modifications have been made to support the ASPEED AST2600 and
> > + AST2700
> > + * platforms.
> > + */
> > +
> > +#ifndef ASPEED_PCIE_H
> > +#define ASPEED_PCIE_H
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/pci/pci_bridge.h"
> > +#include "hw/pci/pcie_host.h"
> > +#include "qom/object.h"
> > +
> > +#define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> > +OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass,
> > +ASPEED_PCIE_PHY);
> > +
> > +struct AspeedPCIEPhyState {
> > + SysBusDevice parent_obj;
> > +
> > + MemoryRegion mmio;
> > + uint32_t *regs;
> > + uint32_t id;
> > +};
> > +
> > +struct AspeedPCIEPhyClass {
> > + SysBusDeviceClass parent_class;
> > +
> > + uint64_t nr_regs;
> > +};
> > +
> > +#endif /* ASPEED_PCIE_H */
> > diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c new
> > file mode 100644 index 0000000000..5584449b17
> > --- /dev/null
> > +++ b/hw/pci-host/aspeed_pcie.c
> > @@ -0,0 +1,159 @@
> > +/*
> > + * ASPEED PCIe Host Controller
> > + *
> > + * Copyright (C) 2025 ASPEED Technology Inc.
> > + * Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
> > + *
> > + * Jamin Lin <jamin_lin@aspeedtech.com>
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + *
> > + * This file is based on Cédric Le Goater's patch:
> > + * "pci: Add Aspeed host bridge (WIP)"
> > + *
> >
> +https://github.com/legoater/qemu/commit/d1b97b0c7844219d847122410dc
> 18
> > +9854f9d26df
> > + *
> > + * Modifications have been made to support the ASPEED AST2600 and
> > +AST2700
> > + * platforms.
>
> Same comment
>
Thanks for your review and suggestion.
LICENSE HEADER will be updated as following
/*
* ASPEED PCIe Host Controller
*
* Copyright (C) 2025 ASPEED Technology Inc.
* Copyright (c) 2022 Cédric Le Goater <clg@kaod.org>
*
* Authors:
* Cédric Le Goater <clg@kaod.org>
* Jamin Lin <jamin_lin@aspeedtech.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Based on previous work from Cédric Le Goater.
* Modifications extend support for the ASPEED AST2600 and AST2700 platforms.
*/
Thanks-Jamin
> The rest looks good.
>
> Thanks,
>
> C.
>
>
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu/log.h"
> > +#include "qapi/error.h"
> > +#include "hw/qdev-properties.h"
> > +#include "hw/registerfields.h"
> > +#include "hw/irq.h"
> > +#include "hw/pci/pci_host.h"
> > +#include "hw/pci-host/aspeed_pcie.h"
> > +#include "hw/pci/msi.h"
> > +#include "trace.h"
> > +
> > +/*
> > + * PCIe PHY
> > + *
> > + * PCIe Host Controller (PCIEH)
> > + */
> > +
> > +/* AST2600 */
> > +REG32(PEHR_ID, 0x00)
> > + FIELD(PEHR_ID, DEV, 16, 16)
> > +REG32(PEHR_CLASS_CODE, 0x04)
> > +REG32(PEHR_DATALINK, 0x10)
> > +REG32(PEHR_PROTECT, 0x7C)
> > + FIELD(PEHR_PROTECT, LOCK, 0, 8)
> > +REG32(PEHR_LINK, 0xC0)
> > + FIELD(PEHR_LINK, STS, 5, 1)
> > +
> > +#define ASPEED_PCIE_PHY_UNLOCK 0xA8
> > +
> > +static uint64_t aspeed_pcie_phy_read(void *opaque, hwaddr addr,
> > + unsigned int size) {
> > + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
> > + uint32_t reg = addr >> 2;
> > + uint32_t value = 0;
> > +
> > + value = s->regs[reg];
> > +
> > + trace_aspeed_pcie_phy_read(s->id, addr, value);
> > +
> > + return value;
> > +}
> > +
> > +static void aspeed_pcie_phy_write(void *opaque, hwaddr addr, uint64_t
> data,
> > + unsigned int size) {
> > + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(opaque);
> > + uint32_t reg = addr >> 2;
> > +
> > + trace_aspeed_pcie_phy_write(s->id, addr, data);
> > +
> > + switch (reg) {
> > + case R_PEHR_PROTECT:
> > + data &= R_PEHR_PROTECT_LOCK_MASK;
> > + s->regs[reg] = !!(data == ASPEED_PCIE_PHY_UNLOCK);
> > + break;
> > + default:
> > + s->regs[reg] = data;
> > + break;
> > + }
> > +}
> > +
> > +static const MemoryRegionOps aspeed_pcie_phy_ops = {
> > + .read = aspeed_pcie_phy_read,
> > + .write = aspeed_pcie_phy_write,
> > + .endianness = DEVICE_LITTLE_ENDIAN,
> > + .valid = {
> > + .min_access_size = 1,
> > + .max_access_size = 4,
> > + },
> > +};
> > +
> > +static void aspeed_pcie_phy_reset(DeviceState *dev) {
> > + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
> > + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
> > +
> > + memset(s->regs, 0, apc->nr_regs << 2);
> > +
> > + s->regs[R_PEHR_ID] =
> > + (0x1150 << R_PEHR_ID_DEV_SHIFT) | PCI_VENDOR_ID_ASPEED;
> > + s->regs[R_PEHR_CLASS_CODE] = 0x06040006;
> > + s->regs[R_PEHR_DATALINK] = 0xD7040022;
> > + s->regs[R_PEHR_LINK] = R_PEHR_LINK_STS_MASK; }
> > +
> > +static void aspeed_pcie_phy_realize(DeviceState *dev, Error **errp) {
> > + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
> > + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_GET_CLASS(s);
> > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> > + g_autofree char *name;
> > +
> > + s->regs = g_new(uint32_t, apc->nr_regs);
> > + name = g_strdup_printf(TYPE_ASPEED_PCIE_PHY ".regs.%d", s->id);
> > + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_pcie_phy_ops,
> s, name,
> > + apc->nr_regs << 2);
> > + sysbus_init_mmio(sbd, &s->mmio);
> > +}
> > +
> > +static void aspeed_pcie_phy_unrealize(DeviceState *dev) {
> > + AspeedPCIEPhyState *s = ASPEED_PCIE_PHY(dev);
> > +
> > + g_free(s->regs);
> > + s->regs = NULL;
> > +}
> > +
> > +static const Property aspeed_pcie_phy_props[] = {
> > + DEFINE_PROP_UINT32("id", AspeedPCIEPhyState, id, 0), };
> > +
> > +static void aspeed_pcie_phy_class_init(ObjectClass *klass, const void
> > +*data) {
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > + AspeedPCIEPhyClass *apc = ASPEED_PCIE_PHY_CLASS(klass);
> > +
> > + dc->desc = "ASPEED PCIe Phy";
> > + dc->realize = aspeed_pcie_phy_realize;
> > + dc->unrealize = aspeed_pcie_phy_unrealize;
> > + device_class_set_legacy_reset(dc, aspeed_pcie_phy_reset);
> > + device_class_set_props(dc, aspeed_pcie_phy_props);
> > +
> > + apc->nr_regs = 0x100 >> 2;
> > +}
> > +
> > +static const TypeInfo aspeed_pcie_phy_info = {
> > + .name = TYPE_ASPEED_PCIE_PHY,
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_size = sizeof(AspeedPCIEPhyState),
> > + .class_init = aspeed_pcie_phy_class_init,
> > + .class_size = sizeof(AspeedPCIEPhyClass), };
> > +
> > +static void aspeed_pcie_register_types(void) {
> > + type_register_static(&aspeed_pcie_phy_info);
> > +}
> > +
> > +type_init(aspeed_pcie_register_types);
> > +
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index
> > 2aa4b5d778..83d2676f6c 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -544,6 +544,7 @@ config ASPEED_SOC
> > bool
> > default y
> > depends on TCG && ARM
> > + imply PCI_DEVICES
> > select DS1338
> > select FTGMAC100
> > select I2C
> > @@ -564,6 +565,8 @@ config ASPEED_SOC
> > select MAX31785
> > select FSI_APB2OPB_ASPEED
> > select AT24C
> > + select PCI_EXPRESS
> > + select PCI_EXPRESS_ASPEED
> >
> > config MPS2
> > bool
> > diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index
> > 9824fa188d..8cbb8304a3 100644
> > --- a/hw/pci-host/Kconfig
> > +++ b/hw/pci-host/Kconfig
> > @@ -46,6 +46,10 @@ config PCI_I440FX
> > select PCI
> > select PAM
> >
> > +config PCI_EXPRESS_ASPEED
> > + bool
> > + select PCI_EXPRESS
> > +
> > config PCI_EXPRESS_Q35
> > bool
> > select PCI_EXPRESS
> > diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index
> > 937a0f72ac..86b754d0b0 100644
> > --- a/hw/pci-host/meson.build
> > +++ b/hw/pci-host/meson.build
> > @@ -2,6 +2,7 @@ pci_ss = ss.source_set()
> > pci_ss.add(when: 'CONFIG_PAM', if_true: files('pam.c'))
> > pci_ss.add(when: 'CONFIG_PCI_BONITO', if_true: files('bonito.c'))
> > pci_ss.add(when: 'CONFIG_GT64120', if_true: files('gt64120.c'))
> > +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_ASPEED', if_true:
> > +files('aspeed_pcie.c'))
> > pci_ss.add(when: 'CONFIG_PCI_EXPRESS_DESIGNWARE', if_true:
> files('designware.c'))
> > pci_ss.add(when: 'CONFIG_PCI_EXPRESS_GENERIC_BRIDGE', if_true:
> files('gpex.c'))
> > pci_ss.add(when: ['CONFIG_PCI_EXPRESS_GENERIC_BRIDGE',
> > 'CONFIG_ACPI'], if_true: files('gpex-acpi.c')) diff --git
> > a/hw/pci-host/trace-events b/hw/pci-host/trace-events index
> > 0a816b9aa1..3438516756 100644
> > --- a/hw/pci-host/trace-events
> > +++ b/hw/pci-host/trace-events
> > @@ -1,5 +1,9 @@
> > # See docs/devel/tracing.rst for syntax documentation.
> >
> > +# aspeed_pcie.c
> > +aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d:
> > +addr 0x%" PRIx64 " value 0x%" PRIx32 aspeed_pcie_phy_write(uint32_t
> > +id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%"
> > +PRIx32
> > +
> > # bonito.c
> > bonito_spciconf_small_access(uint64_t addr, unsigned size) "PCI config
> address is smaller then 32-bit, addr: 0x%"PRIx64", size: %u"
> >
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
2025-09-15 17:21 ` [SPAM] " Cédric Le Goater
@ 2025-09-16 7:30 ` Jamin Lin
2025-09-16 7:41 ` Cédric Le Goater
0 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin @ 2025-09-16 7:30 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > Extend the AST2600 functional tests with a PCIe enumeration check.
> >
> > This patch introduces a new helper "do_ast2600_pcie_test()" that runs
> > "lspci" on the emulated system and verifies the presence of the
> > expected PCIe devices:
> >
> > - 80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600
> > - 80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI
> > Bridge
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> > tests/functional/arm/test_aspeed_ast2600.py | 10 ++++++++++
> > 1 file changed, 10 insertions(+)
> >
> > diff --git a/tests/functional/arm/test_aspeed_ast2600.py
> > b/tests/functional/arm/test_aspeed_ast2600.py
> > index 129695ca4e..f97fbbaa11 100755
> > --- a/tests/functional/arm/test_aspeed_ast2600.py
> > +++ b/tests/functional/arm/test_aspeed_ast2600.py
> > @@ -101,6 +101,14 @@ def test_arm_ast2600_evb_buildroot_tpm(self):
> >
> 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast
> 2600-default-obmc.tar.gz',
> >
> >
> 'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba'
> )
> >
> > + def do_ast2600_pcie_test(self):
> > + exec_command_and_wait_for_pattern(self,
> > + 'lspci -s 80:00.0',
> > + '80:00.0 Host bridge: ASPEED Technology, Inc. Device 2600')
> > + exec_command_and_wait_for_pattern(self,
> > + 'lspci -s 80:08.0',
> > + '80:08.0 PCI bridge: ASPEED Technology, Inc. AST1150
> > + PCI-to-PCI Bridge')
> > +
>
> I would add '-device e1000e,netdev=net1,bus=pcie.0 -netdev user,id=net1' and
> check that an IP address is assigned to the interface.
>
>
Thanks for your review and suggestion.
I will add a test for the e1000e device on AST2600.
I also have a question regarding AST2700:
1. SDK v09.06 does not include a built-in e1000e driver.
2. AST2700 A0 does not support PCIe RC.
Given these limitations, if we want to test e1000e on AST2700, can I only add a test case for the AST2700 vbootrom?
1. The AST2700 FC SDK is still on v09.06 and cannot be upgraded to v09.07 until the following patch series is applied:
https://patchwork.kernel.org/project/qemu-devel/cover/20250717034054.1903991-1-jamin_lin@aspeedtech.com/
2. The manual device loader cannot be updated to SDK v09.07 because of this commit
https://github.com/AspeedTech-BMC/openbmc/commit/17f7a259f3a4d052b9ea93353a3400040f39992e
Starting from SDK v09.07, u-boot-nodtb.bin is no longer shipped.
If we want to support it, we can generate it manually with the following commands:
dumpimage -T flat_dt -p 0 -o u-boot-nodtb.bin u-boot.bin
dumpimage -T flat_dt -p 1 -o u-boot.dtb u-boot.bin
However, I am not sure how to install the dumpimage tool in the test environment.
Could you please give me any suggestion?
Thanks-Jamin
> Thanks,
>
> C.
>
>
>
> > def test_arm_ast2600_evb_sdk(self):
> > self.set_machine('ast2600-evb')
> >
> > @@ -136,5 +144,7 @@ def test_arm_ast2600_evb_sdk(self):
> > exec_command_and_wait_for_pattern(self,
> > '/sbin/hwclock -f /dev/rtc1', year)
> >
> > + self.do_ast2600_pcie_test()
> > +
> > if __name__ == '__main__':
> > AspeedTest.main()
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
2025-09-16 7:30 ` Jamin Lin
@ 2025-09-16 7:41 ` Cédric Le Goater
2025-09-16 7:44 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-16 7:41 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi,
>> I would add '-device e1000e,netdev=net1,bus=pcie.0 -netdev user,id=net1' and
>> check that an IP address is assigned to the interface.
>>
>>
> Thanks for your review and suggestion.
> I will add a test for the e1000e device on AST2600.
>
> I also have a question regarding AST2700:
>
> 1. SDK v09.06 does not include a built-in e1000e driver.
We have switched to v09.07 now.
> 2. AST2700 A0 does not support PCIe RC.
>
> Given these limitations, if we want to test e1000e on AST2700, can I only add a test case for the AST2700 vbootrom?
I think we can limit the test to AST2700 A1 since A0 should be removed.
> 1. The AST2700 FC SDK is still on v09.06 and cannot be upgraded to v09.07 until the following patch series is applied:
> https://patchwork.kernel.org/project/qemu-devel/cover/20250717034054.1903991-1-jamin_lin@aspeedtech.com/
v09.07 is merged already and the series above needs a large rework
of the coprocessor models.
> 2. The manual device loader cannot be updated to SDK v09.07 because of this commit
> https://github.com/AspeedTech-BMC/openbmc/commit/17f7a259f3a4d052b9ea93353a3400040f39992e
>
> Starting from SDK v09.07, u-boot-nodtb.bin is no longer shipped.
> If we want to support it, we can generate it manually with the following commands:
>
> dumpimage -T flat_dt -p 0 -o u-boot-nodtb.bin u-boot.bin
> dumpimage -T flat_dt -p 1 -o u-boot.dtb u-boot.bin
>
> However, I am not sure how to install the dumpimage tool in the test environment.
> Could you please give me any suggestion?
I am not sure I am following the constraints/requirements :/
As of today, ast2600-evb looks ok to me. May be work on that first.
Thanks,
C.
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
2025-09-16 7:41 ` Cédric Le Goater
@ 2025-09-16 7:44 ` Jamin Lin
0 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin @ 2025-09-16 7:44 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test
>
> Hi,
>
> >> I would add '-device e1000e,netdev=net1,bus=pcie.0 -netdev
> >> user,id=net1' and check that an IP address is assigned to the interface.
> >>
> >>
> > Thanks for your review and suggestion.
> > I will add a test for the e1000e device on AST2600.
> >
> > I also have a question regarding AST2700:
> >
> > 1. SDK v09.06 does not include a built-in e1000e driver.
>
> We have switched to v09.07 now.
>
> > 2. AST2700 A0 does not support PCIe RC.
> >
> > Given these limitations, if we want to test e1000e on AST2700, can I only add
> a test case for the AST2700 vbootrom?
>
> I think we can limit the test to AST2700 A1 since A0 should be removed.
>
> > 1. The AST2700 FC SDK is still on v09.06 and cannot be upgraded to v09.07
> until the following patch series is applied:
> > https://patchwork.kernel.org/project/qemu-devel/cover/20250717034054.1
> > 903991-1-jamin_lin@aspeedtech.com/
>
> v09.07 is merged already and the series above needs a large rework of the
> coprocessor models.
>
> > 2. The manual device loader cannot be updated to SDK v09.07 because of
> > this commit
> >
> https://github.com/AspeedTech-BMC/openbmc/commit/17f7a259f3a4d052b9
> ea9
> > 3353a3400040f39992e
> >
> > Starting from SDK v09.07, u-boot-nodtb.bin is no longer shipped.
> > If we want to support it, we can generate it manually with the following
> commands:
> >
> > dumpimage -T flat_dt -p 0 -o u-boot-nodtb.bin u-boot.bin dumpimage -T
> > flat_dt -p 1 -o u-boot.dtb u-boot.bin
> >
> > However, I am not sure how to install the dumpimage tool in the test
> environment.
> > Could you please give me any suggestion?
>
> I am not sure I am following the constraints/requirements :/
>
> As of today, ast2600-evb looks ok to me. May be work on that first.
>
Thanks for the suggestions.
Will resend v3
Jamin
>
> Thanks,
>
> C.
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge
2025-09-15 16:51 ` [SPAM] " Cédric Le Goater
@ 2025-09-17 1:45 ` Jamin Lin
2025-09-17 9:05 ` Jamin Lin
0 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin @ 2025-09-17 1:45 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: Re: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe
> config space and host bridge
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > Introduce PCIe config and host bridge model for the AST2600 platform.
> >
> > This patch adds support for the H2X (AHB to PCIe Bus Bridge)
> > controller with a 0x100 byte register space. The register layout is
> > shared between two root complexes: 0x00–0x7f is common, 0x80–0xbf for
> > RC_L, and 0xc0–0xff for RC_H. Only RC_H is modeled in this
> implementation.
> >
> > The RC_H bus uses bus numbers in the 0x80–0xff range instead of the
> > standard root bus 0x00. To allow the PCI subsystem to discover
> > devices, the host bridge logic remaps the root bus number back to 0x00
> > whenever the configured bus number matches the "bus-nr" property.
> >
> > New MMIO callbacks are added for the H2X config space:
> > - aspeed_pcie_cfg_read() and aspeed_pcie_cfg_write() handle register
> > accesses.
> > - aspeed_pcie_cfg_readwrite() provides configuration read/write support.
> > - aspeed_pcie_cfg_translate_write() handles PCIe byte-enable semantics for
> > write operations.
> >
> > The reset handler initializes the H2X register block with default
> > values as defined in the AST2600 datasheet.
> >
> > Additional changes:
> > - Implement ASPEED PCIe root complex (TYPE_ASPEED_PCIE_RC).
> > - Wire up interrupt propagation via aspeed_pcie_rc_set_irq().
> > - Add tracepoints for config read/write and INTx handling.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > ---
> > include/hw/pci-host/aspeed_pcie.h | 58 ++++
> > hw/pci-host/aspeed_pcie.c | 422
> ++++++++++++++++++++++++++++++
> > hw/pci-host/trace-events | 4 +
> > 3 files changed, 484 insertions(+)
> >
> > diff --git a/include/hw/pci-host/aspeed_pcie.h
> > b/include/hw/pci-host/aspeed_pcie.h
> > index faf87073ab..e2c5dc6f62 100644
> > --- a/include/hw/pci-host/aspeed_pcie.h
> > +++ b/include/hw/pci-host/aspeed_pcie.h
> > @@ -24,6 +24,64 @@
> > #include "hw/pci/pcie_host.h"
> > #include "qom/object.h"
> >
> > +typedef struct AspeedPCIECfgTxDesc {
> > + uint32_t desc0;
> > + uint32_t desc1;
> > + uint32_t desc2;
> > + uint32_t desc3;
> > + uint32_t wdata;
> > + uint32_t rdata_reg;
> > +} AspeedPCIECfgTxDesc;
> > +
> > +typedef struct AspeedPCIERcRegs {
> > + uint32_t int_en_reg;
> > + uint32_t int_sts_reg;
> > +} AspeedPCIERcRegs;
> > +
> > +typedef struct AspeedPCIERegMap {
> > + AspeedPCIERcRegs rc;
> > +} AspeedPCIERegMap;
> > +
> > +#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
> > +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
> > +
> > +struct AspeedPCIERcState {
> > + PCIExpressHost parent_obj;
> > +
> > + MemoryRegion mmio_window;
> > + MemoryRegion io_window;
> > + MemoryRegion mmio;
> > + MemoryRegion io;
> > +
> > + uint32_t bus_nr;
> > + char name[16];
> > + qemu_irq irq;
> > +};
> > +
> > +/* Bridge between AHB bus and PCIe RC. */ #define
> > +TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
> > +OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass,
> > +ASPEED_PCIE_CFG);
> > +
> > +struct AspeedPCIECfgState {
> > + SysBusDevice parent_obj;
> > +
> > + MemoryRegion mmio;
> > + uint32_t *regs;
> > + uint32_t id;
> > +
> > + AspeedPCIERcState rc;
> > +};
> > +
> > +struct AspeedPCIECfgClass {
> > + SysBusDeviceClass parent_class;
> > +
> > + const AspeedPCIERegMap *reg_map;
> > + const MemoryRegionOps *reg_ops;
> > +
> > + uint64_t rc_bus_nr;
> > + uint64_t nr_regs;
> > +};
> > +
> > #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> > OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass,
> > ASPEED_PCIE_PHY);
> >
> > diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> > index 5584449b17..9fb7c1ef67 100644
> > --- a/hw/pci-host/aspeed_pcie.c
> > +++ b/hw/pci-host/aspeed_pcie.c
> > @@ -27,6 +27,426 @@
> > #include "hw/pci/msi.h"
> > #include "trace.h"
> >
> > +/*
> > + * PCIe Root Complex (RC)
> > + */
> > +
> > +static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int level)
> > +{
> > + AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
> > + AspeedPCIECfgState *cfg =
> > + container_of(rc, AspeedPCIECfgState, rc);
> > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
> > + const AspeedPCIERcRegs *rc_regs;
>
> I suggest you cache &apc->reg_map->rc under AspeedPCIECfgState as an
> attribute (at realize time). This will ease reading the code and improve
> performance.
>
Thanks for your review and suggestion.
Will do.
> > + bool intx;
> > +
> > + assert(irq < PCI_NUM_PINS);
> > +
> > + rc_regs = &apc->reg_map->rc;
> > +
> > + if (level) {
> > + cfg->regs[rc_regs->int_sts_reg] |= BIT(irq);
> > + } else {
> > + cfg->regs[rc_regs->int_sts_reg] &= ~BIT(irq);
> > + }
> > +
> > + intx = !!(cfg->regs[rc_regs->int_sts_reg] &
> cfg->regs[rc_regs->int_en_reg]);
> > + trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx);
> > + qemu_set_irq(rc->irq, intx);
> > +}
> > +
> > +static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num) {
> > + return irq_num % PCI_NUM_PINS;
> > +}
> > +
> > +static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) {
> > + PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
> > + AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev);
> > + AspeedPCIECfgState *cfg =
> > + container_of(rc, AspeedPCIECfgState, rc);
> > + PCIHostState *pci = PCI_HOST_BRIDGE(dev);
> > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> > + g_autofree char *name;
> > +
> > + /* PCI configuration space */
> > + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
> > + sysbus_init_mmio(sbd, &pex->mmio);
> > +
> > + /* MMIO and IO region */
> > + memory_region_init(&rc->mmio, OBJECT(rc), "mmio", UINT64_MAX);
> > + memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000);
> > +
> > + name = g_strdup_printf("pcie.%d.mmio_window", cfg->id);
> > + memory_region_init_io(&rc->mmio_window, OBJECT(rc),
> &unassigned_io_ops,
> > + OBJECT(rc), name, UINT64_MAX);
> > + name = g_strdup_printf("pcie.%d.ioport_window", cfg->id);
> > + memory_region_init_io(&rc->io_window, OBJECT(rc),
> &unassigned_io_ops,
> > + OBJECT(rc), name, 0x10000);
> > +
> > + memory_region_add_subregion(&rc->mmio_window, 0, &rc->mmio);
> > + memory_region_add_subregion(&rc->io_window, 0, &rc->io);
> > + sysbus_init_mmio(sbd, &rc->mmio_window);
> > + sysbus_init_mmio(sbd, &rc->io_window);
> > +
> > + sysbus_init_irq(sbd, &rc->irq);
> > + name = g_strdup_printf("pcie.rc%d", cfg->id);
> > + pci->bus = pci_register_root_bus(dev, name, aspeed_pcie_rc_set_irq,
> > + aspeed_pcie_rc_map_irq, rc,
> &rc->mmio,
> > + &rc->io, 0, 4,
> TYPE_PCIE_BUS);
> > + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; }
> > +
> > +static const char *aspeed_pcie_rc_root_bus_path(PCIHostState
> *host_bridge,
> > + PCIBus *rootbus)
> {
> > + AspeedPCIERcState *rc = ASPEED_PCIE_RC(host_bridge);
> > + AspeedPCIECfgState *cfg =
> > + container_of(rc, AspeedPCIECfgState, rc);
> > +
> > + snprintf(rc->name, sizeof(rc->name), "%04x:%02x", cfg->id,
> > + rc->bus_nr);
> > +
> > + return rc->name;
> > +}
> > +
> > +static const Property aspeed_pcie_rc_props[] = {
> > + DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), };
> > +
> > +static void aspeed_pcie_rc_class_init(ObjectClass *klass, const void
> > +*data) {
> > + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > + dc->desc = "ASPEED PCIe RC";
> > + dc->realize = aspeed_pcie_rc_realize;
> > + dc->fw_name = "pci";
> > + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> > +
> > + hc->root_bus_path = aspeed_pcie_rc_root_bus_path;
> > + device_class_set_props(dc, aspeed_pcie_rc_props);
> > +
> > + msi_nonbroken = true;
> > +}
> > +
> > +static const TypeInfo aspeed_pcie_rc_info = {
> > + .name = TYPE_ASPEED_PCIE_RC,
> > + .parent = TYPE_PCIE_HOST_BRIDGE,
> > + .instance_size = sizeof(AspeedPCIERcState),
> > + .class_init = aspeed_pcie_rc_class_init, };
> > +
> > +/*
> > + * PCIe Config
> > + *
> > + * AHB to PCIe Bus Bridge (H2X)
> > + *
> > + * On the AST2600:
> > + * NOTE: rc_l is not supported by this model.
> > + * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
> > + * - Registers 0x80 - 0xBF are specific to PCIe0.
> > + * - Registers 0xC0 - 0xFF are specific to PCIe1.
> > + */
> > +
> > +/* AST2600 */
> > +REG32(H2X_CTRL, 0x00)
> > + FIELD(H2X_CTRL, CLEAR_RX, 4, 1)
> > +REG32(H2X_TX_CLEAR, 0x08)
> > + FIELD(H2X_TX_CLEAR, IDLE, 0, 1)
> > +REG32(H2X_RDATA, 0x0C)
> > +REG32(H2X_TX_DESC0, 0x10)
> > +REG32(H2X_TX_DESC1, 0x14)
> > +REG32(H2X_TX_DESC2, 0x18)
> > +REG32(H2X_TX_DESC3, 0x1C)
> > +REG32(H2X_TX_DATA, 0x20)
> > +REG32(H2X_TX_STS, 0x24)
> > + FIELD(H2X_TX_STS, IDLE, 31, 1)
> > + FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1)
> > + FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1)
> > + FIELD(H2X_TX_STS, TRIG, 0, 1)
> > +REG32(H2X_RC_H_CTRL, 0xC0)
> > +REG32(H2X_RC_H_INT_EN, 0xC4)
> > +REG32(H2X_RC_H_INT_STS, 0xC8)
> > + SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
> > + SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
> > +REG32(H2X_RC_H_RDATA, 0xCC)
> > +
> > +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
> > +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
> > +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
> > +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
> > +
> > +#define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff) #define
> > +PCIE_CFG_BYTE_EN(x) ((x) & 0xf)
> > +
> > +static const AspeedPCIERegMap aspeed_regmap = {
> > + .rc = {
> > + .int_en_reg = R_H2X_RC_H_INT_EN,
> > + .int_sts_reg = R_H2X_RC_H_INT_STS,
> > + },
> > +};
> > +
> > +static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
> > + unsigned int size) {
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> > + uint32_t reg = addr >> 2;
> > + uint32_t value = 0;
> > +
> > + value = s->regs[reg];
> > +
> > + trace_aspeed_pcie_cfg_read(s->id, addr, value);
> > +
> > + return value;
> > +}
> > +
> > +static void aspeed_pcie_cfg_translate_write(uint8_t byte_en, uint32_t
> *addr,
> > + uint64_t *val, int
> *len)
> > +{
> > + uint64_t packed_val = 0;
> > + int first_bit = -1;
> > + int index = 0;
> > + int i;
> > +
> > + *len = ctpop8(byte_en);
> > +
> > + if (*len == 0 || *len > 4) {
> > + goto err;
> > + }
> > +
> > + /* Special case: full 4-byte write must be 4-byte aligned */
> > + if (byte_en == 0x0f) {
> > + if (*addr % 4 != 0) {
>
> This is an aligment issue to be reported as such and not with "invalid byte
> enable"
>
> I think you should remove the "goto err" and generate a LOG_GUEST_ERROR
> instead.
>
Will fix it.
> > + goto err;
> > + }
> > + *val = *val & 0xffffffff;
> > + return;
> > + }
> > +
> > + for (i = 0; i < 4; i++) {
> > + if (byte_en & (1 << i)) {
> > + if (first_bit < 0) {
> > + first_bit = i;
> > + }
> > + packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8);
> > + index++;> + }
> > + }
> > +
> > + *addr += first_bit;
> > + *val = packed_val;
> > +
> > + return;
> > +
> > +err:
> > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable:
> 0x%x\n",
> > + __func__, byte_en); }
> > +
> > +static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s,
> > + const AspeedPCIECfgTxDesc
> > +*desc) {
> > + AspeedPCIERcState *rc = &s->rc;
> > + PCIHostState *pci;
> > + uint32_t cfg_addr;
> > + PCIDevice *pdev;
> > + uint32_t offset;
> > + uint8_t byte_en;
> > + bool is_write;
> > + uint8_t devfn;
> > + uint64_t val;
> > + uint8_t bus;
> > + int len;
> > +
> > + val = ~0;
> > + is_write = !!(desc->desc0 & BIT(30));
> > + cfg_addr = desc->desc2;
>
>
> hmm, what about endianess ?
>
I am only considering little endianness the same as target side.
Will fix them in v3.
Jamin
> > + bus = (cfg_addr >> 24) & 0xff;
> > + devfn = (cfg_addr >> 16) & 0xff;
> > + offset = cfg_addr & 0xffc;
> > +
> > + pci = PCI_HOST_BRIDGE(rc);
> > +
> > + /*
> > + * On the AST2600, the RC_H bus number ranges from 0x80 to 0xFF,
> and its
> > + * root port uses bus number 0x80 instead of the standard 0x00. To
> locate
> > + * the device at root port 0, remap bus number 0x80 to 0x00 so that
> the
> > + * PCI subsystem can correctly discover the devices.
> > + */
> > + if (bus == rc->bus_nr) {
> > + bus = 0;
> > + }
> > +
> > + pdev = pci_find_device(pci->bus, bus, devfn);
> > + if (!pdev) {
> > + s->regs[desc->rdata_reg] = ~0;
> > + goto out;
> > + }
> > +
> > + switch (PCIE_CFG_FMTTYPE_MASK(desc->desc0)) {
> > + case TLP_FMTTYPE_CFGWR0:
> > + case TLP_FMTTYPE_CFGWR1:
> > + byte_en = PCIE_CFG_BYTE_EN(desc->desc1);
> > + val = desc->wdata;
>
> what about endianess ?
>
> > + aspeed_pcie_cfg_translate_write(byte_en, &offset, &val, &len);
> > + pci_host_config_write_common(pdev, offset,
> pci_config_size(pdev),
> > + val, len);
> > + break;
> > + case TLP_FMTTYPE_CFGRD0:
> > + case TLP_FMTTYPE_CFGRD1:
> > + val = pci_host_config_read_common(pdev, offset,
> > + pci_config_size(pdev),
> 4);
> > + s->regs[desc->rdata_reg] = val;
> > + break;
> > + default:
> > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type.
> DESC0=0x%x\n",
> > + __func__, desc->desc0);
> > + }
> > +
> > +out:
> > + trace_aspeed_pcie_cfg_rw(s->id, is_write ? "write" : "read", bus,
> devfn,
> > + cfg_addr, val); }
> > +
> > +static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr, uint64_t
> data,
> > + unsigned int size) {
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> > + AspeedPCIECfgTxDesc desc;
> > + uint32_t reg = addr >> 2;
> > + uint32_t rc_reg;
> > +
> > + trace_aspeed_pcie_cfg_write(s->id, addr, data);
> > +
> > + switch (reg) {
> > + case R_H2X_CTRL:
> > + if (data & R_H2X_CTRL_CLEAR_RX_MASK) {
> > + s->regs[R_H2X_RDATA] = ~0;
> > + }
> > + break;
> > + case R_H2X_TX_CLEAR:
> > + if (data & R_H2X_TX_CLEAR_IDLE_MASK) {
> > + s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_IDLE_MASK;
> > + }
> > + break;
> > + case R_H2X_TX_STS:
> > + if (data & R_H2X_TX_STS_TRIG_MASK) {
> > + desc.desc0 = s->regs[R_H2X_TX_DESC0];
> > + desc.desc1 = s->regs[R_H2X_TX_DESC1];
> > + desc.desc2 = s->regs[R_H2X_TX_DESC2];
> > + desc.desc3 = s->regs[R_H2X_TX_DESC3];
> > + desc.wdata = s->regs[R_H2X_TX_DATA];
> > + desc.rdata_reg = R_H2X_RC_H_RDATA;
>
> endianess.
>
>
> Thanks,
>
> C.
>
>
>
> > + aspeed_pcie_cfg_readwrite(s, &desc);
> > + rc_reg = apc->reg_map->rc.int_sts_reg;
> > + s->regs[rc_reg] |= H2X_RC_INT_INTDONE_MASK;
> > + s->regs[R_H2X_TX_STS] |=
> > + BIT(R_H2X_TX_STS_RC_H_TX_COMP_SHIFT);
> > + s->regs[R_H2X_TX_STS] |= R_H2X_TX_STS_IDLE_MASK;
> > + }
> > + break;
> > + /* preserve INTx status */
> > + case R_H2X_RC_H_INT_STS:
> > + if (data & H2X_RC_INT_INTDONE_MASK) {
> > + s->regs[R_H2X_TX_STS] &=
> ~R_H2X_TX_STS_RC_H_TX_COMP_MASK;
> > + }
> > + s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK;
> > + break;
> > + default:
> > + s->regs[reg] = data;
> > + break;
> > + }
> > +}
> > +
> > +static const MemoryRegionOps aspeed_pcie_cfg_ops = {
> > + .read = aspeed_pcie_cfg_read,
> > + .write = aspeed_pcie_cfg_write,
> > + .endianness = DEVICE_LITTLE_ENDIAN,
> > + .valid = {
> > + .min_access_size = 1,
> > + .max_access_size = 4,
> > + },
> > +};
> > +
> > +static void aspeed_pcie_cfg_instance_init(Object *obj) {
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj);
> > +
> > + object_initialize_child(obj, "rc", &s->rc, TYPE_ASPEED_PCIE_RC);
> > +
> > + return;
> > +}
> > +
> > +static void aspeed_pcie_cfg_reset(DeviceState *dev) {
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> > +
> > + memset(s->regs, 0, apc->nr_regs << 2); }
> > +
> > +static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) {
> > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> > + g_autofree char *name;
> > +
> > + s->regs = g_new(uint32_t, apc->nr_regs);
> > + name = g_strdup_printf(TYPE_ASPEED_PCIE_CFG ".regs.%d", s->id);
> > + memory_region_init_io(&s->mmio, OBJECT(s), apc->reg_ops, s, name,
> > + apc->nr_regs << 2);
> > + sysbus_init_mmio(sbd, &s->mmio);
> > +
> > + object_property_set_int(OBJECT(&s->rc), "bus-nr",
> > + apc->rc_bus_nr,
> > + &error_abort);
> > + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> > + return;
> > + }
> > +}
> > +
> > +static void aspeed_pcie_cfg_unrealize(DeviceState *dev) {
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> > +
> > + g_free(s->regs);
> > + s->regs = NULL;
> > +}
> > +
> > +static const Property aspeed_pcie_cfg_props[] = {
> > + DEFINE_PROP_UINT32("id", AspeedPCIECfgState, id, 0), };
> > +
> > +static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const void
> > +*data) {
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
> > +
> > + dc->desc = "ASPEED PCIe Config";
> > + dc->realize = aspeed_pcie_cfg_realize;
> > + dc->unrealize = aspeed_pcie_cfg_unrealize;
> > + device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset);
> > + device_class_set_props(dc, aspeed_pcie_cfg_props);
> > +
> > + apc->reg_ops = &aspeed_pcie_cfg_ops;
> > + apc->reg_map = &aspeed_regmap;
> > + apc->nr_regs = 0x100 >> 2;
> > + apc->rc_bus_nr = 0x80;
> > +}
> > +
> > +static const TypeInfo aspeed_pcie_cfg_info = {
> > + .name = TYPE_ASPEED_PCIE_CFG,
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_init = aspeed_pcie_cfg_instance_init,
> > + .instance_size = sizeof(AspeedPCIECfgState),
> > + .class_init = aspeed_pcie_cfg_class_init,
> > + .class_size = sizeof(AspeedPCIECfgClass), };
> > +
> > /*
> > * PCIe PHY
> > *
> > @@ -152,6 +572,8 @@ static const TypeInfo aspeed_pcie_phy_info = {
> >
> > static void aspeed_pcie_register_types(void)
> > {
> > + type_register_static(&aspeed_pcie_rc_info);
> > + type_register_static(&aspeed_pcie_cfg_info);
> > type_register_static(&aspeed_pcie_phy_info);
> > }
> >
> > diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index
> > 3438516756..2584ea56e2 100644
> > --- a/hw/pci-host/trace-events
> > +++ b/hw/pci-host/trace-events
> > @@ -1,6 +1,10 @@
> > # See docs/devel/tracing.rst for syntax documentation.
> >
> > # aspeed_pcie.c
> > +aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d: num %d
> set IRQ leve %d"
> > +aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d:
> > +addr 0x%" PRIx64 " value 0x%" PRIx32 aspeed_pcie_cfg_write(uint32_t
> > +id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%"
> > +PRIx32 aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t bus,
> > +uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x
> > +devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64
> > aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d:
> addr 0x%" PRIx64 " value 0x%" PRIx32
> > aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value)
> > "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> >
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks
2025-09-15 17:23 ` [SPAM] " Cédric Le Goater
@ 2025-09-17 1:48 ` Jamin Lin
0 siblings, 0 replies; 34+ messages in thread
From: Jamin Lin @ 2025-09-17 1:48 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: Re: [SPAM] [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe
> config with dedicated H2X blocks
>
> On 9/11/25 09:24, Jamin Lin wrote:
> > Introduce PCIe config (H2X) support for the AST2700 SoC.
> >
> > Unlike the AST2600, the AST2700 provides three independent Root
> > Complexes, each with its own H2X (AHB to PCIe bridge) register block of size
> 0x100.
> > All RCs use the same MSI address (0x000000F0). The H2X block includes
> > two different access paths:
> >
> > 1. CFGI (internal bridge): used to access the host bridge itself, always
> > with BDF=0. The AST2700 controller simplifies the design by exposing
> > only one register (H2X_CFGI_TLP) with fields for ADDR[15:0],
> BEN[19:16],
> > and WR[20]. This is not a full TLP descriptor as in the external case.
> > For QEMU readability and code reuse, the model converts
> H2X_CFGI_TLP
> > into a standard TLP TX descriptor with BDF forced to 0 and then calls
> > the existing helpers aspeed_pcie_cfg_readwrite() and
> > aspeed_pcie_cfg_translate_write().
> >
> > 2. CFGE (external EP access): used to access external endpoints. The
> > AST2700 design provides H2X_CFGE_TLP1 and a small FIFO at
> H2X_CFGE_TLPN.
> > For reads, TX DESC0 is stored in TLP1 and DESC1/DESC2 in TLPN FIFO
> > slots. For writes, TX DESC0 is stored in TLP1, DESC1/DESC2 in TLPN
> > FIFO[0..1], and TX write data in TLPN FIFO[2].
> >
> > The implementation extends AspeedPCIECfgState with a small FIFO and
> > index, wires up new register definitions for AST2700, and adds a
> > specific ops table and class (TYPE_ASPEED_2700_PCIE_CFG). The reset
> > handler clears the FIFO state. Interrupt and MSI status registers are also
> supported.
> >
> > This provides enough modeling for firmware and drivers to use any of
> > the three PCIe RCs on AST2700 with their own dedicated H2X config
> > window, while reusing existing TLP decode helpers in QEMU.
> >
> > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
>
> I thinks this implementation has endianess issues when accessing the
> descriptors.
>
Thanks for your review and suggestion.
I am only considering little endianness the same as target side.
Will fix it.
Jamin
> C.
>
>
> > ---
> > include/hw/pci-host/aspeed_pcie.h | 3 +
> > hw/pci-host/aspeed_pcie.c | 158
> ++++++++++++++++++++++++++++++
> > 2 files changed, 161 insertions(+)
> >
> > diff --git a/include/hw/pci-host/aspeed_pcie.h
> > b/include/hw/pci-host/aspeed_pcie.h
> > index c0d46e6a03..d7b4b66f75 100644
> > --- a/include/hw/pci-host/aspeed_pcie.h
> > +++ b/include/hw/pci-host/aspeed_pcie.h
> > @@ -89,6 +89,7 @@ struct AspeedPCIERcState {
> >
> > /* Bridge between AHB bus and PCIe RC. */
> > #define TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
> > +#define TYPE_ASPEED_2700_PCIE_CFG TYPE_ASPEED_PCIE_CFG "-ast2700"
> > OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass,
> > ASPEED_PCIE_CFG);
> >
> > struct AspeedPCIECfgState {
> > @@ -99,6 +100,8 @@ struct AspeedPCIECfgState {
> > uint32_t id;
> >
> > AspeedPCIERcState rc;
> > + uint32_t tlpn_fifo[3];
> > + uint32_t tlpn_idx;
> > };
> >
> > struct AspeedPCIECfgClass {
> > diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> > index bc491aa7bf..4adf17e40b 100644
> > --- a/hw/pci-host/aspeed_pcie.c
> > +++ b/hw/pci-host/aspeed_pcie.c
> > @@ -336,6 +336,11 @@ static const TypeInfo aspeed_pcie_rc_info = {
> > * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1
> (rc_h).
> > * - Registers 0x80 - 0xBF are specific to PCIe0.
> > * - Registers 0xC0 - 0xFF are specific to PCIe1.
> > + *
> > + * On the AST2700:
> > + * - The register range 0x00 - 0xFF is assigned to a single PCIe configuration.
> > + * - There are three PCIe Root Complexes (RCs), each with its own dedicated
> H2X
> > + * register set of size 0x100 (covering offsets 0x00 to 0xFF).
> > */
> >
> > /* AST2600 */
> > @@ -365,6 +370,31 @@ REG32(H2X_RC_H_MSI_EN1, 0xE4)
> > REG32(H2X_RC_H_MSI_STS0, 0xE8)
> > REG32(H2X_RC_H_MSI_STS1, 0xEC)
> >
> > +/* AST2700 */
> > +REG32(H2X_CFGE_INT_STS, 0x08)
> > + FIELD(H2X_CFGE_INT_STS, TX_IDEL, 0, 1)
> > + FIELD(H2X_CFGE_INT_STS, RX_BUSY, 1, 1)
> > +REG32(H2X_CFGI_TLP, 0x20)
> > + FIELD(H2X_CFGI_TLP, ADDR, 0, 16)
> > + FIELD(H2X_CFGI_TLP, BEN, 16, 4)
> > + FIELD(H2X_CFGI_TLP, WR, 20, 1)
> > +REG32(H2X_CFGI_WDATA, 0x24)
> > +REG32(H2X_CFGI_CTRL, 0x28)
> > + FIELD(H2X_CFGI_CTRL, FIRE, 0, 1)
> > +REG32(H2X_CFGI_RDATA, 0x2C)
> > +REG32(H2X_CFGE_TLP1, 0x30)
> > +REG32(H2X_CFGE_TLPN, 0x34)
> > +REG32(H2X_CFGE_CTRL, 0x38)
> > + FIELD(H2X_CFGE_CTRL, FIRE, 0, 1)
> > +REG32(H2X_CFGE_RDATA, 0x3C)
> > +REG32(H2X_INT_EN, 0x40)
> > +REG32(H2X_INT_STS, 0x48)
> > + FIELD(H2X_INT_STS, INTX, 0, 4)
> > +REG32(H2X_MSI_EN0, 0x50)
> > +REG32(H2X_MSI_EN1, 0x54)
> > +REG32(H2X_MSI_STS0, 0x58)
> > +REG32(H2X_MSI_STS1, 0x5C)
> > +
> > #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
> > #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
> > #define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
> @@
> > -382,6 +412,15 @@ static const AspeedPCIERegMap aspeed_regmap = {
> > },
> > };
> >
> > +static const AspeedPCIERegMap aspeed_2700_regmap = {
> > + .rc = {
> > + .int_en_reg = R_H2X_INT_EN,
> > + .int_sts_reg = R_H2X_INT_STS,
> > + .msi_sts0_reg = R_H2X_MSI_STS0,
> > + .msi_sts1_reg = R_H2X_MSI_STS1,
> > + },
> > +};
> > +
> > static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
> > unsigned int size)
> > {
> > @@ -606,6 +645,8 @@ static void aspeed_pcie_cfg_reset(DeviceState *dev)
> > AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> >
> > memset(s->regs, 0, apc->nr_regs << 2);
> > + memset(s->tlpn_fifo, 0, sizeof(s->tlpn_fifo));
> > + s->tlpn_idx = 0;
> > }
> >
> > static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp)
> > @@ -679,6 +720,122 @@ static const TypeInfo aspeed_pcie_cfg_info = {
> > .class_size = sizeof(AspeedPCIECfgClass),
> > };
> >
> > +static void aspeed_2700_pcie_cfg_write(void *opaque, hwaddr addr,
> > + uint64_t data, unsigned int
> > +size) {
> > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> > + AspeedPCIECfgTxDesc desc;
> > + uint32_t reg = addr >> 2;
> > +
> > + trace_aspeed_pcie_cfg_write(s->id, addr, data);
> > +
> > + switch (reg) {
> > + case R_H2X_CFGE_INT_STS:
> > + if (data & R_H2X_CFGE_INT_STS_TX_IDEL_MASK) {
> > + s->regs[R_H2X_CFGE_INT_STS] &=
> ~R_H2X_CFGE_INT_STS_TX_IDEL_MASK;
> > + }
> > +
> > + if (data & R_H2X_CFGE_INT_STS_RX_BUSY_MASK) {
> > + s->regs[R_H2X_CFGE_INT_STS] &=
> ~R_H2X_CFGE_INT_STS_RX_BUSY_MASK;
> > + }
> > + break;
> > + case R_H2X_CFGI_CTRL:
> > + if (data & R_H2X_CFGI_CTRL_FIRE_MASK) {
> > + /*
> > + * Internal access to bridge
> > + * Type and BDF are 0
> > + */
> > + desc.desc0 = 0x04000001 |
> > + (ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, WR) <<
> 30);
> > + desc.desc1 = 0x00401000 |
> > + ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, BEN);
> > + desc.desc2 = 0x00000000 |
> > + ARRAY_FIELD_EX32(s->regs, H2X_CFGI_TLP, ADDR);
> > + desc.wdata = s->regs[R_H2X_CFGI_WDATA];
> > + desc.rdata_reg = R_H2X_CFGI_RDATA;
> > + aspeed_pcie_cfg_readwrite(s, &desc);
> > + }
> > + break;
> > + case R_H2X_CFGE_TLPN:
> > + s->tlpn_fifo[s->tlpn_idx] = data;
> > + s->tlpn_idx = (s->tlpn_idx + 1) % ARRAY_SIZE(s->tlpn_fifo);
> > + break;
> > + case R_H2X_CFGE_CTRL:
> > + if (data & R_H2X_CFGE_CTRL_FIRE_MASK) {
> > + desc.desc0 = s->regs[R_H2X_CFGE_TLP1];
> > + desc.desc1 = s->tlpn_fifo[0];
> > + desc.desc2 = s->tlpn_fifo[1];
> > + desc.wdata = s->tlpn_fifo[2];
> > + desc.rdata_reg = R_H2X_CFGE_RDATA;
> > + aspeed_pcie_cfg_readwrite(s, &desc);
> > + s->regs[R_H2X_CFGE_INT_STS] |=
> R_H2X_CFGE_INT_STS_TX_IDEL_MASK;
> > + s->regs[R_H2X_CFGE_INT_STS] |=
> R_H2X_CFGE_INT_STS_RX_BUSY_MASK;
> > + s->tlpn_idx = 0;
> > + }
> > + break;
> > +
> > + case R_H2X_INT_STS:
> > + s->regs[reg] &= ~data | R_H2X_INT_STS_INTX_MASK;
> > + break;
> > + /*
> > + * These status registers are used for notify sources ISR are executed.
> > + * If one source ISR is executed, it will clear one bit.
> > + * If it clear all bits, it means to initialize this register status
> > + * rather than sources ISR are executed.
> > + */
> > + case R_H2X_MSI_STS0:
> > + case R_H2X_MSI_STS1:
> > + if (data == 0) {
> > + return ;
> > + }
> > +
> > + s->regs[reg] &= ~data;
> > + if (data == 0xffffffff) {
> > + return;
> > + }
> > +
> > + if (!s->regs[R_H2X_MSI_STS0] &&
> > + !s->regs[R_H2X_MSI_STS1]) {
> > + trace_aspeed_pcie_rc_msi_clear_irq(s->id, 0);
> > + qemu_set_irq(s->rc.irq, 0);
> > + }
> > + break;
> > + default:
> > + s->regs[reg] = data;
> > + break;
> > + }
> > +}
> > +
> > +static const MemoryRegionOps aspeed_2700_pcie_cfg_ops = {
> > + .read = aspeed_pcie_cfg_read,
> > + .write = aspeed_2700_pcie_cfg_write,
> > + .endianness = DEVICE_LITTLE_ENDIAN,
> > + .valid = {
> > + .min_access_size = 1,
> > + .max_access_size = 4,
> > + },
> > +};
> > +
> > +static void aspeed_2700_pcie_cfg_class_init(ObjectClass *klass,
> > + const void *data) {
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
> > +
> > + dc->desc = "ASPEED 2700 PCIe Config";
> > + apc->reg_ops = &aspeed_2700_pcie_cfg_ops;
> > + apc->reg_map = &aspeed_2700_regmap;
> > + apc->nr_regs = 0x100 >> 2;
> > + apc->rc_msi_addr = 0x000000F0;
> > + apc->rc_bus_nr = 0;
> > +}
> > +
> > +static const TypeInfo aspeed_2700_pcie_cfg_info = {
> > + .name = TYPE_ASPEED_2700_PCIE_CFG,
> > + .parent = TYPE_ASPEED_PCIE_CFG,
> > + .class_init = aspeed_2700_pcie_cfg_class_init, };
> > +
> > /*
> > * PCIe PHY
> > *
> > @@ -846,6 +1003,7 @@ static void aspeed_pcie_register_types(void)
> > type_register_static(&aspeed_pcie_root_device_info);
> > type_register_static(&aspeed_pcie_root_port_info);
> > type_register_static(&aspeed_pcie_cfg_info);
> > + type_register_static(&aspeed_2700_pcie_cfg_info);
> > type_register_static(&aspeed_pcie_phy_info);
> > type_register_static(&aspeed_2700_pcie_phy_info);
> > }
^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge
2025-09-17 1:45 ` Jamin Lin
@ 2025-09-17 9:05 ` Jamin Lin
2025-09-17 10:21 ` Cédric Le Goater
0 siblings, 1 reply; 34+ messages in thread
From: Jamin Lin @ 2025-09-17 9:05 UTC (permalink / raw)
To: Cédric Le Goater, Paolo Bonzini, Peter Maydell, Steven Lee,
Troy Lee, Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
Hi Cédric
> Subject: RE: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe
> config space and host bridge
>
> Hi Cédric
>
> > Subject: Re: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600
> > PCIe config space and host bridge
> >
> > On 9/11/25 09:24, Jamin Lin wrote:
> > > Introduce PCIe config and host bridge model for the AST2600 platform.
> > >
> > > This patch adds support for the H2X (AHB to PCIe Bus Bridge)
> > > controller with a 0x100 byte register space. The register layout is
> > > shared between two root complexes: 0x00–0x7f is common, 0x80–0xbf
> > > for RC_L, and 0xc0–0xff for RC_H. Only RC_H is modeled in this
> > implementation.
> > >
> > > The RC_H bus uses bus numbers in the 0x80–0xff range instead of the
> > > standard root bus 0x00. To allow the PCI subsystem to discover
> > > devices, the host bridge logic remaps the root bus number back to
> > > 0x00 whenever the configured bus number matches the "bus-nr" property.
> > >
> > > New MMIO callbacks are added for the H2X config space:
> > > - aspeed_pcie_cfg_read() and aspeed_pcie_cfg_write() handle register
> > > accesses.
> > > - aspeed_pcie_cfg_readwrite() provides configuration read/write support.
> > > - aspeed_pcie_cfg_translate_write() handles PCIe byte-enable semantics
> for
> > > write operations.
> > >
> > > The reset handler initializes the H2X register block with default
> > > values as defined in the AST2600 datasheet.
> > >
> > > Additional changes:
> > > - Implement ASPEED PCIe root complex (TYPE_ASPEED_PCIE_RC).
> > > - Wire up interrupt propagation via aspeed_pcie_rc_set_irq().
> > > - Add tracepoints for config read/write and INTx handling.
> > >
> > > Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
> > > ---
> > > include/hw/pci-host/aspeed_pcie.h | 58 ++++
> > > hw/pci-host/aspeed_pcie.c | 422
> > ++++++++++++++++++++++++++++++
> > > hw/pci-host/trace-events | 4 +
> > > 3 files changed, 484 insertions(+)
> > >
> > > diff --git a/include/hw/pci-host/aspeed_pcie.h
> > > b/include/hw/pci-host/aspeed_pcie.h
> > > index faf87073ab..e2c5dc6f62 100644
> > > --- a/include/hw/pci-host/aspeed_pcie.h
> > > +++ b/include/hw/pci-host/aspeed_pcie.h
> > > @@ -24,6 +24,64 @@
> > > #include "hw/pci/pcie_host.h"
> > > #include "qom/object.h"
> > >
> > > +typedef struct AspeedPCIECfgTxDesc {
> > > + uint32_t desc0;
> > > + uint32_t desc1;
> > > + uint32_t desc2;
> > > + uint32_t desc3;
> > > + uint32_t wdata;
> > > + uint32_t rdata_reg;
> > > +} AspeedPCIECfgTxDesc;
> > > +
> > > +typedef struct AspeedPCIERcRegs {
> > > + uint32_t int_en_reg;
> > > + uint32_t int_sts_reg;
> > > +} AspeedPCIERcRegs;
> > > +
> > > +typedef struct AspeedPCIERegMap {
> > > + AspeedPCIERcRegs rc;
> > > +} AspeedPCIERegMap;
> > > +
> > > +#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
> > > +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
> > > +
> > > +struct AspeedPCIERcState {
> > > + PCIExpressHost parent_obj;
> > > +
> > > + MemoryRegion mmio_window;
> > > + MemoryRegion io_window;
> > > + MemoryRegion mmio;
> > > + MemoryRegion io;
> > > +
> > > + uint32_t bus_nr;
> > > + char name[16];
> > > + qemu_irq irq;
> > > +};
> > > +
> > > +/* Bridge between AHB bus and PCIe RC. */ #define
> > > +TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
> > > +OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass,
> > > +ASPEED_PCIE_CFG);
> > > +
> > > +struct AspeedPCIECfgState {
> > > + SysBusDevice parent_obj;
> > > +
> > > + MemoryRegion mmio;
> > > + uint32_t *regs;
> > > + uint32_t id;
> > > +
> > > + AspeedPCIERcState rc;
> > > +};
> > > +
> > > +struct AspeedPCIECfgClass {
> > > + SysBusDeviceClass parent_class;
> > > +
> > > + const AspeedPCIERegMap *reg_map;
> > > + const MemoryRegionOps *reg_ops;
> > > +
> > > + uint64_t rc_bus_nr;
> > > + uint64_t nr_regs;
> > > +};
> > > +
> > > #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
> > > OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass,
> > > ASPEED_PCIE_PHY);
> > >
> > > diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
> > > index 5584449b17..9fb7c1ef67 100644
> > > --- a/hw/pci-host/aspeed_pcie.c
> > > +++ b/hw/pci-host/aspeed_pcie.c
> > > @@ -27,6 +27,426 @@
> > > #include "hw/pci/msi.h"
> > > #include "trace.h"
> > >
> > > +/*
> > > + * PCIe Root Complex (RC)
> > > + */
> > > +
> > > +static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int
> > > +level) {
> > > + AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
> > > + AspeedPCIECfgState *cfg =
> > > + container_of(rc, AspeedPCIECfgState, rc);
> > > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
> > > + const AspeedPCIERcRegs *rc_regs;
> >
> > I suggest you cache &apc->reg_map->rc under AspeedPCIECfgState as an
> > attribute (at realize time). This will ease reading the code and
> > improve performance.
> >
> Thanks for your review and suggestion.
> Will do.
>
> > > + bool intx;
> > > +
> > > + assert(irq < PCI_NUM_PINS);
> > > +
> > > + rc_regs = &apc->reg_map->rc;
> > > +
> > > + if (level) {
> > > + cfg->regs[rc_regs->int_sts_reg] |= BIT(irq);
> > > + } else {
> > > + cfg->regs[rc_regs->int_sts_reg] &= ~BIT(irq);
> > > + }
> > > +
> > > + intx = !!(cfg->regs[rc_regs->int_sts_reg] &
> > cfg->regs[rc_regs->int_en_reg]);
> > > + trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx);
> > > + qemu_set_irq(rc->irq, intx);
> > > +}
> > > +
> > > +static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num) {
> > > + return irq_num % PCI_NUM_PINS;
> > > +}
> > > +
> > > +static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) {
> > > + PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
> > > + AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev);
> > > + AspeedPCIECfgState *cfg =
> > > + container_of(rc, AspeedPCIECfgState, rc);
> > > + PCIHostState *pci = PCI_HOST_BRIDGE(dev);
> > > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> > > + g_autofree char *name;
> > > +
> > > + /* PCI configuration space */
> > > + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
> > > + sysbus_init_mmio(sbd, &pex->mmio);
> > > +
> > > + /* MMIO and IO region */
> > > + memory_region_init(&rc->mmio, OBJECT(rc), "mmio",
> UINT64_MAX);
> > > + memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000);
> > > +
> > > + name = g_strdup_printf("pcie.%d.mmio_window", cfg->id);
> > > + memory_region_init_io(&rc->mmio_window, OBJECT(rc),
> > &unassigned_io_ops,
> > > + OBJECT(rc), name, UINT64_MAX);
> > > + name = g_strdup_printf("pcie.%d.ioport_window", cfg->id);
> > > + memory_region_init_io(&rc->io_window, OBJECT(rc),
> > &unassigned_io_ops,
> > > + OBJECT(rc), name, 0x10000);
> > > +
> > > + memory_region_add_subregion(&rc->mmio_window, 0,
> &rc->mmio);
> > > + memory_region_add_subregion(&rc->io_window, 0, &rc->io);
> > > + sysbus_init_mmio(sbd, &rc->mmio_window);
> > > + sysbus_init_mmio(sbd, &rc->io_window);
> > > +
> > > + sysbus_init_irq(sbd, &rc->irq);
> > > + name = g_strdup_printf("pcie.rc%d", cfg->id);
> > > + pci->bus = pci_register_root_bus(dev, name, aspeed_pcie_rc_set_irq,
> > > + aspeed_pcie_rc_map_irq,
> rc,
> > &rc->mmio,
> > > + &rc->io, 0, 4,
> > TYPE_PCIE_BUS);
> > > + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; }
> > > +
> > > +static const char *aspeed_pcie_rc_root_bus_path(PCIHostState
> > *host_bridge,
> > > + PCIBus
> *rootbus)
> > {
> > > + AspeedPCIERcState *rc = ASPEED_PCIE_RC(host_bridge);
> > > + AspeedPCIECfgState *cfg =
> > > + container_of(rc, AspeedPCIECfgState, rc);
> > > +
> > > + snprintf(rc->name, sizeof(rc->name), "%04x:%02x", cfg->id,
> > > + rc->bus_nr);
> > > +
> > > + return rc->name;
> > > +}
> > > +
> > > +static const Property aspeed_pcie_rc_props[] = {
> > > + DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), };
> > > +
> > > +static void aspeed_pcie_rc_class_init(ObjectClass *klass, const
> > > +void
> > > +*data) {
> > > + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
> > > + DeviceClass *dc = DEVICE_CLASS(klass);
> > > +
> > > + dc->desc = "ASPEED PCIe RC";
> > > + dc->realize = aspeed_pcie_rc_realize;
> > > + dc->fw_name = "pci";
> > > + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> > > +
> > > + hc->root_bus_path = aspeed_pcie_rc_root_bus_path;
> > > + device_class_set_props(dc, aspeed_pcie_rc_props);
> > > +
> > > + msi_nonbroken = true;
> > > +}
> > > +
> > > +static const TypeInfo aspeed_pcie_rc_info = {
> > > + .name = TYPE_ASPEED_PCIE_RC,
> > > + .parent = TYPE_PCIE_HOST_BRIDGE,
> > > + .instance_size = sizeof(AspeedPCIERcState),
> > > + .class_init = aspeed_pcie_rc_class_init, };
> > > +
> > > +/*
> > > + * PCIe Config
> > > + *
> > > + * AHB to PCIe Bus Bridge (H2X)
> > > + *
> > > + * On the AST2600:
> > > + * NOTE: rc_l is not supported by this model.
> > > + * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
> > > + * - Registers 0x80 - 0xBF are specific to PCIe0.
> > > + * - Registers 0xC0 - 0xFF are specific to PCIe1.
> > > + */
> > > +
> > > +/* AST2600 */
> > > +REG32(H2X_CTRL, 0x00)
> > > + FIELD(H2X_CTRL, CLEAR_RX, 4, 1)
> > > +REG32(H2X_TX_CLEAR, 0x08)
> > > + FIELD(H2X_TX_CLEAR, IDLE, 0, 1)
> > > +REG32(H2X_RDATA, 0x0C)
> > > +REG32(H2X_TX_DESC0, 0x10)
> > > +REG32(H2X_TX_DESC1, 0x14)
> > > +REG32(H2X_TX_DESC2, 0x18)
> > > +REG32(H2X_TX_DESC3, 0x1C)
> > > +REG32(H2X_TX_DATA, 0x20)
> > > +REG32(H2X_TX_STS, 0x24)
> > > + FIELD(H2X_TX_STS, IDLE, 31, 1)
> > > + FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1)
> > > + FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1)
> > > + FIELD(H2X_TX_STS, TRIG, 0, 1)
> > > +REG32(H2X_RC_H_CTRL, 0xC0)
> > > +REG32(H2X_RC_H_INT_EN, 0xC4)
> > > +REG32(H2X_RC_H_INT_STS, 0xC8)
> > > + SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
> > > + SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
> > > +REG32(H2X_RC_H_RDATA, 0xCC)
> > > +
> > > +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
> > > +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
> > > +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
> > > +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
> > > +
> > > +#define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff) #define
> > > +PCIE_CFG_BYTE_EN(x) ((x) & 0xf)
> > > +
> > > +static const AspeedPCIERegMap aspeed_regmap = {
> > > + .rc = {
> > > + .int_en_reg = R_H2X_RC_H_INT_EN,
> > > + .int_sts_reg = R_H2X_RC_H_INT_STS,
> > > + },
> > > +};
> > > +
> > > +static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
> > > + unsigned int size) {
> > > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> > > + uint32_t reg = addr >> 2;
> > > + uint32_t value = 0;
> > > +
> > > + value = s->regs[reg];
> > > +
> > > + trace_aspeed_pcie_cfg_read(s->id, addr, value);
> > > +
> > > + return value;
> > > +}
> > > +
> > > +static void aspeed_pcie_cfg_translate_write(uint8_t byte_en,
> > > +uint32_t
> > *addr,
> > > + uint64_t *val, int
> > *len)
> > > +{
> > > + uint64_t packed_val = 0;
> > > + int first_bit = -1;
> > > + int index = 0;
> > > + int i;
> > > +
> > > + *len = ctpop8(byte_en);
> > > +
> > > + if (*len == 0 || *len > 4) {
> > > + goto err;
> > > + }
> > > +
> > > + /* Special case: full 4-byte write must be 4-byte aligned */
> > > + if (byte_en == 0x0f) {
> > > + if (*addr % 4 != 0) {
> >
> > This is an aligment issue to be reported as such and not with "invalid
> > byte enable"
> >
> > I think you should remove the "goto err" and generate a
> > LOG_GUEST_ERROR instead.
> >
> Will fix it.
> > > + goto err;
> > > + }
> > > + *val = *val & 0xffffffff;
> > > + return;
> > > + }
> > > +
> > > + for (i = 0; i < 4; i++) {
> > > + if (byte_en & (1 << i)) {
> > > + if (first_bit < 0) {
> > > + first_bit = i;
> > > + }
> > > + packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8);
> > > + index++;> + }
> > > + }
> > > +
> > > + *addr += first_bit;
> > > + *val = packed_val;
> > > +
> > > + return;
> > > +
> > > +err:
> > > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable:
> > 0x%x\n",
> > > + __func__, byte_en); }
> > > +
> > > +static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s,
> > > + const
> AspeedPCIECfgTxDesc
> > > +*desc) {
> > > + AspeedPCIERcState *rc = &s->rc;
> > > + PCIHostState *pci;
> > > + uint32_t cfg_addr;
> > > + PCIDevice *pdev;
> > > + uint32_t offset;
> > > + uint8_t byte_en;
> > > + bool is_write;
> > > + uint8_t devfn;
> > > + uint64_t val;
> > > + uint8_t bus;
> > > + int len;
> > > +
> > > + val = ~0;
> > > + is_write = !!(desc->desc0 & BIT(30));
> > > + cfg_addr = desc->desc2;
> >
> >
> > hmm, what about endianess ?
> >
We keep .endianness = DEVICE_LITTLE_ENDIAN on the MMIO region, so the guest wire-LE bytes are already converted at the MMIO boundary.
Inside the device (s->regs[], FIFOs, and desc.*) we consistently use CPU-endian integers and apply bitfield operations on those.
I tried adding explicit le32_to_cpu()/cpu_to_le32() on s->regs[] to “convert” them again to CPU-endian,
but that caused a "double swap" on big-endian hosts (e.g. MIPS64). The result was broken config TLP decoding and incorrect byte-enable packing.
I experimented with the patch below—does this look incorrect given the MMIO endianness model?
case R_H2X_TX_STS:
if (data & R_H2X_TX_STS_TRIG_MASK) {
- desc.desc0 = s->regs[R_H2X_TX_DESC0];
- desc.desc1 = s->regs[R_H2X_TX_DESC1];
- desc.desc2 = s->regs[R_H2X_TX_DESC2];
- desc.desc3 = s->regs[R_H2X_TX_DESC3];
- desc.wdata = s->regs[R_H2X_TX_DATA];
+ desc.desc0 = le32_to_cpu(s->regs[R_H2X_TX_DESC0]);
+ desc.desc1 = le32_to_cpu(s->regs[R_H2X_TX_DESC1]);
+ desc.desc2 = le32_to_cpu(s->regs[R_H2X_TX_DESC2]);
+ desc.desc3 = le32_to_cpu(s->regs[R_H2X_TX_DESC3]);
+ desc.wdata = le32_to_cpu(s->regs[R_H2X_TX_DATA]);
desc.rdata_reg = R_H2X_RC_H_RDATA;
aspeed_pcie_cfg_readwrite(s, &desc);
...
On MIPS64 (BE) this change made the device non-functional, which aligns with the double-swap hypothesis.
My understanding is we should not add these conversions and instead keep s->regs[]/desc.* strictly CPU-endian, relying on the MMIO layer for wire-LE ↔ CPU conversions.
I tested the v2 patch series and it passes on a big-endian host machine (qemumips64)
Does anything above look incorrect to you?
Thanks-Jamin
> I am only considering little endianness the same as target side.
> Will fix them in v3.
> Jamin
> > > + bus = (cfg_addr >> 24) & 0xff;
> > > + devfn = (cfg_addr >> 16) & 0xff;
> > > + offset = cfg_addr & 0xffc;
> > > +
> > > + pci = PCI_HOST_BRIDGE(rc);
> > > +
> > > + /*
> > > + * On the AST2600, the RC_H bus number ranges from 0x80 to
> > > + 0xFF,
> > and its
> > > + * root port uses bus number 0x80 instead of the standard 0x00.
> > > + To
> > locate
> > > + * the device at root port 0, remap bus number 0x80 to 0x00 so
> > > + that
> > the
> > > + * PCI subsystem can correctly discover the devices.
> > > + */
> > > + if (bus == rc->bus_nr) {
> > > + bus = 0;
> > > + }
> > > +
> > > + pdev = pci_find_device(pci->bus, bus, devfn);
> > > + if (!pdev) {
> > > + s->regs[desc->rdata_reg] = ~0;
> > > + goto out;
> > > + }
> > > +
> > > + switch (PCIE_CFG_FMTTYPE_MASK(desc->desc0)) {
> > > + case TLP_FMTTYPE_CFGWR0:
> > > + case TLP_FMTTYPE_CFGWR1:
> > > + byte_en = PCIE_CFG_BYTE_EN(desc->desc1);
> > > + val = desc->wdata;
> >
> > what about endianess ?
> >
> > > + aspeed_pcie_cfg_translate_write(byte_en, &offset, &val, &len);
> > > + pci_host_config_write_common(pdev, offset,
> > pci_config_size(pdev),
> > > + val, len);
> > > + break;
> > > + case TLP_FMTTYPE_CFGRD0:
> > > + case TLP_FMTTYPE_CFGRD1:
> > > + val = pci_host_config_read_common(pdev, offset,
> > > + pci_config_size(pdev),
> > 4);
> > > + s->regs[desc->rdata_reg] = val;
> > > + break;
> > > + default:
> > > + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CFG type.
> > DESC0=0x%x\n",
> > > + __func__, desc->desc0);
> > > + }
> > > +
> > > +out:
> > > + trace_aspeed_pcie_cfg_rw(s->id, is_write ? "write" : "read",
> > > +bus,
> > devfn,
> > > + cfg_addr, val); }
> > > +
> > > +static void aspeed_pcie_cfg_write(void *opaque, hwaddr addr,
> > > +uint64_t
> > data,
> > > + unsigned int size) {
> > > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
> > > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> > > + AspeedPCIECfgTxDesc desc;
> > > + uint32_t reg = addr >> 2;
> > > + uint32_t rc_reg;
> > > +
> > > + trace_aspeed_pcie_cfg_write(s->id, addr, data);
> > > +
> > > + switch (reg) {
> > > + case R_H2X_CTRL:
> > > + if (data & R_H2X_CTRL_CLEAR_RX_MASK) {
> > > + s->regs[R_H2X_RDATA] = ~0;
> > > + }
> > > + break;
> > > + case R_H2X_TX_CLEAR:
> > > + if (data & R_H2X_TX_CLEAR_IDLE_MASK) {
> > > + s->regs[R_H2X_TX_STS] &= ~R_H2X_TX_STS_IDLE_MASK;
> > > + }
> > > + break;
> > > + case R_H2X_TX_STS:
> > > + if (data & R_H2X_TX_STS_TRIG_MASK) {
> > > + desc.desc0 = s->regs[R_H2X_TX_DESC0];
> > > + desc.desc1 = s->regs[R_H2X_TX_DESC1];
> > > + desc.desc2 = s->regs[R_H2X_TX_DESC2];
> > > + desc.desc3 = s->regs[R_H2X_TX_DESC3];
> > > + desc.wdata = s->regs[R_H2X_TX_DATA];
> > > + desc.rdata_reg = R_H2X_RC_H_RDATA;
> >
> > endianess.
> >
> >
> > Thanks,
> >
> > C.
> >
> >
> >
> > > + aspeed_pcie_cfg_readwrite(s, &desc);
> > > + rc_reg = apc->reg_map->rc.int_sts_reg;
> > > + s->regs[rc_reg] |= H2X_RC_INT_INTDONE_MASK;
> > > + s->regs[R_H2X_TX_STS] |=
> > > + BIT(R_H2X_TX_STS_RC_H_TX_COMP_SHIFT);
> > > + s->regs[R_H2X_TX_STS] |= R_H2X_TX_STS_IDLE_MASK;
> > > + }
> > > + break;
> > > + /* preserve INTx status */
> > > + case R_H2X_RC_H_INT_STS:
> > > + if (data & H2X_RC_INT_INTDONE_MASK) {
> > > + s->regs[R_H2X_TX_STS] &=
> > ~R_H2X_TX_STS_RC_H_TX_COMP_MASK;
> > > + }
> > > + s->regs[reg] &= ~data | H2X_RC_INT_INTX_MASK;
> > > + break;
> > > + default:
> > > + s->regs[reg] = data;
> > > + break;
> > > + }
> > > +}
> > > +
> > > +static const MemoryRegionOps aspeed_pcie_cfg_ops = {
> > > + .read = aspeed_pcie_cfg_read,
> > > + .write = aspeed_pcie_cfg_write,
> > > + .endianness = DEVICE_LITTLE_ENDIAN,
> > > + .valid = {
> > > + .min_access_size = 1,
> > > + .max_access_size = 4,
> > > + },
> > > +};
> > > +
> > > +static void aspeed_pcie_cfg_instance_init(Object *obj) {
> > > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(obj);
> > > +
> > > + object_initialize_child(obj, "rc", &s->rc,
> > > + TYPE_ASPEED_PCIE_RC);
> > > +
> > > + return;
> > > +}
> > > +
> > > +static void aspeed_pcie_cfg_reset(DeviceState *dev) {
> > > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> > > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> > > +
> > > + memset(s->regs, 0, apc->nr_regs << 2); }
> > > +
> > > +static void aspeed_pcie_cfg_realize(DeviceState *dev, Error **errp) {
> > > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> > > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> > > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(s);
> > > + g_autofree char *name;
> > > +
> > > + s->regs = g_new(uint32_t, apc->nr_regs);
> > > + name = g_strdup_printf(TYPE_ASPEED_PCIE_CFG ".regs.%d", s->id);
> > > + memory_region_init_io(&s->mmio, OBJECT(s), apc->reg_ops, s,
> name,
> > > + apc->nr_regs << 2);
> > > + sysbus_init_mmio(sbd, &s->mmio);
> > > +
> > > + object_property_set_int(OBJECT(&s->rc), "bus-nr",
> > > + apc->rc_bus_nr,
> > > + &error_abort);
> > > + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rc), errp)) {
> > > + return;
> > > + }
> > > +}
> > > +
> > > +static void aspeed_pcie_cfg_unrealize(DeviceState *dev) {
> > > + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(dev);
> > > +
> > > + g_free(s->regs);
> > > + s->regs = NULL;
> > > +}
> > > +
> > > +static const Property aspeed_pcie_cfg_props[] = {
> > > + DEFINE_PROP_UINT32("id", AspeedPCIECfgState, id, 0), };
> > > +
> > > +static void aspeed_pcie_cfg_class_init(ObjectClass *klass, const
> > > +void
> > > +*data) {
> > > + DeviceClass *dc = DEVICE_CLASS(klass);
> > > + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_CLASS(klass);
> > > +
> > > + dc->desc = "ASPEED PCIe Config";
> > > + dc->realize = aspeed_pcie_cfg_realize;
> > > + dc->unrealize = aspeed_pcie_cfg_unrealize;
> > > + device_class_set_legacy_reset(dc, aspeed_pcie_cfg_reset);
> > > + device_class_set_props(dc, aspeed_pcie_cfg_props);
> > > +
> > > + apc->reg_ops = &aspeed_pcie_cfg_ops;
> > > + apc->reg_map = &aspeed_regmap;
> > > + apc->nr_regs = 0x100 >> 2;
> > > + apc->rc_bus_nr = 0x80;
> > > +}
> > > +
> > > +static const TypeInfo aspeed_pcie_cfg_info = {
> > > + .name = TYPE_ASPEED_PCIE_CFG,
> > > + .parent = TYPE_SYS_BUS_DEVICE,
> > > + .instance_init = aspeed_pcie_cfg_instance_init,
> > > + .instance_size = sizeof(AspeedPCIECfgState),
> > > + .class_init = aspeed_pcie_cfg_class_init,
> > > + .class_size = sizeof(AspeedPCIECfgClass), };
> > > +
> > > /*
> > > * PCIe PHY
> > > *
> > > @@ -152,6 +572,8 @@ static const TypeInfo aspeed_pcie_phy_info = {
> > >
> > > static void aspeed_pcie_register_types(void)
> > > {
> > > + type_register_static(&aspeed_pcie_rc_info);
> > > + type_register_static(&aspeed_pcie_cfg_info);
> > > type_register_static(&aspeed_pcie_phy_info);
> > > }
> > >
> > > diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events
> > > index
> > > 3438516756..2584ea56e2 100644
> > > --- a/hw/pci-host/trace-events
> > > +++ b/hw/pci-host/trace-events
> > > @@ -1,6 +1,10 @@
> > > # See docs/devel/tracing.rst for syntax documentation.
> > >
> > > # aspeed_pcie.c
> > > +aspeed_pcie_rc_intx_set_irq(uint32_t id, int num, int level) "%d:
> > > +num %d
> > set IRQ leve %d"
> > > +aspeed_pcie_cfg_read(uint32_t id, uint64_t addr, uint32_t value) "%d:
> > > +addr 0x%" PRIx64 " value 0x%" PRIx32 aspeed_pcie_cfg_write(uint32_t
> > > +id, uint64_t addr, uint32_t value) "%d: addr 0x%" PRIx64 " value 0x%"
> > > +PRIx32 aspeed_pcie_cfg_rw(uint32_t id, const char *dir, uint8_t
> > > +bus, uint8_t devfn, uint64_t addr, uint64_t data) "%d: %s bus:0x%x
> > > +devfn:0x%x addr 0x%" PRIx64 " data 0x%" PRIx64
> > > aspeed_pcie_phy_read(uint32_t id, uint64_t addr, uint32_t value) "%d:
> > addr 0x%" PRIx64 " value 0x%" PRIx32
> > > aspeed_pcie_phy_write(uint32_t id, uint64_t addr, uint32_t value)
> > > "%d: addr 0x%" PRIx64 " value 0x%" PRIx32
> > >
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge
2025-09-17 9:05 ` Jamin Lin
@ 2025-09-17 10:21 ` Cédric Le Goater
0 siblings, 0 replies; 34+ messages in thread
From: Cédric Le Goater @ 2025-09-17 10:21 UTC (permalink / raw)
To: Jamin Lin, Paolo Bonzini, Peter Maydell, Steven Lee, Troy Lee,
Andrew Jeffery, Joel Stanley, Michael S. Tsirkin,
Marcel Apfelbaum, open list:ARM TCG CPUs,
open list:All patches CC here
Cc: Troy Lee, nabihestefan@google.com, wuhaotsh@google.com,
titusr@google.com
On 9/17/25 11:05, Jamin Lin wrote:
> Hi Cédric
>
>> Subject: RE: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe
>> config space and host bridge
>>
>> Hi Cédric
>>
>>> Subject: Re: [SPAM] [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600
>>> PCIe config space and host bridge
>>>
>>> On 9/11/25 09:24, Jamin Lin wrote:
>>>> Introduce PCIe config and host bridge model for the AST2600 platform.
>>>>
>>>> This patch adds support for the H2X (AHB to PCIe Bus Bridge)
>>>> controller with a 0x100 byte register space. The register layout is
>>>> shared between two root complexes: 0x00–0x7f is common, 0x80–0xbf
>>>> for RC_L, and 0xc0–0xff for RC_H. Only RC_H is modeled in this
>>> implementation.
>>>>
>>>> The RC_H bus uses bus numbers in the 0x80–0xff range instead of the
>>>> standard root bus 0x00. To allow the PCI subsystem to discover
>>>> devices, the host bridge logic remaps the root bus number back to
>>>> 0x00 whenever the configured bus number matches the "bus-nr" property.
>>>>
>>>> New MMIO callbacks are added for the H2X config space:
>>>> - aspeed_pcie_cfg_read() and aspeed_pcie_cfg_write() handle register
>>>> accesses.
>>>> - aspeed_pcie_cfg_readwrite() provides configuration read/write support.
>>>> - aspeed_pcie_cfg_translate_write() handles PCIe byte-enable semantics
>> for
>>>> write operations.
>>>>
>>>> The reset handler initializes the H2X register block with default
>>>> values as defined in the AST2600 datasheet.
>>>>
>>>> Additional changes:
>>>> - Implement ASPEED PCIe root complex (TYPE_ASPEED_PCIE_RC).
>>>> - Wire up interrupt propagation via aspeed_pcie_rc_set_irq().
>>>> - Add tracepoints for config read/write and INTx handling.
>>>>
>>>> Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
>>>> ---
>>>> include/hw/pci-host/aspeed_pcie.h | 58 ++++
>>>> hw/pci-host/aspeed_pcie.c | 422
>>> ++++++++++++++++++++++++++++++
>>>> hw/pci-host/trace-events | 4 +
>>>> 3 files changed, 484 insertions(+)
>>>>
>>>> diff --git a/include/hw/pci-host/aspeed_pcie.h
>>>> b/include/hw/pci-host/aspeed_pcie.h
>>>> index faf87073ab..e2c5dc6f62 100644
>>>> --- a/include/hw/pci-host/aspeed_pcie.h
>>>> +++ b/include/hw/pci-host/aspeed_pcie.h
>>>> @@ -24,6 +24,64 @@
>>>> #include "hw/pci/pcie_host.h"
>>>> #include "qom/object.h"
>>>>
>>>> +typedef struct AspeedPCIECfgTxDesc {
>>>> + uint32_t desc0;
>>>> + uint32_t desc1;
>>>> + uint32_t desc2;
>>>> + uint32_t desc3;
>>>> + uint32_t wdata;
>>>> + uint32_t rdata_reg;
>>>> +} AspeedPCIECfgTxDesc;
>>>> +
>>>> +typedef struct AspeedPCIERcRegs {
>>>> + uint32_t int_en_reg;
>>>> + uint32_t int_sts_reg;
>>>> +} AspeedPCIERcRegs;
>>>> +
>>>> +typedef struct AspeedPCIERegMap {
>>>> + AspeedPCIERcRegs rc;
>>>> +} AspeedPCIERegMap;
>>>> +
>>>> +#define TYPE_ASPEED_PCIE_RC "aspeed.pcie-rc"
>>>> +OBJECT_DECLARE_SIMPLE_TYPE(AspeedPCIERcState, ASPEED_PCIE_RC);
>>>> +
>>>> +struct AspeedPCIERcState {
>>>> + PCIExpressHost parent_obj;
>>>> +
>>>> + MemoryRegion mmio_window;
>>>> + MemoryRegion io_window;
>>>> + MemoryRegion mmio;
>>>> + MemoryRegion io;
>>>> +
>>>> + uint32_t bus_nr;
>>>> + char name[16];
>>>> + qemu_irq irq;
>>>> +};
>>>> +
>>>> +/* Bridge between AHB bus and PCIe RC. */ #define
>>>> +TYPE_ASPEED_PCIE_CFG "aspeed.pcie-cfg"
>>>> +OBJECT_DECLARE_TYPE(AspeedPCIECfgState, AspeedPCIECfgClass,
>>>> +ASPEED_PCIE_CFG);
>>>> +
>>>> +struct AspeedPCIECfgState {
>>>> + SysBusDevice parent_obj;
>>>> +
>>>> + MemoryRegion mmio;
>>>> + uint32_t *regs;
>>>> + uint32_t id;
>>>> +
>>>> + AspeedPCIERcState rc;
>>>> +};
>>>> +
>>>> +struct AspeedPCIECfgClass {
>>>> + SysBusDeviceClass parent_class;
>>>> +
>>>> + const AspeedPCIERegMap *reg_map;
>>>> + const MemoryRegionOps *reg_ops;
>>>> +
>>>> + uint64_t rc_bus_nr;
>>>> + uint64_t nr_regs;
>>>> +};
>>>> +
>>>> #define TYPE_ASPEED_PCIE_PHY "aspeed.pcie-phy"
>>>> OBJECT_DECLARE_TYPE(AspeedPCIEPhyState, AspeedPCIEPhyClass,
>>>> ASPEED_PCIE_PHY);
>>>>
>>>> diff --git a/hw/pci-host/aspeed_pcie.c b/hw/pci-host/aspeed_pcie.c
>>>> index 5584449b17..9fb7c1ef67 100644
>>>> --- a/hw/pci-host/aspeed_pcie.c
>>>> +++ b/hw/pci-host/aspeed_pcie.c
>>>> @@ -27,6 +27,426 @@
>>>> #include "hw/pci/msi.h"
>>>> #include "trace.h"
>>>>
>>>> +/*
>>>> + * PCIe Root Complex (RC)
>>>> + */
>>>> +
>>>> +static void aspeed_pcie_rc_set_irq(void *opaque, int irq, int
>>>> +level) {
>>>> + AspeedPCIERcState *rc = (AspeedPCIERcState *) opaque;
>>>> + AspeedPCIECfgState *cfg =
>>>> + container_of(rc, AspeedPCIECfgState, rc);
>>>> + AspeedPCIECfgClass *apc = ASPEED_PCIE_CFG_GET_CLASS(cfg);
>>>> + const AspeedPCIERcRegs *rc_regs;
>>>
>>> I suggest you cache &apc->reg_map->rc under AspeedPCIECfgState as an
>>> attribute (at realize time). This will ease reading the code and
>>> improve performance.
>>>
>> Thanks for your review and suggestion.
>> Will do.
>>
>>>> + bool intx;
>>>> +
>>>> + assert(irq < PCI_NUM_PINS);
>>>> +
>>>> + rc_regs = &apc->reg_map->rc;
>>>> +
>>>> + if (level) {
>>>> + cfg->regs[rc_regs->int_sts_reg] |= BIT(irq);
>>>> + } else {
>>>> + cfg->regs[rc_regs->int_sts_reg] &= ~BIT(irq);
>>>> + }
>>>> +
>>>> + intx = !!(cfg->regs[rc_regs->int_sts_reg] &
>>> cfg->regs[rc_regs->int_en_reg]);
>>>> + trace_aspeed_pcie_rc_intx_set_irq(cfg->id, irq, intx);
>>>> + qemu_set_irq(rc->irq, intx);
>>>> +}
>>>> +
>>>> +static int aspeed_pcie_rc_map_irq(PCIDevice *pci_dev, int irq_num) {
>>>> + return irq_num % PCI_NUM_PINS;
>>>> +}
>>>> +
>>>> +static void aspeed_pcie_rc_realize(DeviceState *dev, Error **errp) {
>>>> + PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev);
>>>> + AspeedPCIERcState *rc = ASPEED_PCIE_RC(dev);
>>>> + AspeedPCIECfgState *cfg =
>>>> + container_of(rc, AspeedPCIECfgState, rc);
>>>> + PCIHostState *pci = PCI_HOST_BRIDGE(dev);
>>>> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>>>> + g_autofree char *name;
>>>> +
>>>> + /* PCI configuration space */
>>>> + pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX);
>>>> + sysbus_init_mmio(sbd, &pex->mmio);
>>>> +
>>>> + /* MMIO and IO region */
>>>> + memory_region_init(&rc->mmio, OBJECT(rc), "mmio",
>> UINT64_MAX);
>>>> + memory_region_init(&rc->io, OBJECT(rc), "io", 0x10000);
>>>> +
>>>> + name = g_strdup_printf("pcie.%d.mmio_window", cfg->id);
>>>> + memory_region_init_io(&rc->mmio_window, OBJECT(rc),
>>> &unassigned_io_ops,
>>>> + OBJECT(rc), name, UINT64_MAX);
>>>> + name = g_strdup_printf("pcie.%d.ioport_window", cfg->id);
>>>> + memory_region_init_io(&rc->io_window, OBJECT(rc),
>>> &unassigned_io_ops,
>>>> + OBJECT(rc), name, 0x10000);
>>>> +
>>>> + memory_region_add_subregion(&rc->mmio_window, 0,
>> &rc->mmio);
>>>> + memory_region_add_subregion(&rc->io_window, 0, &rc->io);
>>>> + sysbus_init_mmio(sbd, &rc->mmio_window);
>>>> + sysbus_init_mmio(sbd, &rc->io_window);
>>>> +
>>>> + sysbus_init_irq(sbd, &rc->irq);
>>>> + name = g_strdup_printf("pcie.rc%d", cfg->id);
>>>> + pci->bus = pci_register_root_bus(dev, name, aspeed_pcie_rc_set_irq,
>>>> + aspeed_pcie_rc_map_irq,
>> rc,
>>> &rc->mmio,
>>>> + &rc->io, 0, 4,
>>> TYPE_PCIE_BUS);
>>>> + pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; }
>>>> +
>>>> +static const char *aspeed_pcie_rc_root_bus_path(PCIHostState
>>> *host_bridge,
>>>> + PCIBus
>> *rootbus)
>>> {
>>>> + AspeedPCIERcState *rc = ASPEED_PCIE_RC(host_bridge);
>>>> + AspeedPCIECfgState *cfg =
>>>> + container_of(rc, AspeedPCIECfgState, rc);
>>>> +
>>>> + snprintf(rc->name, sizeof(rc->name), "%04x:%02x", cfg->id,
>>>> + rc->bus_nr);
>>>> +
>>>> + return rc->name;
>>>> +}
>>>> +
>>>> +static const Property aspeed_pcie_rc_props[] = {
>>>> + DEFINE_PROP_UINT32("bus-nr", AspeedPCIERcState, bus_nr, 0), };
>>>> +
>>>> +static void aspeed_pcie_rc_class_init(ObjectClass *klass, const
>>>> +void
>>>> +*data) {
>>>> + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
>>>> + DeviceClass *dc = DEVICE_CLASS(klass);
>>>> +
>>>> + dc->desc = "ASPEED PCIe RC";
>>>> + dc->realize = aspeed_pcie_rc_realize;
>>>> + dc->fw_name = "pci";
>>>> + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
>>>> +
>>>> + hc->root_bus_path = aspeed_pcie_rc_root_bus_path;
>>>> + device_class_set_props(dc, aspeed_pcie_rc_props);
>>>> +
>>>> + msi_nonbroken = true;
>>>> +}
>>>> +
>>>> +static const TypeInfo aspeed_pcie_rc_info = {
>>>> + .name = TYPE_ASPEED_PCIE_RC,
>>>> + .parent = TYPE_PCIE_HOST_BRIDGE,
>>>> + .instance_size = sizeof(AspeedPCIERcState),
>>>> + .class_init = aspeed_pcie_rc_class_init, };
>>>> +
>>>> +/*
>>>> + * PCIe Config
>>>> + *
>>>> + * AHB to PCIe Bus Bridge (H2X)
>>>> + *
>>>> + * On the AST2600:
>>>> + * NOTE: rc_l is not supported by this model.
>>>> + * - Registers 0x00 - 0x7F are shared by both PCIe0 (rc_l) and PCIe1 (rc_h).
>>>> + * - Registers 0x80 - 0xBF are specific to PCIe0.
>>>> + * - Registers 0xC0 - 0xFF are specific to PCIe1.
>>>> + */
>>>> +
>>>> +/* AST2600 */
>>>> +REG32(H2X_CTRL, 0x00)
>>>> + FIELD(H2X_CTRL, CLEAR_RX, 4, 1)
>>>> +REG32(H2X_TX_CLEAR, 0x08)
>>>> + FIELD(H2X_TX_CLEAR, IDLE, 0, 1)
>>>> +REG32(H2X_RDATA, 0x0C)
>>>> +REG32(H2X_TX_DESC0, 0x10)
>>>> +REG32(H2X_TX_DESC1, 0x14)
>>>> +REG32(H2X_TX_DESC2, 0x18)
>>>> +REG32(H2X_TX_DESC3, 0x1C)
>>>> +REG32(H2X_TX_DATA, 0x20)
>>>> +REG32(H2X_TX_STS, 0x24)
>>>> + FIELD(H2X_TX_STS, IDLE, 31, 1)
>>>> + FIELD(H2X_TX_STS, RC_L_TX_COMP, 24, 1)
>>>> + FIELD(H2X_TX_STS, RC_H_TX_COMP, 25, 1)
>>>> + FIELD(H2X_TX_STS, TRIG, 0, 1)
>>>> +REG32(H2X_RC_H_CTRL, 0xC0)
>>>> +REG32(H2X_RC_H_INT_EN, 0xC4)
>>>> +REG32(H2X_RC_H_INT_STS, 0xC8)
>>>> + SHARED_FIELD(H2X_RC_INT_INTDONE, 4, 1)
>>>> + SHARED_FIELD(H2X_RC_INT_INTX, 0, 4)
>>>> +REG32(H2X_RC_H_RDATA, 0xCC)
>>>> +
>>>> +#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
>>>> +#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
>>>> +#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
>>>> +#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
>>>> +
>>>> +#define PCIE_CFG_FMTTYPE_MASK(x) (((x) >> 24) & 0xff) #define
>>>> +PCIE_CFG_BYTE_EN(x) ((x) & 0xf)
>>>> +
>>>> +static const AspeedPCIERegMap aspeed_regmap = {
>>>> + .rc = {
>>>> + .int_en_reg = R_H2X_RC_H_INT_EN,
>>>> + .int_sts_reg = R_H2X_RC_H_INT_STS,
>>>> + },
>>>> +};
>>>> +
>>>> +static uint64_t aspeed_pcie_cfg_read(void *opaque, hwaddr addr,
>>>> + unsigned int size) {
>>>> + AspeedPCIECfgState *s = ASPEED_PCIE_CFG(opaque);
>>>> + uint32_t reg = addr >> 2;
>>>> + uint32_t value = 0;
>>>> +
>>>> + value = s->regs[reg];
>>>> +
>>>> + trace_aspeed_pcie_cfg_read(s->id, addr, value);
>>>> +
>>>> + return value;
>>>> +}
>>>> +
>>>> +static void aspeed_pcie_cfg_translate_write(uint8_t byte_en,
>>>> +uint32_t
>>> *addr,
>>>> + uint64_t *val, int
>>> *len)
>>>> +{
>>>> + uint64_t packed_val = 0;
>>>> + int first_bit = -1;
>>>> + int index = 0;
>>>> + int i;
>>>> +
>>>> + *len = ctpop8(byte_en);
>>>> +
>>>> + if (*len == 0 || *len > 4) {
>>>> + goto err;
>>>> + }
>>>> +
>>>> + /* Special case: full 4-byte write must be 4-byte aligned */
>>>> + if (byte_en == 0x0f) {
>>>> + if (*addr % 4 != 0) {
>>>
>>> This is an aligment issue to be reported as such and not with "invalid
>>> byte enable"
>>>
>>> I think you should remove the "goto err" and generate a
>>> LOG_GUEST_ERROR instead.
>>>
>> Will fix it.
>>>> + goto err;
>>>> + }
>>>> + *val = *val & 0xffffffff;
>>>> + return;
>>>> + }
>>>> +
>>>> + for (i = 0; i < 4; i++) {
>>>> + if (byte_en & (1 << i)) {
>>>> + if (first_bit < 0) {
>>>> + first_bit = i;
>>>> + }
>>>> + packed_val |= ((*val >> (i * 8)) & 0xff) << (index * 8);
>>>> + index++;> + }
>>>> + }
>>>> +
>>>> + *addr += first_bit;
>>>> + *val = packed_val;
>>>> +
>>>> + return;
>>>> +
>>>> +err:
>>>> + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid byte enable:
>>> 0x%x\n",
>>>> + __func__, byte_en); }
>>>> +
>>>> +static void aspeed_pcie_cfg_readwrite(AspeedPCIECfgState *s,
>>>> + const
>> AspeedPCIECfgTxDesc
>>>> +*desc) {
>>>> + AspeedPCIERcState *rc = &s->rc;
>>>> + PCIHostState *pci;
>>>> + uint32_t cfg_addr;
>>>> + PCIDevice *pdev;
>>>> + uint32_t offset;
>>>> + uint8_t byte_en;
>>>> + bool is_write;
>>>> + uint8_t devfn;
>>>> + uint64_t val;
>>>> + uint8_t bus;
>>>> + int len;
>>>> +
>>>> + val = ~0;
>>>> + is_write = !!(desc->desc0 & BIT(30));
>>>> + cfg_addr = desc->desc2;
>>>
>>>
>>> hmm, what about endianess ?
>>>
>
> We keep .endianness = DEVICE_LITTLE_ENDIAN on the MMIO region, so the guest wire-LE bytes are already converted at the MMIO boundary.
> Inside the device (s->regs[], FIFOs, and desc.*) we consistently use CPU-endian integers and apply bitfield operations on those.
>
> I tried adding explicit le32_to_cpu()/cpu_to_le32() on s->regs[] to “convert” them again to CPU-endian,
> but that caused a "double swap" on big-endian hosts (e.g. MIPS64). The result was broken config TLP decoding and incorrect byte-enable packing.
>
> I experimented with the patch below—does this look incorrect given the MMIO endianness model?
> case R_H2X_TX_STS:
> if (data & R_H2X_TX_STS_TRIG_MASK) {
> - desc.desc0 = s->regs[R_H2X_TX_DESC0];
> - desc.desc1 = s->regs[R_H2X_TX_DESC1];
> - desc.desc2 = s->regs[R_H2X_TX_DESC2];
> - desc.desc3 = s->regs[R_H2X_TX_DESC3];
> - desc.wdata = s->regs[R_H2X_TX_DATA];
> + desc.desc0 = le32_to_cpu(s->regs[R_H2X_TX_DESC0]);
> + desc.desc1 = le32_to_cpu(s->regs[R_H2X_TX_DESC1]);
> + desc.desc2 = le32_to_cpu(s->regs[R_H2X_TX_DESC2]);
> + desc.desc3 = le32_to_cpu(s->regs[R_H2X_TX_DESC3]);
> + desc.wdata = le32_to_cpu(s->regs[R_H2X_TX_DATA]);
> desc.rdata_reg = R_H2X_RC_H_RDATA;
> aspeed_pcie_cfg_readwrite(s, &desc);
> ...
>
> On MIPS64 (BE) this change made the device non-functional, which aligns with the double-swap hypothesis.
> My understanding is we should not add these conversions and instead keep s->regs[]/desc.* strictly CPU-endian, relying on the MMIO layer for wire-LE ↔ CPU conversions.
>
> I tested the v2 patch series and it passes on a big-endian host machine (qemumips64)
> Does anything above look incorrect to you?
My bad. Conversions are no needed since the descriptors come from
register values, and not RAM :/
A e1000e device works find on a BE host :
10: eth4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 52:54:00:12:34:59 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth4
valid_lft 86279sec preferred_lft 86279sec
inet6 fe80::5054:ff:fe12:3459/64 scope link
valid_lft forever preferred_lft forever
root@ast2600-default:~# grep eth4 /proc/interrupts
82: 155 0 PCIe MSI 67633152 Edge eth4-rx-0
83: 139 0 PCIe MSI 67633153 Edge eth4-tx-0
84: 4 0 PCIe MSI 67633154 Edge eth4
root@ast2600-default:~# dmesg | grep e1000e
[ 1.349634] e1000e: Intel(R) PRO/1000 Network Driver
[ 1.350111] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
[ 12.940541] e1000e 0000:81:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[ 13.008532] e1000e 0000:81:00.0 0000:81:00.0 (uninitialized): registered PHC clock
[ 13.079534] e1000e 0000:81:00.0 eth4: (PCI Express:2.5GT/s:Width x1) 52:54:00:12:34:59
[ 13.080149] e1000e 0000:81:00.0 eth4: Intel(R) PRO/1000 Network Connection
[ 13.080612] e1000e 0000:81:00.0 eth4: MAC: 3, PHY: 8, PBA No: 000000-000
[ 38.428521] e1000e 0000:81:00.0 eth4: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: Rx/Tx
Thanks for looking.
C.
^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2025-09-17 10:22 UTC | newest]
Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-11 7:24 [PATCH v2 00/14] Support PCIe RC to AST2600 and AST2700 Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 01/14] hw/pci/pci_ids Add PCI vendor ID for ASPEED Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 02/14] hw/pci-host/aspeed: Add AST2600 PCIe PHY model Jamin Lin via
2025-09-15 9:20 ` [SPAM] " Cédric Le Goater
2025-09-16 5:32 ` Jamin Lin
2025-09-11 7:24 ` [PATCH v2 03/14] hw/pci-host/aspeed: Add AST2600 PCIe config space and host bridge Jamin Lin via
2025-09-15 16:51 ` [SPAM] " Cédric Le Goater
2025-09-17 1:45 ` Jamin Lin
2025-09-17 9:05 ` Jamin Lin
2025-09-17 10:21 ` Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 04/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Device support Jamin Lin via
2025-09-15 16:53 ` [SPAM] " Cédric Le Goater
2025-09-16 2:42 ` Jamin Lin
2025-09-11 7:24 ` [PATCH v2 05/14] hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable Jamin Lin via
2025-09-15 16:54 ` [SPAM] " Cédric Le Goater
2025-09-16 2:51 ` Jamin Lin
2025-09-11 7:24 ` [PATCH v2 06/14] hw/pci-host/aspeed: Add MSI support and per-RC IOMMU address space Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 07/14] hw/arm/aspeed: Wire up PCIe devices in SoC model Jamin Lin via
2025-09-15 17:14 ` [SPAM] " Cédric Le Goater
2025-09-11 7:24 ` [PATCH v2 08/14] hw/arm/aspeed_ast2600: Add PCIe RC support (RC_H only) Jamin Lin via
2025-09-15 17:19 ` [SPAM] " Cédric Le Goater
2025-09-16 3:41 ` Jamin Lin
2025-09-11 7:24 ` [PATCH v2 09/14] hw/pci-host/aspeed: Add AST2700 PCIe PHY Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 10/14] hw/pci-host/aspeed: Add AST2700 PCIe config with dedicated H2X blocks Jamin Lin via
2025-09-15 17:23 ` [SPAM] " Cédric Le Goater
2025-09-17 1:48 ` Jamin Lin
2025-09-11 7:24 ` [PATCH v2 11/14] hw/pci-host/aspeed: Disable Root Device and place Root Port at 00:00.0 to AST2700 Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 12/14] hw/arm/aspeed_ast27x0: Introduce 3 PCIe RCs for AST2700 Jamin Lin via
2025-09-11 7:24 ` [PATCH v2 13/14] tests/functional/arm/test_aspeed_ast2600: add PCIe enumeration test Jamin Lin via
2025-09-15 17:21 ` [SPAM] " Cédric Le Goater
2025-09-16 7:30 ` Jamin Lin
2025-09-16 7:41 ` Cédric Le Goater
2025-09-16 7:44 ` Jamin Lin
2025-09-11 7:24 ` [PATCH v2 14/14] tests/functional/aarch64/aspeed_ast2700: " Jamin Lin via
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).