* [PATCH net-next] net: phy: mxl-gpy: add PHY-level statistics via ethtool
@ 2026-03-13 1:19 Daniel Golle
2026-03-14 17:48 ` Jakub Kicinski
0 siblings, 1 reply; 2+ messages in thread
From: Daniel Golle @ 2026-03-13 1:19 UTC (permalink / raw)
To: Liang Xu, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
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>
---
drivers/net/phy/mxl-gpy.c | 66 +++++++++++++++++++++++++++++++++++++--
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
index 65a77b8431514..8248bb37f0cae 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
@@ -332,8 +338,9 @@ static int gpy_mbox_read(struct phy_device *phydev, u32 addr)
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)
@@ -1099,6 +1106,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),
@@ -1124,6 +1156,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
.phy_id = PHY_ID_GPY115B,
@@ -1150,6 +1184,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
@@ -1175,6 +1211,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
.phy_id = PHY_ID_GPY211B,
@@ -1201,6 +1239,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
@@ -1226,6 +1266,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
.phy_id = PHY_ID_GPY212B,
@@ -1252,6 +1294,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
@@ -1277,6 +1321,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
.phy_id = PHY_ID_GPY215B,
@@ -1303,6 +1349,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
@@ -1328,6 +1376,8 @@ static struct phy_driver gpy_drivers[] = {
.led_hw_control_set = gpy_led_hw_control_set,
.led_polarity_set = gpy_led_polarity_set,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
@@ -1348,6 +1398,8 @@ static struct phy_driver gpy_drivers[] = {
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
@@ -1368,6 +1420,8 @@ static struct phy_driver gpy_drivers[] = {
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
@@ -1388,6 +1442,8 @@ static struct phy_driver gpy_drivers[] = {
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
.link_change_notify = gpy_link_change_notify,
+ .update_stats = gpy_update_stats,
+ .get_phy_stats = gpy_get_phy_stats,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C),
@@ -1412,6 +1468,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),
@@ -1429,6 +1487,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,
@@ -1451,6 +1511,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,
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH net-next] net: phy: mxl-gpy: add PHY-level statistics via ethtool
2026-03-13 1:19 [PATCH net-next] net: phy: mxl-gpy: add PHY-level statistics via ethtool Daniel Golle
@ 2026-03-14 17:48 ` Jakub Kicinski
0 siblings, 0 replies; 2+ messages in thread
From: Jakub Kicinski @ 2026-03-14 17:48 UTC (permalink / raw)
To: Daniel Golle
Cc: Liang Xu, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Paolo Abeni, netdev, linux-kernel
On Fri, 13 Mar 2026 01:19:07 +0000 Daniel Golle wrote:
> 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().
This does not apply. The code itself LGTM. One question - the 8b
counter seems kinda narrow. I wonder if we should read it periodically
so it doesn't overflow? Could be an overkill tho, IDK what's normal,
I defer to PHY maintainers..
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-03-14 17:48 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-13 1:19 [PATCH net-next] net: phy: mxl-gpy: add PHY-level statistics via ethtool Daniel Golle
2026-03-14 17:48 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox