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 11/19] hw/net/bcm2838_genet: implement GENET MDIO
Date: Tue, 11 Aug 2026 10:35:48 -0400 [thread overview]
Message-ID: <20260811143557.7862-12-marcelomanzo@gmail.com> (raw)
In-Reply-To: <20260811143557.7862-1-marcelomanzo@gmail.com>
From: Sergey Kambalin <serg.oker@gmail.com>
Implement the MDIO command register handling used to read/write the
BCM54213PE PHY's standard MII registers (BMCR, BMSR, LPA, ...) plus
its vendor "shadow" register blocks reached through the AUX_CTL/
EXP_SHD indirection scheme, and a simple link-state model (PHY
starts down, comes up once BMCR is programmed and autonegotiation
is (re)started).
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;
see the following "Fix GENET PHY autonegotiation-restart deadlock"
patch for a real bug found and fixed in this code during testing.
Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com>
Signed-off-by: Marcelo Manzo <marcelomanzo@gmail.com>
---
hw/net/bcm2838_genet.c | 132 ++++++++++++++++++++++++++++++++-
include/hw/net/bcm2838_genet.h | 3 +-
2 files changed, 132 insertions(+), 3 deletions(-)
diff --git a/hw/net/bcm2838_genet.c b/hw/net/bcm2838_genet.c
index f3c5ca26c5..6b3c9cb12b 100644
--- a/hw/net/bcm2838_genet.c
+++ b/hw/net/bcm2838_genet.c
@@ -216,6 +216,7 @@ FIELD(GENET_PHY_STAT_1000, LOCALRXOK, 13, 1)
FIELD(GENET_PHY_STAT_1000, MSRES, 14, 1)
FIELD(GENET_PHY_STAT_1000, MSFAIL, 15, 1)
+/* There are two data representations for PHY_AUX_CTRL register */
REG16(GENET_PHY_AUX_CTRL_0, 0)
FIELD(GENET_PHY_AUX_CTRL_0, REG_ID_MASK, 0, 3)
FIELD(GENET_PHY_AUX_CTRL_0, RSVD_3, 3, 1)
@@ -272,6 +273,129 @@ static void bcm2838_genet_set_irq_prio(BCM2838GenetState *s)
qemu_set_irq(s->irq_prio, level);
}
+static void bcm2838_genet_phy_aux_ctl_write(BCM2838GenetState *s,
+ uint16_t value)
+{
+ uint16_t reg_id = FIELD_EX16(value, GENET_PHY_AUX_CTRL_0, REG_ID);
+ uint16_t reg_id_mask = FIELD_EX16(value, GENET_PHY_AUX_CTRL_0, REG_ID_MASK);
+ uint16_t misc_wren = FIELD_EX16(value, GENET_PHY_AUX_CTRL_0, MISC_WREN);
+ uint16_t reg_data = FIELD_EX16(value, GENET_PHY_AUX_CTRL_0, REG_DATA);
+ uint16_t reg_data12 = FIELD_EX16(value, GENET_PHY_AUX_CTRL_1, REG_DATA);
+
+ uint16_t *phy_aux_ctl_shd_reg_id =
+ (uint16_t *)&s->phy_aux_ctl_shd_regs + reg_id;
+ uint16_t *phy_aux_ctl_shd_reg_id_mask =
+ (uint16_t *)&s->phy_aux_ctl_shd_regs + reg_id_mask;
+
+ if (reg_id_mask == BCM2838_GENET_PHY_AUX_CTL_MISC) {
+ if (reg_id == BCM2838_GENET_PHY_AUX_CTL_MISC) {
+ if (misc_wren == 0) {
+ /* write for subsequent read (8-bit from AUX_CTL_MISC) */
+ FIELD_DP16(value, GENET_PHY_AUX_CTRL_0, REG_DATA,
+ *phy_aux_ctl_shd_reg_id);
+ } else {
+ /* write 8 bits to AUX_CTL_MISC */
+ *phy_aux_ctl_shd_reg_id_mask = reg_data;
+ }
+ } else {
+ /* write for subsequent read (12-bit) */
+ FIELD_DP16(value, GENET_PHY_AUX_CTRL_1, REG_DATA,
+ *phy_aux_ctl_shd_reg_id);
+ }
+ } else {
+ /* write 12 bits */
+ *phy_aux_ctl_shd_reg_id_mask = reg_data12;
+ }
+
+ s->phy_regs.aux_ctl = value;
+}
+
+static void bcm2838_genet_phy_shadow_write(BCM2838GenetState *s,
+ uint16_t value)
+{
+ uint16_t reg_id = FIELD_EX16(value, GENET_PHY_SHADOW, REG_ID);
+ uint16_t wr = FIELD_EX16(value, GENET_PHY_SHADOW, WR);
+ uint16_t reg_data = FIELD_EX16(value, GENET_PHY_SHADOW, REG_DATA);
+
+ uint16_t *phy_shd_reg = (uint16_t *)&s->phy_shd_regs + reg_id;
+
+ if (wr == 0) {
+ FIELD_DP16(value, GENET_PHY_SHADOW, REG_DATA, *phy_shd_reg);
+ } else {
+ *phy_shd_reg = reg_data;
+ }
+
+ s->phy_regs.shd = value;
+}
+
+static void bcm2838_genet_phy_exp_shadow_write(BCM2838GenetState *s,
+ uint16_t value)
+{
+ /*
+ * TODO Stub implementation without side effect,
+ * just storing registers values
+ */
+ uint16_t reg_id = FIELD_EX16(s->phy_regs.exp_ctrl,
+ GENET_PHY_EXP_SEL, REG_ID);
+ uint16_t block_id = FIELD_EX16(s->phy_regs.exp_ctrl,
+ GENET_PHY_EXP_SEL, BLOCK_ID);
+
+ s->phy_exp_shd_regs.regs[block_id][reg_id] = value;
+}
+
+static uint16_t bcm2838_genet_phy_exp_shadow_read(BCM2838GenetState *s)
+{
+ uint16_t reg_id = FIELD_EX16(s->phy_regs.exp_ctrl,
+ GENET_PHY_EXP_SEL, REG_ID);
+ uint16_t block_id = FIELD_EX16(s->phy_regs.exp_ctrl,
+ GENET_PHY_EXP_SEL, BLOCK_ID);
+
+ return s->phy_exp_shd_regs.regs[block_id][reg_id];
+}
+
+static uint64_t bcm2838_genet_mdio_cmd(BCM2838GenetState *s, uint64_t cmd)
+{
+ uint32_t phy_reg_id = FIELD_EX32(cmd, GENET_UMAC_MDIO_CMD, REG_ID);
+ uint32_t phy_reg_data = FIELD_EX32(cmd, GENET_UMAC_MDIO_CMD, REG_DATA);
+ uint32_t start_busy = FIELD_EX32(cmd, GENET_UMAC_MDIO_CMD, START_BUSY);
+ uint32_t rd = FIELD_EX32(cmd, GENET_UMAC_MDIO_CMD, RD);
+ uint32_t wr = FIELD_EX32(cmd, GENET_UMAC_MDIO_CMD, WR);
+ uint16_t *phy_reg = (uint16_t *)&s->phy_regs + phy_reg_id;
+
+ uint16_t anrestart = FIELD_EX16(phy_reg_data, GENET_PHY_BMCR, ANRESTART);
+
+ if (start_busy != 0) {
+ cmd = FIELD_DP32(cmd, GENET_UMAC_MDIO_CMD, START_BUSY, 0);
+
+ if (rd != 0) {
+ if (phy_reg_id == BCM2838_GENET_EXP_DATA) {
+ cmd = FIELD_DP32(cmd, GENET_UMAC_MDIO_CMD, REG_DATA,
+ bcm2838_genet_phy_exp_shadow_read(s));
+ } else {
+ cmd = FIELD_DP32(cmd, GENET_UMAC_MDIO_CMD, REG_DATA, *phy_reg);
+ }
+ } else if (wr != 0) {
+ if (phy_reg_id == BCM2838_GENET_PHY_AUX_CTL) {
+ bcm2838_genet_phy_aux_ctl_write(s, phy_reg_data);
+ } else if (phy_reg_id == BCM2838_GENET_PHY_SHD) {
+ bcm2838_genet_phy_shadow_write(s, phy_reg_data);
+ } else if (phy_reg_id == BCM2838_GENET_EXP_DATA) {
+ bcm2838_genet_phy_exp_shadow_write(s, phy_reg_data);
+ } else {
+ if (phy_reg_id == BCM2838_GENET_PHY_BMCR) {
+ /* Initiate auto-negotiation once it has been restarted */
+ if (anrestart == 1) {
+ FIELD_DP16(phy_reg_data, GENET_PHY_BMCR, ANRESTART, 0);
+ }
+ }
+ *phy_reg = phy_reg_data;
+ }
+ }
+ }
+
+ return cmd;
+}
+
static uint64_t bcm2838_genet_read(void *opaque, hwaddr offset, unsigned size)
{
uint64_t value = ~0;
@@ -359,10 +483,13 @@ static void bcm2838_genet_write(void *opaque, hwaddr offset, uint64_t value,
s->regs.rdma.status = (~value) & GENET_DMA_ENABLE_MASK;
break;
case BCM2838_GENET_UMAC_MDIO_CMD:
+ value = bcm2838_genet_mdio_cmd(s, value);
+ s->regs.intrl0.stat = FIELD_DP32(s->regs.intrl0.stat,
+ GENET_INTRL_0, MDIO_DONE, 1);
+ break;
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");
+ qemu_log_mask(LOG_UNIMP, "TDMA isn't implemented yet");
break;
default:
break;
@@ -464,6 +591,7 @@ static void bcm2838_genet_reset(Object *obj, ResetType type)
trace_bcm2838_genet_reset("done");
+ bcm2838_genet_set_qemu_mac(s);
bcm2838_genet_phy_reset(s);
}
diff --git a/include/hw/net/bcm2838_genet.h b/include/hw/net/bcm2838_genet.h
index e3dee5a78f..b34c4b3ce7 100644
--- a/include/hw/net/bcm2838_genet.h
+++ b/include/hw/net/bcm2838_genet.h
@@ -103,7 +103,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(BCM2838GenetState, BCM2838_GENET)
#define BCM2838_GENET_EXP_DATA BCM2838_GENET_PHY_REG(exp_data)
#define BCM2838_GENET_EXP_SEL BCM2838_GENET_PHY_REG(exp_ctrl)
-#define BCM2838_GENET_PHY_AUX_CTL_MISC 0x7
+#define BCM2838_GENET_PHY_AUX_CTL_AUXCTL 0x0
+#define BCM2838_GENET_PHY_AUX_CTL_MISC 0x7
#define BCM2838_GENET_PHY_AUX_CTL_REGS_SIZE 8
#define BCM2838_GENET_PHY_EXP_SHD_BLOCKS_CNT 256
--
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 ` [PATCH v2 10/19] hw/net/bcm2838_genet: implement GENET register ops Marcelo Manzo
2026-08-11 14:35 ` Marcelo Manzo [this message]
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-12-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox