From: Daniel Golle Date: Sun, 15 Mar 2026 15:19:44 +0000 (+0000) Subject: net: phy: mxl-gpy: add PHY-level statistics via ethtool X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05c1fc56d37b2531b14afac9e76443ed98276923;p=thirdparty%2Fkernel%2Flinux.git net: phy: mxl-gpy: add PHY-level statistics via ethtool Report PCS receive error counts for all supported GPY115x, GPY2xx and MxL862xx PHYs. Accumulate the vendor-specific PHY_ERRCNT read-clear counter (SEL=RXERR) in .update_stats() and expose it as both IEEE 802.3 SymbolErrorDuringCarrier and generic rx_errors via .get_phy_stats(). Signed-off-by: Daniel Golle Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/0029a2fb29bfdcc26abff828d2e18400067b5c58.1773587924.git.daniel@makrotopia.org Signed-off-by: Jakub Kicinski --- diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c index 5f99766fb64c3..0da2d4e9d8548 100644 --- a/drivers/net/phy/mxl-gpy.c +++ b/drivers/net/phy/mxl-gpy.c @@ -38,12 +38,17 @@ #define PHY_CTL1_MDICD BIT(3) #define PHY_CTL1_MDIAB BIT(2) #define PHY_CTL1_AMDIX BIT(0) +#define PHY_ERRCNT 0x15 /* Error counter */ #define PHY_MIISTAT 0x18 /* MII state */ #define PHY_IMASK 0x19 /* interrupt mask */ #define PHY_ISTAT 0x1A /* interrupt status */ #define PHY_LED 0x1B /* LEDs */ #define PHY_FWV 0x1E /* firmware version */ +#define PHY_ERRCNT_SEL GENMASK(11, 8) +#define PHY_ERRCNT_COUNT GENMASK(7, 0) +#define PHY_ERRCNT_SEL_RXERR 0 + #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0) #define PHY_MIISTAT_DPX BIT(3) #define PHY_MIISTAT_LS BIT(10) @@ -134,6 +139,7 @@ struct gpy_priv { u8 fw_major; u8 fw_minor; u32 wolopts; + u64 rx_errors; /* It takes 3 seconds to fully switch out of loopback mode before * it can safely re-enter loopback mode. Record the time when @@ -331,8 +337,9 @@ out: static int gpy_config_init(struct phy_device *phydev) { - /* Nothing to configure. Configuration Requirement Placeholder */ - return 0; + /* Count MDI RX errors (SymbolErrorDuringCarrier) */ + return phy_write(phydev, PHY_ERRCNT, + FIELD_PREP(PHY_ERRCNT_SEL, PHY_ERRCNT_SEL_RXERR)); } static int gpy21x_config_init(struct phy_device *phydev) @@ -1067,6 +1074,31 @@ static int gpy_config_inband(struct phy_device *phydev, unsigned int modes) VSPEC1_SGMII_ANEN_ANRS); } +static int gpy_update_stats(struct phy_device *phydev) +{ + struct gpy_priv *priv = phydev->priv; + int ret; + + /* PHY_ERRCNT: 8-bit read-clear counter, SEL set to RXERR */ + ret = phy_read(phydev, PHY_ERRCNT); + if (ret < 0) + return ret; + + priv->rx_errors += FIELD_GET(PHY_ERRCNT_COUNT, ret); + + return 0; +} + +static void gpy_get_phy_stats(struct phy_device *phydev, + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats) +{ + struct gpy_priv *priv = phydev->priv; + + eth_stats->SymbolErrorDuringCarrier = priv->rx_errors; + stats->rx_errors = priv->rx_errors; +} + static struct phy_driver gpy_drivers[] = { { PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx), @@ -1091,6 +1123,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { .phy_id = PHY_ID_GPY115B, @@ -1116,6 +1150,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY115C), @@ -1140,6 +1176,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { .phy_id = PHY_ID_GPY211B, @@ -1165,6 +1203,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY211C), @@ -1189,6 +1229,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { .phy_id = PHY_ID_GPY212B, @@ -1214,6 +1256,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY212C), @@ -1238,6 +1282,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { .phy_id = PHY_ID_GPY215B, @@ -1263,6 +1309,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY215C), @@ -1287,6 +1335,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY241B), @@ -1306,6 +1356,8 @@ static struct phy_driver gpy_drivers[] = { .set_wol = gpy_set_wol, .get_wol = gpy_get_wol, .set_loopback = gpy_loopback, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM), @@ -1325,6 +1377,8 @@ static struct phy_driver gpy_drivers[] = { .set_wol = gpy_set_wol, .get_wol = gpy_get_wol, .set_loopback = gpy_loopback, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_GPY245B), @@ -1344,6 +1398,8 @@ static struct phy_driver gpy_drivers[] = { .set_wol = gpy_set_wol, .get_wol = gpy_get_wol, .set_loopback = gpy_loopback, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C), @@ -1368,6 +1424,8 @@ static struct phy_driver gpy_drivers[] = { .led_hw_control_get = gpy_led_hw_control_get, .led_hw_control_set = gpy_led_hw_control_set, .led_polarity_set = gpy_led_polarity_set, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, }, { PHY_ID_MATCH_MODEL(PHY_ID_MXL86252), @@ -1385,6 +1443,8 @@ static struct phy_driver gpy_drivers[] = { .set_wol = gpy_set_wol, .get_wol = gpy_get_wol, .set_loopback = gpy_loopback, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, .led_brightness_set = gpy_led_brightness_set, .led_hw_is_supported = gpy_led_hw_is_supported, .led_hw_control_get = gpy_led_hw_control_get, @@ -1407,6 +1467,8 @@ static struct phy_driver gpy_drivers[] = { .set_wol = gpy_set_wol, .get_wol = gpy_get_wol, .set_loopback = gpy_loopback, + .update_stats = gpy_update_stats, + .get_phy_stats = gpy_get_phy_stats, .led_brightness_set = gpy_led_brightness_set, .led_hw_is_supported = gpy_led_hw_is_supported, .led_hw_control_get = gpy_led_hw_control_get,