realtek: dsa: enhance pcs_get_state() for RTL93xx

Currently the SerDes driven SFP ports give strange ethtool readings
on RTL93xx devices. Especially duplex and speed are shown even if
no link is up and running. That leads to confusion because the MAC
reports arbitrary values.

Enhance the readout by refactoring the pcs_get_state() function.
Calculate speed/duplex/pause only if link is detected.

Suggested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
Signed-off-by: Sharadanand Karanjkar <sk@simonwunderlich.de>
Link: https://github.com/openwrt/openwrt/pull/19575
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
Harshal Gohel 2025-07-14 10:37:28 +02:00 committed by Hauke Mehrtens
parent 2645c4afbb
commit 9ccfca3303
1 changed files with 24 additions and 29 deletions

View File

@ -621,8 +621,8 @@ static void rtldsa_83xx_pcs_get_state(struct phylink_pcs *pcs, struct phylink_li
state->pause |= MLO_PAUSE_TX;
}
static void rtl93xx_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
static void rtldsa_93xx_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
{
struct rtl838x_pcs *rtpcs = container_of(pcs, struct rtl838x_pcs, pcs);
struct rtl838x_switch_priv *priv = rtpcs->priv;
@ -631,21 +631,25 @@ static void rtl93xx_pcs_get_state(struct phylink_pcs *pcs,
u64 link;
u64 media;
if (port < 0 || port > priv->cpu_port) {
state->link = false;
state->link = 0;
state->speed = SPEED_UNKNOWN;
state->duplex = DUPLEX_UNKNOWN;
state->pause &= ~(MLO_PAUSE_RX | MLO_PAUSE_TX);
if (port < 0 || port > priv->cpu_port)
return;
}
/* On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
* state needs to be read twice in order to read a correct result.
* This would not be necessary for ports connected e.g. to RTL8218D
* PHYs.
*/
state->link = 0;
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
if (link & BIT_ULL(port))
state->link = 1;
if (!(link & BIT_ULL(port)))
return;
state->link = 1;
if (priv->family_id == RTL9310_FAMILY_ID)
media = priv->r->get_port_reg_le(RTL931X_MAC_LINK_MEDIA_STS);
@ -656,46 +660,37 @@ static void rtl93xx_pcs_get_state(struct phylink_pcs *pcs,
pr_debug("%s: link state port %d: %llx, media %llx\n", __func__, port,
link & BIT_ULL(port), media);
state->duplex = 0;
if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port))
state->duplex = 1;
state->duplex = DUPLEX_FULL;
else
state->duplex = DUPLEX_HALF;
speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port));
speed >>= (port % 8) << 2;
switch (speed & 0xf) {
case 0:
speed = (speed >> ((port % 8) << 2)) & 0xf;
switch (speed) {
case RTL_SPEED_10:
state->speed = SPEED_10;
break;
case 1:
case RTL_SPEED_100:
state->speed = SPEED_100;
break;
case 2:
case 7:
case RTL_SPEED_1000:
state->speed = SPEED_1000;
break;
case 4:
case RTL_SPEED_10000:
state->speed = SPEED_10000;
break;
case 5:
case 8:
case RTL_SPEED_2500:
state->speed = SPEED_2500;
break;
case 6:
case RTL_SPEED_5000:
state->speed = SPEED_5000;
break;
default:
pr_err("%s: unknown speed: %d\n", __func__, (u32)speed & 0xf);
}
if (priv->family_id == RTL9310_FAMILY_ID
&& (port >= 52 && port <= 55)) { /* Internal serdes */
state->speed = SPEED_10000;
state->link = 1;
state->duplex = 1;
}
pr_debug("%s: speed is: %d %d\n", __func__, (u32)speed & 0xf, state->speed);
state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX);
if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & BIT_ULL(port))
state->pause |= MLO_PAUSE_RX;
if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & BIT_ULL(port))
@ -2750,7 +2745,7 @@ const struct dsa_switch_ops rtl83xx_switch_ops = {
const struct phylink_pcs_ops rtl93xx_pcs_ops = {
.pcs_an_restart = rtl83xx_pcs_an_restart,
.pcs_get_state = rtl93xx_pcs_get_state,
.pcs_get_state = rtldsa_93xx_pcs_get_state,
.pcs_config = rtl83xx_pcs_config,
};