From: Marcelo Manzo <marcelomanzo@gmail.com>
To: qemu-devel@nongnu.org, qemu-arm@nongnu.org
Cc: "Peter Maydell" <peter.maydell@linaro.org>,
"Philippe Mathieu-Daudé" <philmd@oss.qualcomm.com>,
"Sergey Kambalin" <serg.oker@gmail.com>,
"Sergey Kambalin" <sergey.kambalin@auriga.com>,
"Marcelo Manzo" <marcelomanzo@gmail.com>,
"Philippe Mathieu-Daudé" <philmd@mailo.com>,
"Jason Wang" <jasowangio@gmail.com>
Subject: [PATCH v2 10/19] hw/net/bcm2838_genet: implement GENET register ops
Date: Tue, 11 Aug 2026 10:35:47 -0400 [thread overview]
Message-ID: <20260811143557.7862-11-marcelomanzo@gmail.com> (raw)
In-Reply-To: <20260811143557.7862-1-marcelomanzo@gmail.com>
From: Sergey Kambalin <serg.oker@gmail.com>
Replace the stub MMIO read/write handlers with real dispatch over
the register offsets from the previous patches: SYS revision
control, INTRL0/1 status/mask registers, UMAC command/MAC-address/
MDIO-command registers, and TDMA/RDMA ring configuration, wired up
to the NIC's realize()/reset() and qdev NIC properties
(DEFINE_NIC_PROPERTIES) so the device gets a MAC address and peer
netdev from the command line.
Part of Sergey Kambalin's original Raspberry Pi 4B PCIe/GENET
networking series (patchwork series 829638, posted to qemu-devel in
2024, never merged). Carried forward and completed by Marcelo Manzo:
adapted to the current Property/class_init/Resettable APIs, with no
functional change to the register dispatch logic itself.
Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com>
Signed-off-by: Marcelo Manzo <marcelomanzo@gmail.com>
---
hw/net/bcm2838_genet.c | 197 +++++++++++++++++++++++++++++++--
include/hw/net/bcm2838_genet.h | 2 +
2 files changed, 190 insertions(+), 9 deletions(-)
diff --git a/hw/net/bcm2838_genet.c b/hw/net/bcm2838_genet.c
index 2bc2a770b1..f3c5ca26c5 100644
--- a/hw/net/bcm2838_genet.c
+++ b/hw/net/bcm2838_genet.c
@@ -236,25 +236,151 @@ REG16(GENET_PHY_EXP_SEL, 0)
FIELD(GENET_PHY_EXP_SEL, REG_ID, 0, 8)
FIELD(GENET_PHY_EXP_SEL, BLOCK_ID, 8, 8)
+static void bcm2838_genet_set_qemu_mac(BCM2838GenetState *s)
+{
+ const MACAddr *addr = &s->nic_conf.macaddr;
+
+ s->regs.umac.mac0 = FIELD_DP32(s->regs.umac.mac0, GENET_UMAC_MAC_0,
+ ADDR_0, addr->a[0]);
+ s->regs.umac.mac0 = FIELD_DP32(s->regs.umac.mac0, GENET_UMAC_MAC_0,
+ ADDR_1, addr->a[1]);
+ s->regs.umac.mac0 = FIELD_DP32(s->regs.umac.mac0, GENET_UMAC_MAC_0,
+ ADDR_2, addr->a[2]);
+ s->regs.umac.mac0 = FIELD_DP32(s->regs.umac.mac0, GENET_UMAC_MAC_0,
+ ADDR_3, addr->a[3]);
+ s->regs.umac.mac1 = FIELD_DP32(s->regs.umac.mac1, GENET_UMAC_MAC_1,
+ ADDR_4, addr->a[4]);
+ s->regs.umac.mac1 = FIELD_DP32(s->regs.umac.mac1, GENET_UMAC_MAC_1,
+ ADDR_5, addr->a[5]);
+}
+
+static void bcm2838_genet_set_irq_default(BCM2838GenetState *s)
+{
+ uint32_t intrl_0_status = s->regs.intrl0.stat;
+ uint32_t intrl_0_mask = s->regs.intrl0.mask_status;
+ int level = (intrl_0_status & ~intrl_0_mask) == 0 ? 0 : 1;
+
+ qemu_set_irq(s->irq_default, level);
+}
+
+static void bcm2838_genet_set_irq_prio(BCM2838GenetState *s)
+{
+ uint32_t intrl_1_status = s->regs.intrl1.stat;
+ uint32_t intrl_1_mask = s->regs.intrl1.mask_status;
+ int level = (intrl_1_status & ~intrl_1_mask) == 0 ? 0 : 1;
+
+ qemu_set_irq(s->irq_prio, level);
+}
+
static uint64_t bcm2838_genet_read(void *opaque, hwaddr offset, unsigned size)
{
uint64_t value = ~0;
+ BCM2838GenetState *s = opaque;
- qemu_log_mask(
- LOG_GUEST_ERROR,
- "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
- __func__, size, offset);
+ if (offset + size < sizeof(s->regs)) {
+ memcpy(&value, (uint8_t *)&s->regs + offset, size);
+ } else {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
+ __func__, size, offset);
+ }
trace_bcm2838_genet_read(size, offset, value);
return value;
}
static void bcm2838_genet_write(void *opaque, hwaddr offset, uint64_t value,
- unsigned size) {
- qemu_log_mask(
- LOG_GUEST_ERROR,
- "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
- __func__, size, offset);
+ unsigned size)
+{
+ BCM2838GenetState *s = opaque;
+ MACAddr *mac = &s->nic_conf.macaddr;
+ NetClientState *ncs = qemu_get_queue(s->nic);
+
+ trace_bcm2838_genet_write(size, offset, value);
+
+ if (offset + size < sizeof(s->regs)) {
+ switch (offset) {
+ case BCM2838_GENET_INTRL0_SET:
+ s->regs.intrl0.stat |= value;
+ break;
+ case BCM2838_GENET_INTRL0_CLEAR:
+ s->regs.intrl0.stat &= ~value;
+ break;
+ case BCM2838_GENET_INTRL0_MASK_SET:
+ s->regs.intrl0.mask_status |= value;
+ break;
+ case BCM2838_GENET_INTRL0_MASK_CLEAR:
+ s->regs.intrl0.mask_status &= ~value;
+ break;
+ case BCM2838_GENET_INTRL1_SET:
+ s->regs.intrl1.stat |= value;
+ break;
+ case BCM2838_GENET_INTRL1_CLEAR:
+ s->regs.intrl1.stat &= ~value;
+ break;
+ case BCM2838_GENET_INTRL1_MASK_SET:
+ s->regs.intrl1.mask_status |= value;
+ break;
+ case BCM2838_GENET_INTRL1_MASK_CLEAR:
+ s->regs.intrl1.mask_status &= ~value;
+ break;
+ case BCM2838_GENET_UMAC_CMD:
+ /* Complete SW reset as soon as it has been requested */
+ if (FIELD_EX32(value, GENET_UMAC_CMD, SW_RESET) == 1) {
+ device_cold_reset(DEVICE(s));
+ value = FIELD_DP32(value, GENET_UMAC_CMD, SW_RESET, 0);
+ }
+ break;
+ /*
+ * TODO: before changing MAC address we'd better inform QEMU
+ * network subsystem about freeing previously used one, but
+ * qemu_macaddr_set_free function isn't accessible for us (marked
+ * as static in net/net.c), see also https://lists.nongnu.org/
+ * archive/html/qemu-devel/2022-07/msg02123.html
+ */
+ case BCM2838_GENET_UMAC_MAC0:
+ mac->a[0] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_0);
+ mac->a[1] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_1);
+ mac->a[2] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_2);
+ mac->a[3] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_3);
+ qemu_macaddr_default_if_unset(mac);
+ qemu_format_nic_info_str(ncs, mac->a);
+ trace_bcm2838_genet_mac_address(ncs->info_str);
+ break;
+ case BCM2838_GENET_UMAC_MAC1:
+ mac->a[4] = FIELD_EX32(value, GENET_UMAC_MAC_1, ADDR_4);
+ mac->a[5] = FIELD_EX32(value, GENET_UMAC_MAC_1, ADDR_5);
+ qemu_macaddr_default_if_unset(mac);
+ qemu_format_nic_info_str(ncs, mac->a);
+ trace_bcm2838_genet_mac_address(ncs->info_str);
+ break;
+ case BCM2838_GENET_RDMA_CTRL:
+ s->regs.rdma.status = (~value) & GENET_DMA_ENABLE_MASK;
+ break;
+ case BCM2838_GENET_UMAC_MDIO_CMD:
+ case BCM2838_GENET_TDMA_REGS
+ ... BCM2838_GENET_TDMA_REGS + sizeof(BCM2838GenetRegsTdma) - 1:
+ qemu_log_mask(LOG_UNIMP,
+ "UMAC MDIO and TDMA aren't implemented yet");
+ break;
+ default:
+ break;
+ }
+
+ memcpy((uint8_t *)&s->regs + offset, &value, size);
+ if (offset == BCM2838_GENET_RDMA_CTRL &&
+ FIELD_EX32(value, GENET_DMA_CTRL, EN)) {
+ qemu_flush_queued_packets(ncs);
+ }
+ bcm2838_genet_set_irq_default(s);
+ bcm2838_genet_set_irq_prio(s);
+ } else {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
+ __func__, size, offset);
+ }
}
static const MemoryRegionOps bcm2838_genet_ops = {
@@ -265,9 +391,14 @@ static const MemoryRegionOps bcm2838_genet_ops = {
.valid = {.min_access_size = 4},
};
+static NetClientInfo bcm2838_genet_client_info = {
+ .type = NET_CLIENT_DRIVER_NIC,
+ .size = sizeof(NICState)
+};
static void bcm2838_genet_realize(DeviceState *dev, Error **errp)
{
+ NetClientState *ncs;
BCM2838GenetState *s = BCM2838_GENET(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
@@ -275,10 +406,46 @@ static void bcm2838_genet_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&s->regs_mr, OBJECT(s), &bcm2838_genet_ops, s,
"bcm2838_genet_regs", sizeof(s->regs));
sysbus_init_mmio(sbd, &s->regs_mr);
+
+ /* QEMU-managed NIC (host network back-end connection) */
+ qemu_macaddr_default_if_unset(&s->nic_conf.macaddr);
+ s->nic = qemu_new_nic(&bcm2838_genet_client_info, &s->nic_conf,
+ object_get_typename(OBJECT(dev)), dev->id,
+ &dev->mem_reentrancy_guard, s);
+ bcm2838_genet_set_qemu_mac(s);
+ ncs = qemu_get_queue(s->nic);
+ qemu_format_nic_info_str(ncs, s->nic_conf.macaddr.a);
+ trace_bcm2838_genet_mac_address(ncs->info_str);
+
+ /* Interrupts */
+ sysbus_init_irq(sbd, &s->irq_default);
+ sysbus_init_irq(sbd, &s->irq_prio);
+
+ /* DMA space */
+ address_space_init(&s->dma_as, get_system_memory(), "bcm2838_genet_dma");
}
static void bcm2838_genet_phy_reset(BCM2838GenetState *s)
{
+ memset(&s->phy_regs, 0x00, sizeof(s->phy_regs));
+ memset(&s->phy_shd_regs, 0x00, sizeof(s->phy_shd_regs));
+ memset(&s->phy_aux_ctl_shd_regs, 0x00, sizeof(s->phy_aux_ctl_shd_regs));
+
+ /* All values below were taken from real HW trace and logs */
+ s->phy_regs.bmcr = 0x1140;
+ s->phy_regs.bmsr = 0x7949;
+ s->phy_regs.sid1 = 0x600D;
+ s->phy_regs.sid2 = 0x84A2;
+ s->phy_regs.advertise = 0x01E1;
+ s->phy_regs.ctrl1000 = 0x0200;
+ s->phy_regs.estatus = 0x3000;
+
+ s->phy_shd_regs.clk_ctl = 0x0200;
+ s->phy_shd_regs.scr3 = 0x001F;
+ s->phy_shd_regs.apd = 0x0001;
+
+ s->phy_aux_ctl_shd_regs.misc = 0x1E;
+
trace_bcm2838_genet_phy_reset("done");
}
@@ -288,11 +455,22 @@ static void bcm2838_genet_reset(Object *obj, ResetType type)
memset(&s->regs, 0x00, sizeof(s->regs));
+ s->regs.sys.rev_ctrl = FIELD_DP32(s->regs.sys.rev_ctrl, GENET_SYS_REV_CTRL,
+ MAJOR_REV, BCM2838_GENET_REV_MAJOR);
+ s->regs.sys.rev_ctrl = FIELD_DP32(s->regs.sys.rev_ctrl, GENET_SYS_REV_CTRL,
+ MINOR_REV, BCM2838_GENET_REV_MINOR);
+ s->regs.rdma.status = GENET_DMA_ENABLE_MASK;
+ s->regs.tdma.status = GENET_DMA_ENABLE_MASK;
+
trace_bcm2838_genet_reset("done");
bcm2838_genet_phy_reset(s);
}
+static const Property genet_properties[] = {
+ DEFINE_NIC_PROPERTIES(BCM2838GenetState, nic_conf),
+};
+
static void bcm2838_genet_class_init(ObjectClass *class, const void *data)
{
static ResettablePhases unused_parent_phases;
@@ -302,6 +480,7 @@ static void bcm2838_genet_class_init(ObjectClass *class, const void *data)
dc->realize = bcm2838_genet_realize;
resettable_class_set_parent_phases(rc, NULL, bcm2838_genet_reset, NULL,
&unused_parent_phases);
+ device_class_set_props(dc, genet_properties);
}
static const TypeInfo bcm2838_genet_info = {
diff --git a/include/hw/net/bcm2838_genet.h b/include/hw/net/bcm2838_genet.h
index b9895933b6..e3dee5a78f 100644
--- a/include/hw/net/bcm2838_genet.h
+++ b/include/hw/net/bcm2838_genet.h
@@ -390,6 +390,8 @@ struct BCM2838GenetState {
SysBusDevice parent_obj;
/*< public >*/
+ NICState *nic;
+ NICConf nic_conf;
MemoryRegion regs_mr;
AddressSpace dma_as;
--
2.47.1
next prev parent reply other threads:[~2026-08-11 14:37 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-11 14:35 [PATCH v2 00/19] hw/arm/raspi4b: working PCIe and GENET (real networking) Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 01/19] hw/arm/bcm2838_pcie: add BCM2838 PCIe Root Complex Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 02/19] hw/arm/bcm2838_pcie: add BCM2838 PCIe host Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 03/19] hw/arm/bcm2838: enable BCM2838 PCIe host bridge Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 04/19] hw/net/bcm2838_genet: add GENET stub device Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 05/19] hw/net/bcm2838_genet: add GENET register structs, part 1/4 Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 06/19] hw/net/bcm2838_genet: add GENET register structs, part 2/4 Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 07/19] hw/net/bcm2838_genet: add GENET register structs, part 3/4 Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 08/19] hw/net/bcm2838_genet: add GENET register structs, part 4/4 Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 09/19] hw/net/bcm2838_genet: add GENET register access macros Marcelo Manzo
2026-08-11 14:35 ` Marcelo Manzo [this message]
2026-08-11 14:35 ` [PATCH v2 11/19] hw/net/bcm2838_genet: implement GENET MDIO Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 12/19] hw/net/bcm2838_genet: implement GENET TX path Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 13/19] hw/net/bcm2838_genet: implement GENET RX path Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 14/19] hw/arm/bcm2838: enable BCM2838 GENET controller Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 15/19] hw/net/bcm2838_genet: fix PHY autonegotiation-restart deadlock Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 16/19] hw/net/bcm2838_genet: fix bogus RX checksum reporting Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 17/19] hw/net/bcm2838_genet: fix TX ring activation check Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 18/19] docs/system/arm/raspi: move PCIe/GENET from missing to implemented Marcelo Manzo
2026-08-11 14:35 ` [PATCH v2 19/19] tests/functional/aarch64: add raspi4b GENET networking test Marcelo Manzo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260811143557.7862-11-marcelomanzo@gmail.com \
--to=marcelomanzo@gmail.com \
--cc=jasowangio@gmail.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@mailo.com \
--cc=philmd@oss.qualcomm.com \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=serg.oker@gmail.com \
--cc=sergey.kambalin@auriga.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.