net: phy: qca83xx: read resolved QCA8337 link status

After a QCA8337 SmartSpeed downshift, genphy_read_status() can report
1000 Mbit/s from the advertised modes when the PHY is running at
100 Mbit/s. On a Redmi AX5400 running downstream OpenWrt Linux 6.18.52,
this caused qca8k to program the user-port MAC for 1000 Mbit/s and
traffic failed. An earlier prototype that read the resolved speed
from the PHY restored traffic.

Wrap genphy_read_status() and use at803x_read_specific_status() to
replace its speed and duplex result when an autonegotiated link comes
up. Resolve pause for the resulting duplex. Until the PHY reports a
resolved speed and duplex, report the link as down so that the next
poll reads them again, as marvell_read_status_page_an() does. In that
case also clear the duplex, master/slave state and link partner modes
that genphy_read_status() read for an up link. QCA8337's register
layout matches the helper (data sheet 80-Y0619-3 Rev. D, pages
327-328).

Keep the generic master/slave read order and the early return for a
steady autonegotiated link. This avoids additional reads of the
clear-on-read idle error count in MII_STAT1000. Forced mode continues
to use BMCR. Report the MDI-X state read at link-up and invalidate it
when the link goes down or autonegotiation is disabled. Clear
mdix_ctrl because this driver does not implement MDI-X configuration.

The QCA8327 entries keep genphy_read_status(). The AR8327 data sheet
(MKG-15791 Rev. 1.0) lists the QCA8327-A PHY ID and documents the same
PHY-specific status layout, with SmartSpeed enabled after a hardware
reset, so those PHYs may have the same problem. That is untested on
QCA8327 hardware, so limit this change to QCA8337.

Fixes: b3591c2a36 ("net: dsa: qca8k: Switch to PHYLINK instead of PHYLIB")
Suggested-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
Assisted-by: LLM
Link: https://patch.msgid.link/20261013190339.51909-1-yongzhao.derek@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Yongzhao Chen 2026-10-13 21:03:39 +02:00 committed by Paolo Abeni
parent d06ae66516
commit 42a91cbfc7

View File

@ -92,6 +92,63 @@ static int qca83xx_probe(struct phy_device *phydev)
return 0;
}
static int qca8337_read_status(struct phy_device *phydev)
{
struct at803x_ss_mask ss_mask = {
.speed_mask = AT803X_SS_SPEED_MASK,
.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK),
};
int ret, old_link = phydev->link;
ret = genphy_read_status(phydev);
if (ret)
return ret;
/* Keep the state resolved at link-up while the link stays up */
if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
goto out;
phydev->mdix = ETH_TP_MDI_INVALID;
/* genphy_read_status() derives speed and duplex from the
* advertisements, which is wrong after a SmartSpeed downshift.
* Use what the PHY resolved instead.
*/
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete &&
phydev->link) {
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->pause = false;
phydev->asym_pause = false;
ret = at803x_read_specific_status(phydev, ss_mask);
if (ret)
return ret;
/* Report the link down until speed and duplex are resolved,
* so that the next poll reads them again. Also clear what
* genphy_read_status() read for an up link, as the
* PHY_HALTED path of the state machine does.
*/
if (phydev->speed == SPEED_UNKNOWN ||
phydev->duplex == DUPLEX_UNKNOWN) {
phydev->link = 0;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->mdix = ETH_TP_MDI_INVALID;
phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
linkmode_zero(phydev->lp_advertising);
} else {
phy_resolve_aneg_pause(phydev);
}
}
out:
/* MDI-X configuration is not implemented */
phydev->mdix_ctrl = ETH_TP_MDI_INVALID;
return 0;
}
static int qca83xx_config_init(struct phy_device *phydev)
{
u8 switch_revision;
@ -220,6 +277,7 @@ static struct phy_driver qca83xx_driver[] = {
.flags = PHY_IS_INTERNAL,
.config_init = qca83xx_config_init,
.soft_reset = genphy_soft_reset,
.read_status = qca8337_read_status,
.get_sset_count = qca83xx_get_sset_count,
.get_strings = qca83xx_get_strings,
.get_stats = qca83xx_get_stats,