Merge branch 'net-phy-smsc-robustness-fixes-for-lan87xx-lan9500'

Oleksij Rempel says:

====================
net: phy: smsc: robustness fixes for LAN87xx/LAN9500

The SMSC 10/100 PHYs (LAN87xx family) found in smsc95xx (lan95xx)
USB-Ethernet adapters show several quirks around the Auto-MDIX feature:

- A hardware strap (AUTOMDIX_EN) may boot the PHY in fixed-MDI mode, and
  the current driver cannot always override it.

- When Auto-MDIX is left enabled while autonegotiation is forced off,
  the PHY endlessly swaps the TX/RX pairs and never links up.

- The driver sets the enable bit for Auto-MDIX but forgets the override
  bit, so userspace requests are silently ignored.

- Rapid configuration changes can wedge the link if PHY IRQs are
  enabled.

The four patches below make the MDIX state fully predictable and prevent
link failures in every tested strap / autoneg / MDI-X permutation.

Tested on LAN9512 Eval board.
====================

Link: https://patch.msgid.link/20250703114941.3243890-1-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-07-08 18:12:55 -07:00
commit 3c78f91e82

View File

@ -155,10 +155,29 @@ static int smsc_phy_reset(struct phy_device *phydev)
static int lan87xx_config_aneg(struct phy_device *phydev)
{
int rc;
u8 mdix_ctrl;
int val;
int rc;
switch (phydev->mdix_ctrl) {
/* When auto-negotiation is disabled (forced mode), the PHY's
* Auto-MDIX will continue toggling the TX/RX pairs.
*
* To establish a stable link, we must select a fixed MDI mode.
* If the user has not specified a fixed MDI mode (i.e., mdix_ctrl is
* 'auto'), we default to ETH_TP_MDI. This choice of a ETH_TP_MDI mode
* mirrors the behavior the hardware would exhibit if the AUTOMDIX_EN
* strap were configured for a fixed MDI connection.
*/
if (phydev->autoneg == AUTONEG_DISABLE) {
if (phydev->mdix_ctrl == ETH_TP_MDI_AUTO)
mdix_ctrl = ETH_TP_MDI;
else
mdix_ctrl = phydev->mdix_ctrl;
} else {
mdix_ctrl = phydev->mdix_ctrl;
}
switch (mdix_ctrl) {
case ETH_TP_MDI:
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_;
break;
@ -167,7 +186,8 @@ static int lan87xx_config_aneg(struct phy_device *phydev)
SPECIAL_CTRL_STS_AMDIX_STATE_;
break;
case ETH_TP_MDI_AUTO:
val = SPECIAL_CTRL_STS_AMDIX_ENABLE_;
val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_;
break;
default:
return genphy_config_aneg(phydev);
@ -183,7 +203,7 @@ static int lan87xx_config_aneg(struct phy_device *phydev)
rc |= val;
phy_write(phydev, SPECIAL_CTRL_STS, rc);
phydev->mdix = phydev->mdix_ctrl;
phydev->mdix = mdix_ctrl;
return genphy_config_aneg(phydev);
}
@ -261,6 +281,33 @@ int lan87xx_read_status(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(lan87xx_read_status);
static int lan87xx_phy_config_init(struct phy_device *phydev)
{
int rc;
/* The LAN87xx PHY's initial MDI-X mode is determined by the AUTOMDIX_EN
* hardware strap, but the driver cannot read the strap's status. This
* creates an unpredictable initial state.
*
* To ensure consistent and reliable behavior across all boards,
* override the strap configuration on initialization and force the PHY
* into a known state with Auto-MDIX enabled, which is the expected
* default for modern hardware.
*/
rc = phy_modify(phydev, SPECIAL_CTRL_STS,
SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
SPECIAL_CTRL_STS_AMDIX_STATE_,
SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
SPECIAL_CTRL_STS_AMDIX_ENABLE_);
if (rc < 0)
return rc;
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
return smsc_phy_config_init(phydev);
}
static int lan874x_phy_config_init(struct phy_device *phydev)
{
u16 val;
@ -695,7 +742,7 @@ static struct phy_driver smsc_phy_driver[] = {
/* basic functions */
.read_status = lan87xx_read_status,
.config_init = smsc_phy_config_init,
.config_init = lan87xx_phy_config_init,
.soft_reset = smsc_phy_reset,
.config_aneg = lan87xx_config_aneg,