From 412098d0ed2a3e2819b21e633e92726ae68a723b Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:01:09 +0200 Subject: [PATCH 1/2] phy: advertise half duplex only when half duplex was asked for The 100M branch of phy_set_speed() is missing an else. A port set to 100M half writes 0x1481, falls into the next if, finds duplex is not 1 and takes its else, and immediately overwrites the register with 0x1581. The port ends up advertising 100BASE-TX half and full, which is what "both" is supposed to mean. The 10M branch directly above has the chain the 100M one meant to have, and the values line up with it: bit 7 is 100BASE-TX half, bit 8 is full, so 0x1481, 0x1501 and 0x1581 are half, full and both, exactly as 0x1421, 0x1441 and 0x1461 are for 10M. Forcing half duplex is something people do to match a link partner that cannot negotiate, so quietly advertising full as well is the wrong way for this to fail. Not tried on hardware: every port on the switch here has a link up, and setting the speed restarts negotiation. --- rtl837x_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl837x_phy.c b/rtl837x_phy.c index a3383544..09b5c168 100644 --- a/rtl837x_phy.c +++ b/rtl837x_phy.c @@ -343,7 +343,7 @@ void phy_set_speed(void) __banked phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001); if (!phy_settings.duplex) phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1481); - if (phy_settings.duplex == 1) + else if (phy_settings.duplex == 1) phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1501); else phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1581); From fe3da0c7a518d8b004f6217563a338dc17d477a0 Mon Sep 17 00:00:00 2001 From: d00f <8052722+DrDoof@users.noreply.github.com> Date: Tue, 8 Sep 2026 23:14:15 +0200 Subject: [PATCH 2/2] phy: advertise half duplex only when half duplex was asked for The 100M branch of phy_set_speed() was missing an else. A port set to 100M half wrote 0x1481, walked into the next if, found duplex was not 1, took its else and overwrote the register with 0x1581 straight away, so it advertised 100BASE-TX half and full both. That is what asking for both is meant to do, and forcing half is something people do to match a partner that cannot negotiate, so the link comes up looking fine and misbehaves under load. Bit 7 is 100BASE-TX half and bit 8 is full, which makes 0x1481, 0x1501 and 0x1581 half, full and both, the same shape as 0x1421, 0x1441 and 0x1461 in the 10M branch above. Both branches now choose the advertisement into a variable and write it once, and they compare against the PHY_DUPLEX_* names instead of 0, 1 and whatever is left. Same values reach the PHY; BANK2 loses 92 bytes. --- rtl837x_phy.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/rtl837x_phy.c b/rtl837x_phy.c index 09b5c168..25f7e150 100644 --- a/rtl837x_phy.c +++ b/rtl837x_phy.c @@ -332,21 +332,25 @@ void phy_set_speed(void) __banked phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000); // Clear bit 12: No Autoneg, Set Extended Pages (bit 13) if (phy_settings.speed == PHY_SPEED_10M) { phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001); - if (!phy_settings.duplex) - phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1421); - else if (phy_settings.duplex == 1) - phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1441); + uint16_t adv10; + if (phy_settings.duplex == PHY_DUPLEX_HALF) + adv10 = 0x1421; + else if (phy_settings.duplex == PHY_DUPLEX_FULL) + adv10 = 0x1441; else - phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1461); + adv10 = 0x1461; + phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, adv10); phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000); } else if (phy_settings.speed == PHY_SPEED_100M) { phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001); - if (!phy_settings.duplex) - phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1481); - else if (phy_settings.duplex == 1) - phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1501); + uint16_t adv100; + if (phy_settings.duplex == PHY_DUPLEX_HALF) + adv100 = 0x1481; + else if (phy_settings.duplex == PHY_DUPLEX_FULL) + adv100 = 0x1501; else - phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1581); + adv100 = 0x1581; + phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, adv100); phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000); } else { // AN Advertisement Register (MMD 7.0x0010)