* [PATCH net v3 0/4] net: phy: as21xxx: simple series for trivial fixes
@ 2026-10-28 7:30 Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 1/4] net: phy: as21xxx: handle stale link status during autoneg Christian Marangi
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Christian Marangi @ 2026-10-28 7:30 UTC (permalink / raw)
To: Christian Marangi, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
This is a simple series to account some fixes for some corner
case for as21xxx
Changes v3:
- Rework to address some comments from sashiko
Changes v2:
- Add review tag
Christian Marangi (4):
net: phy: as21xxx: handle stale link status during autoneg
net: phy: as21xxx: fix read_status speed handling
net: phy: as21xxx: force C45 OPs for AUTONEG
net: phy: as21xxx: handle odd firmware on loading
drivers/net/phy/as21xxx.c | 180 ++++++++++++++++++++++++++++----------
1 file changed, 136 insertions(+), 44 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v3 1/4] net: phy: as21xxx: handle stale link status during autoneg
2026-10-28 7:30 [PATCH net v3 0/4] net: phy: as21xxx: simple series for trivial fixes Christian Marangi
@ 2026-10-28 7:30 ` Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 2/4] net: phy: as21xxx: fix read_status speed handling Christian Marangi
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Christian Marangi @ 2026-10-28 7:30 UTC (permalink / raw)
To: Christian Marangi, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
Cc: Maxime Chevallier
When autonegotiation is restarted, MDIO_STAT1_ANEGCOMPLETE may be
cleared before MDIO_STAT1_LSTATUS. In this case, the link status bit
may still indicate link up even though autonegotiation has not
completed.
Force the link down when autonegotiation is enabled but has not
completed, preventing as21xxx_read_status() from reporting a link up
with an unknown or stale speed.
Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/phy/as21xxx.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/net/phy/as21xxx.c b/drivers/net/phy/as21xxx.c
index d5738117eca6..0db82da8dbdf 100644
--- a/drivers/net/phy/as21xxx.c
+++ b/drivers/net/phy/as21xxx.c
@@ -658,6 +658,13 @@ static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
return status;
phydev->link = !!(status & MDIO_STAT1_LSTATUS);
+ phydev->autoneg_complete = !!(status & MDIO_AN_STAT1_COMPLETE);
+
+ /* Consider the case that autoneg was started and "aneg complete"
+ * bit has been reset, but "link up" bit not yet.
+ */
+ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
+ phydev->link = 0;
return 0;
}
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v3 2/4] net: phy: as21xxx: fix read_status speed handling
2026-10-28 7:30 [PATCH net v3 0/4] net: phy: as21xxx: simple series for trivial fixes Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 1/4] net: phy: as21xxx: handle stale link status during autoneg Christian Marangi
@ 2026-10-28 7:30 ` Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 3/4] net: phy: as21xxx: force C45 OPs for AUTONEG Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 4/4] net: phy: as21xxx: handle odd firmware on loading Christian Marangi
3 siblings, 0 replies; 5+ messages in thread
From: Christian Marangi @ 2026-10-28 7:30 UTC (permalink / raw)
To: Christian Marangi, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
With further test with 2.5G NIC it was discovered that
phy_resolve_aneg_linkmode is not enough to detect speed higher that 1G
when autoneg is enabled.
Also in the switch case there is a typo where the speed mask is AND with
VEND1_SPEED_STATUS instead of the correct mask VEND1_SPEED_MASK.
Rework the read_status code to use the vendor register to read speed >1G.
While at it also address a problem in read link where autoneg_complete
wasn't cleared when AN was restarted.
Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/phy/as21xxx.c | 100 ++++++++++++++++++++++----------------
1 file changed, 58 insertions(+), 42 deletions(-)
diff --git a/drivers/net/phy/as21xxx.c b/drivers/net/phy/as21xxx.c
index 0db82da8dbdf..f5968eec11c8 100644
--- a/drivers/net/phy/as21xxx.c
+++ b/drivers/net/phy/as21xxx.c
@@ -650,6 +650,7 @@ static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
*/
if (*bmcr & BMCR_ANRESTART) {
phydev->link = 0;
+ phydev->autoneg_complete = 0;
return 0;
}
@@ -671,7 +672,7 @@ static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
static int as21xxx_read_c22_lpa(struct phy_device *phydev)
{
- int lpagb;
+ int lpagb, lpa;
/* MII_STAT1000 are only filled in the mapped C22
* in C45, use that to fill lpagb values and check.
@@ -698,13 +699,22 @@ static int as21xxx_read_c22_lpa(struct phy_device *phydev)
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
lpagb);
+ lpa = phy_read_mmd(phydev, MDIO_MMD_AN,
+ AS21XXX_MDIO_AN_C22 + MII_LPA);
+ if (lpa < 0)
+ return lpa;
+
+ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
+
return 0;
}
static int as21xxx_read_status(struct phy_device *phydev)
{
int bmcr, old_link = phydev->link;
+ int speed;
int ret;
+ int val;
ret = as21xxx_read_link(phydev, &bmcr);
if (ret)
@@ -719,57 +729,63 @@ static int as21xxx_read_status(struct phy_device *phydev)
phydev->pause = 0;
phydev->asym_pause = 0;
+ if (!phydev->link)
+ return 0;
+
if (phydev->autoneg == AUTONEG_ENABLE) {
- ret = genphy_c45_read_lpa(phydev);
- if (ret)
- return ret;
+ if (!phydev->autoneg_complete) {
+ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+ 0);
+ mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+ return 0;
+ }
ret = as21xxx_read_c22_lpa(phydev);
if (ret)
return ret;
- phy_resolve_aneg_linkmode(phydev);
- } else {
- int speed;
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+ if (val < 0)
+ return val;
+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, val);
+ } else {
linkmode_zero(phydev->lp_advertising);
+ }
- speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
- VEND1_SPEED_STATUS);
- if (speed < 0)
- return speed;
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+ phy_resolve_aneg_linkmode(phydev);
+ } else if (phydev->autoneg == AUTONEG_DISABLE) {
+ ret = genphy_read_status_fixed(phydev);
+ if (ret < 0)
+ return ret;
+ }
- switch (speed & VEND1_SPEED_STATUS) {
- case VEND1_SPEED_10000:
- phydev->speed = SPEED_10000;
- phydev->duplex = DUPLEX_FULL;
- break;
- case VEND1_SPEED_5000:
- phydev->speed = SPEED_5000;
- phydev->duplex = DUPLEX_FULL;
- break;
- case VEND1_SPEED_2500:
- phydev->speed = SPEED_2500;
- phydev->duplex = DUPLEX_FULL;
- break;
- case VEND1_SPEED_1000:
- phydev->speed = SPEED_1000;
- if (bmcr & BMCR_FULLDPLX)
- phydev->duplex = DUPLEX_FULL;
- else
- phydev->duplex = DUPLEX_HALF;
- break;
- case VEND1_SPEED_100:
- phydev->speed = SPEED_100;
- phydev->duplex = DUPLEX_FULL;
- break;
- case VEND1_SPEED_10:
- phydev->speed = SPEED_10;
- phydev->duplex = DUPLEX_FULL;
- break;
- default:
- return -EINVAL;
- }
+ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
+ VEND1_SPEED_STATUS);
+ if (speed < 0)
+ return speed;
+
+ switch (speed & VEND1_SPEED_MASK) {
+ case VEND1_SPEED_10000:
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+ break;
+ case VEND1_SPEED_5000:
+ phydev->speed = SPEED_5000;
+ phydev->duplex = DUPLEX_FULL;
+ break;
+ case VEND1_SPEED_2500:
+ phydev->speed = SPEED_2500;
+ phydev->duplex = DUPLEX_FULL;
+ break;
+ case VEND1_SPEED_1000:
+ case VEND1_SPEED_100:
+ case VEND1_SPEED_10:
+ break;
+ default:
+ return -EINVAL;
}
return 0;
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v3 3/4] net: phy: as21xxx: force C45 OPs for AUTONEG
2026-10-28 7:30 [PATCH net v3 0/4] net: phy: as21xxx: simple series for trivial fixes Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 1/4] net: phy: as21xxx: handle stale link status during autoneg Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 2/4] net: phy: as21xxx: fix read_status speed handling Christian Marangi
@ 2026-10-28 7:30 ` Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 4/4] net: phy: as21xxx: handle odd firmware on loading Christian Marangi
3 siblings, 0 replies; 5+ messages in thread
From: Christian Marangi @ 2026-10-28 7:30 UTC (permalink / raw)
To: Christian Marangi, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
With further testing with 2.5G NIC, it was discovered that the PHY
require the C45 OPs to configure and restart ANEG or speed higher than
1G doesn't function correctly.
To force C45 OPs with generic PHY function, clear the C22 bit from
devices_in_package bitmask and implement a custom .config_aneg.
Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/phy/as21xxx.c | 67 +++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/drivers/net/phy/as21xxx.c b/drivers/net/phy/as21xxx.c
index f5968eec11c8..81e9827873bf 100644
--- a/drivers/net/phy/as21xxx.c
+++ b/drivers/net/phy/as21xxx.c
@@ -616,6 +616,13 @@ static int as21xxx_probe(struct phy_device *phydev)
if (ret)
return ret;
+ /* Even if PHY declare support for Clause 22 register,
+ * Clause 45 register should be used for ANEG configuration
+ * and restart. Clear the C22 bit for devices_in_package to
+ * force C45 generic OPs in generic PHY ANGE OPs.
+ */
+ phydev->c45_ids.devices_in_package &= ~BIT(0);
+
ret = aeon_ipc_sync_parity(phydev, priv);
if (ret)
return ret;
@@ -633,6 +640,56 @@ static int as21xxx_probe(struct phy_device *phydev)
return aeon_dpc_ra_enable(phydev);
}
+static int as21xxx_config_aneg(struct phy_device *phydev)
+{
+ bool changed = false;
+ u16 ctl = 0;
+ int ret;
+
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return genphy_c45_pma_setup_forced(phydev);
+
+ ret = genphy_c45_an_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ ctl |= CTL1000_PREFER_MASTER;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ break;
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ ctl |= CTL1000_AS_MASTER;
+ fallthrough;
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ ctl |= CTL1000_ENABLE_MASTER;
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ break;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ ctl = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
+ AS21XXX_MDIO_AN_C22 + MII_CTRL1000,
+ ADVERTISE_1000FULL | ADVERTISE_1000HALF |
+ CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
+ CTL1000_PREFER_MASTER, ctl);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ changed = true;
+
+ return genphy_c45_check_and_restart_aneg(phydev, changed);
+}
+
static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
{
int status;
@@ -982,6 +1039,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21011JB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -994,6 +1052,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21011PB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1006,6 +1065,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21010PB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1018,6 +1078,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21010JB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1030,6 +1091,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21210PB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1042,6 +1104,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21510JB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1054,6 +1117,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21510PB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1066,6 +1130,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21511JB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1078,6 +1143,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21210JB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
@@ -1090,6 +1156,7 @@ static struct phy_driver as21xxx_drivers[] = {
.name = "Aeonsemi AS21511PB1",
.probe = as21xxx_probe,
.match_phy_device = as21xxx_match_phy_device,
+ .config_aneg = as21xxx_config_aneg,
.read_status = as21xxx_read_status,
.led_brightness_set = as21xxx_led_brightness_set,
.led_hw_is_supported = as21xxx_led_hw_is_supported,
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v3 4/4] net: phy: as21xxx: handle odd firmware on loading
2026-10-28 7:30 [PATCH net v3 0/4] net: phy: as21xxx: simple series for trivial fixes Christian Marangi
` (2 preceding siblings ...)
2026-10-28 7:30 ` [PATCH net v3 3/4] net: phy: as21xxx: force C45 OPs for AUTONEG Christian Marangi
@ 2026-10-28 7:30 ` Christian Marangi
3 siblings, 0 replies; 5+ messages in thread
From: Christian Marangi @ 2026-10-28 7:30 UTC (permalink / raw)
To: Christian Marangi, Andrew Lunn, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
Even if firmware for AS21xxx should be always u16 aligned, we can't assume
this will be the same forever. Better address this by simply validate the
index before reading out of bound from the data buffer.
A zero padded buffer will be passed to the AS21xxx mailbox in such case.
Fixes: 830877d89edc ("net: phy: Add support for Aeonsemi AS21xxx PHYs")
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/phy/as21xxx.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/as21xxx.c b/drivers/net/phy/as21xxx.c
index 81e9827873bf..655127a656c4 100644
--- a/drivers/net/phy/as21xxx.c
+++ b/drivers/net/phy/as21xxx.c
@@ -323,9 +323,11 @@ static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data,
return -EINVAL;
}
- /* Firmware is always aligned to u16 */
+ /* Firmware should be u16 aligned, zero-pad a trailing odd byte */
for (i = 0; i < size; i += 2) {
- val = data[i + 1] << 8 | data[i];
+ val = data[i];
+ if (i + 1 < size)
+ val |= data[i + 1] << 8;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val);
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-28 7:30 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-28 7:30 [PATCH net v3 0/4] net: phy: as21xxx: simple series for trivial fixes Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 1/4] net: phy: as21xxx: handle stale link status during autoneg Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 2/4] net: phy: as21xxx: fix read_status speed handling Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 3/4] net: phy: as21xxx: force C45 OPs for AUTONEG Christian Marangi
2026-10-28 7:30 ` [PATCH net v3 4/4] net: phy: as21xxx: handle odd firmware on loading Christian Marangi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®