]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: phy: mxl-gpy: add PHY-level statistics via ethtool
authorDaniel Golle <daniel@makrotopia.org>
Sun, 15 Mar 2026 15:19:44 +0000 (15:19 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 17 Mar 2026 23:07:40 +0000 (16:07 -0700)
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 <daniel@makrotopia.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/0029a2fb29bfdcc26abff828d2e18400067b5c58.1773587924.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/mxl-gpy.c

index 5f99766fb64c3c2c9be98be18efde39bcd222758..0da2d4e9d85481f9442ed1c35351d51f6426e66f 100644 (file)
 #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,