Currently the SerDes driven SFP ports give strange ethtool readings
on RTL83xx 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. Additionally
add reporting of 10G for SFP+ on RTL839x.
ethtool for empty SFP cage before/after
root@OpenWrt:~# ethtool lan9
Settings for lan9:
Supported ports: [ MII ]
Supported link modes: 1000baseT/Full
1000baseKX/Full
1000baseX/Full
1000baseT1/Full
Supported pause frame use: Symmetric Receive-only
Supports auto-negotiation: Yes
Supported FEC modes: Not reported
Advertised link modes: 1000baseT/Full
1000baseKX/Full
1000baseX/Full
1000baseT1/Full
Advertised pause frame use: Symmetric Receive-only
Advertised auto-negotiation: Yes
Advertised FEC modes: Not reported
Speed: 10Mb/s
Duplex: Half
Port: MII
PHYAD: 0
Transceiver: internal
Auto-negotiation: on
Supports Wake-on: d
Wake-on: d
Link detected: no
root@OpenWrt:~# ethtool lan9
Settings for lan9:
Supported ports: [ MII ]
Supported link modes: 1000baseT/Full
1000baseKX/Full
1000baseX/Full
1000baseT1/Full
Supported pause frame use: Symmetric Receive-only
Supports auto-negotiation: Yes
Supported FEC modes: Not reported
Advertised link modes: 1000baseT/Full
1000baseKX/Full
1000baseX/Full
1000baseT1/Full
Advertised pause frame use: Symmetric Receive-only
Advertised auto-negotiation: Yes
Advertised FEC modes: Not reported
Speed: Unknown!
Duplex: Unknown! (255)
Port: MII
PHYAD: 0
Transceiver: internal
Auto-negotiation: on
Supports Wake-on: d
Wake-on: d
Link detected: no
ethtool with inserted but NOT connected 1G module before/after
root@OpenWrt:~# ethtool lan9
Settings for lan9:
Supported ports: [ FIBRE ]
Supported link modes: 1000baseX/Full
Supported pause frame use: Symmetric Receive-only
Supports auto-negotiation: Yes
Supported FEC modes: Not reported
Advertised link modes: 1000baseX/Full
Advertised pause frame use: Symmetric Receive-only
Advertised auto-negotiation: Yes
Advertised FEC modes: Not reported
Speed: 1000Mb/s
Duplex: Full
Port: FIBRE
PHYAD: 0
Transceiver: internal
Auto-negotiation: on
Supports Wake-on: d
Wake-on: d
Link detected: no
root@OpenWrt:~# ethtool lan9
Settings for lan9:
Supported ports: [ FIBRE ]
Supported link modes: 1000baseX/Full
Supported pause frame use: Symmetric Receive-only
Supports auto-negotiation: Yes
Supported FEC modes: Not reported
Advertised link modes: 1000baseX/Full
Advertised pause frame use: Symmetric Receive-only
Advertised auto-negotiation: Yes
Advertised FEC modes: Not reported
Speed: Unknown!
Duplex: Unknown! (255)
Port: FIBRE
PHYAD: 0
Transceiver: internal
Auto-negotiation: on
Supports Wake-on: d
Wake-on: d
Link detected: no
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/19524
Signed-off-by: Robert Marko <robimarko@gmail.com>
return sds_num;
}
-static void rtl83xx_pcs_get_state(struct phylink_pcs *pcs,
- struct phylink_link_state *state)
+static void rtldsa_83xx_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;
int port = rtpcs->port;
u64 speed;
- u64 link;
- 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;
- }
- state->link = 0;
- link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
- if (link & BIT_ULL(port))
- state->link = 1;
- pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port));
+ if (!(priv->r->get_port_reg_le(priv->r->mac_link_sts) & BIT_ULL(port)))
+ return;
+
+ state->link = 1;
- 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 % 16) << 1;
- switch (speed & 0x3) {
- case 0:
+ speed = (speed >> ((port % 16) << 1)) & 0x3;
+
+ 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 RTL_SPEED_1000:
state->speed = SPEED_1000;
break;
case 3:
- if (priv->family_id == RTL9300_FAMILY_ID
- && (port == 24 || port == 26)) /* Internal serdes */
- state->speed = SPEED_2500;
- else
- state->speed = SPEED_100; /* Is in fact 500Mbit */
+ /*
+ * This is ok so far but with minor inconsistencies. On RTL838x this setting is
+ * for either 500M or 2G. It might be that MAC_GLITE_STS register tells more. On
+ * RTL839x these vendor specifics are derived from MAC_LINK_500M_STS and mode 3
+ * is 10G. This is of interest so resolve to it. Sadly it is off by one for the
+ * current RTL_SPEED_10000 (=4) definition for RTL93xx.
+ */
+ state->speed = SPEED_10000;
+ break;
}
- 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))
const struct phylink_pcs_ops rtl83xx_pcs_ops = {
.pcs_an_restart = rtl83xx_pcs_an_restart,
- .pcs_get_state = rtl83xx_pcs_get_state,
+ .pcs_get_state = rtldsa_83xx_pcs_get_state,
.pcs_config = rtl83xx_pcs_config,
};