diff mbox series

[29/44] Impl GENET MDIO

Message ID 20230726132512.149618-30-sergey.kambalin@auriga.com (mailing list archive)
State New, archived
Headers show
Series Raspberry Pi 4B machine | expand

Commit Message

Sergey Kambalin July 26, 2023, 1:24 p.m. UTC
Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com>
---
 hw/net/bcm2838_genet.c | 109 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/hw/net/bcm2838_genet.c b/hw/net/bcm2838_genet.c
index 01e6819df4..e633323d17 100644
--- a/hw/net/bcm2838_genet.c
+++ b/hw/net/bcm2838_genet.c
@@ -47,6 +47,108 @@  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)
+{
+    BCM2838GenetPhyAuxCtl phy_aux_ctl = {.value = value};
+    uint16_t *phy_aux_ctl_shd_reg_id
+        = (uint16_t *)&s->phy_aux_ctl_shd_regs + phy_aux_ctl.fields_1.reg_id;
+    uint16_t *phy_aux_ctl_shd_reg_id_mask
+        = (uint16_t *)&s->phy_aux_ctl_shd_regs + phy_aux_ctl.fields_1.reg_id_mask;
+
+    if (phy_aux_ctl.fields_1.reg_id_mask == BCM2838_GENET_PHY_AUX_CTL_MISC) {
+        if (phy_aux_ctl.fields_1.reg_id == BCM2838_GENET_PHY_AUX_CTL_MISC) {
+            if (phy_aux_ctl.fields_1.misc_wren == 0) {
+                /* write for subsequent read (8-bit from AUX_CTL_MISC) */
+                phy_aux_ctl.fields_1.reg_data = *phy_aux_ctl_shd_reg_id;
+            } else {
+                /* write 8 bits to AUX_CTL_MISC */
+                *phy_aux_ctl_shd_reg_id_mask = phy_aux_ctl.fields_1.reg_data;
+            }
+        } else {
+            /* write for subsequent read (12-bit) */
+            phy_aux_ctl.fields_2.reg_data = *phy_aux_ctl_shd_reg_id;
+        }
+    } else {
+        /* write 12 bits */
+        *phy_aux_ctl_shd_reg_id_mask = phy_aux_ctl.fields_2.reg_data;
+    }
+
+    s->phy_regs.aux_ctl.value = phy_aux_ctl.value;
+}
+
+static void bcm2838_genet_phy_shadow_write(BCM2838GenetState *s,
+                                           uint16_t value)
+{
+    BCM2838GenetPhyShadow phy_shadow = {.value = value};
+    uint16_t *phy_shd_reg
+        = (uint16_t *)&s->phy_shd_regs + phy_shadow.fields.reg_id;
+
+    if (phy_shadow.fields.wr == 0) {
+        phy_shadow.fields.reg_data = *phy_shd_reg;
+    } else {
+        *phy_shd_reg = phy_shadow.fields.reg_data;
+    }
+
+    s->phy_regs.shd.value = phy_shadow.value;
+}
+
+static void bcm2838_genet_phy_exp_shadow_write(BCM2838GenetState *s,
+                                               uint16_t value)
+{
+    /* TODO Stub implementation without side effect,
+            just storing registers values */
+    BCM2838GenetPhyExpSel* exp_ctrl = &s->phy_regs.exp_ctrl;
+    s->phy_exp_shd_regs.regs[exp_ctrl->block_id][exp_ctrl->reg_id] = value;
+}
+
+static uint16_t bcm2838_genet_phy_exp_shadow_read(BCM2838GenetState *s)
+{
+    BCM2838GenetPhyExpSel* exp_ctrl = &s->phy_regs.exp_ctrl;
+    return s->phy_exp_shd_regs.regs[exp_ctrl->block_id][exp_ctrl->reg_id];
+}
+
+static uint64_t bcm2838_genet_mdio_cmd(BCM2838GenetState *s, uint64_t cmd)
+{
+    BCM2838GenetUmacMdioCmd umac_mdio_cmd = {.value = cmd};
+    uint8_t phy_reg_id = umac_mdio_cmd.fields.reg_id;
+    uint16_t phy_reg_data = umac_mdio_cmd.fields.reg_data;
+    uint16_t *phy_reg = (uint16_t *)&s->phy_regs + phy_reg_id;
+    BCM2838GenetPhyBmcr phy_bmcr = {.value = phy_reg_data};
+
+    if (umac_mdio_cmd.fields.start_busy != 0) {
+        umac_mdio_cmd.fields.start_busy = 0;
+
+        if (umac_mdio_cmd.fields.rd != 0) {
+            if (phy_reg_id == BCM2838_GENET_EXP_DATA) {
+                umac_mdio_cmd.fields.reg_data
+                    = bcm2838_genet_phy_exp_shadow_read(s);
+            } else {
+                umac_mdio_cmd.fields.reg_data = *phy_reg;
+            }
+        } else if (umac_mdio_cmd.fields.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 (phy_bmcr.fields.anrestart == 1) {
+                        phy_bmcr.fields.anrestart = 0;
+                        phy_reg_data = phy_bmcr.value;
+                    }
+                }
+                *phy_reg = phy_reg_data;
+            }
+        }
+    }
+
+    return umac_mdio_cmd.value;
+}
+
 static uint64_t bcm2838_genet_read(void *opaque, hwaddr offset, unsigned size)
 {
     uint64_t value = ~0;
@@ -134,10 +236,12 @@  static void bcm2838_genet_write(void *opaque, hwaddr offset, uint64_t value,
             trace_bcm2838_genet_mac_address(ncs->info_str);
             break;
         case BCM2838_GENET_UMAC_MDIO_CMD:
+            value = bcm2838_genet_mdio_cmd(s, value);
+            s->regs.intrl0.stat.fields.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;
@@ -231,6 +335,7 @@  static void bcm2838_genet_reset(DeviceState *d)
 
     trace_bcm2838_genet_reset("done");
 
+    bcm2838_genet_set_qemu_mac(s);
     bcm2838_genet_phy_reset(s);
 }