* [PATCH net 1/6] net: dsa: b53: serialize SerDes register transactions
[not found] <20261021191824.752309-1-florian.fainelli@broadcom.com>
@ 2026-10-21 19:18 ` Florian Fainelli
2026-10-21 19:18 ` [PATCH net 3/6] net: dsa: b53: constrain SerDes interfaces to initialized PCSes Florian Fainelli
` (2 subsequent siblings)
3 siblings, 0 replies; 4+ messages in thread
From: Florian Fainelli @ 2026-10-21 19:18 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King, Russell King (Oracle),
open list
The lane and block selectors are shared by both SerDes lanes. Taking
reg_mutex separately for each register access allows another port to
change either selector before the data access. Hold the mutex across the
complete transaction and use the raw bus operations while it is held.
Propagate bus errors instead of returning uninitialized register contents,
only cache a successfully selected lane, and invalidate the cache after
failed accesses. Make the PCS callbacks fail configuration or report link
down on read errors, and reject out-of-range lane indices at discovery.
Fixes: 0e01491de646 ("net: dsa: b53: Add SerDes support")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
drivers/net/dsa/b53/b53_common.c | 1 +
drivers/net/dsa/b53/b53_serdes.c | 148 ++++++++++++++++++++++++-------
2 files changed, 115 insertions(+), 34 deletions(-)
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 0880310c9ce3..3f8f956aab82 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -3203,6 +3203,7 @@ struct b53_device *b53_switch_alloc(struct device *base,
dev->ds = ds;
dev->priv = priv;
dev->ops = ops;
+ dev->serdes_lane = B53_INVALID_LANE;
ds->ops = &b53_switch_ops;
ds->phylink_mac_ops = &b53_phylink_mac_ops;
dev->vlan_enabled = true;
diff --git a/drivers/net/dsa/b53/b53_serdes.c b/drivers/net/dsa/b53/b53_serdes.c
index 7460122f6abc..c90894877626 100644
--- a/drivers/net/dsa/b53/b53_serdes.c
+++ b/drivers/net/dsa/b53/b53_serdes.c
@@ -22,47 +22,96 @@ static inline struct b53_pcs *pcs_to_b53_pcs(struct phylink_pcs *pcs)
return container_of(pcs, struct b53_pcs, pcs);
}
-static void b53_serdes_write_blk(struct b53_device *dev, u8 offset, u16 block,
- u16 value)
+/* The caller holds reg_mutex across lane, block and data accesses. */
+static int b53_serdes_write_blk(struct b53_device *dev, u8 offset, u16 block,
+ u16 value)
{
- b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
- b53_write16(dev, B53_SERDES_PAGE, offset, value);
+ int ret;
+
+ lockdep_assert_held(&dev->reg_mutex);
+
+ ret = dev->ops->write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR,
+ block);
+ if (ret)
+ return ret;
+
+ return dev->ops->write16(dev, B53_SERDES_PAGE, offset, value);
}
-static u16 b53_serdes_read_blk(struct b53_device *dev, u8 offset, u16 block)
+static int b53_serdes_read_blk(struct b53_device *dev, u8 offset, u16 block)
{
u16 value;
+ int ret;
+
+ lockdep_assert_held(&dev->reg_mutex);
- b53_write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR, block);
- b53_read16(dev, B53_SERDES_PAGE, offset, &value);
+ ret = dev->ops->write16(dev, B53_SERDES_PAGE, B53_SERDES_BLKADDR,
+ block);
+ if (ret)
+ return ret;
- return value;
+ ret = dev->ops->read16(dev, B53_SERDES_PAGE, offset, &value);
+ return ret ? ret : value;
}
-static void b53_serdes_set_lane(struct b53_device *dev, u8 lane)
+static int b53_serdes_set_lane(struct b53_device *dev, u8 lane)
{
+ int ret;
+
+ lockdep_assert_held(&dev->reg_mutex);
+
+ if (lane >= B53_N_PCS)
+ return -EINVAL;
+
if (dev->serdes_lane == lane)
- return;
+ return 0;
- WARN_ON(lane > 1);
+ ret = b53_serdes_write_blk(dev, B53_SERDES_LANE,
+ SERDES_XGXSBLK0_BLOCKADDRESS, lane);
+ if (ret)
+ return ret;
- b53_serdes_write_blk(dev, B53_SERDES_LANE,
- SERDES_XGXSBLK0_BLOCKADDRESS, lane);
dev->serdes_lane = lane;
+ return 0;
}
-static void b53_serdes_write(struct b53_device *dev, u8 lane,
- u8 offset, u16 block, u16 value)
+static int b53_serdes_write(struct b53_device *dev, u8 lane,
+ u8 offset, u16 block, u16 value)
{
- b53_serdes_set_lane(dev, lane);
- b53_serdes_write_blk(dev, offset, block, value);
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = b53_serdes_set_lane(dev, lane);
+ if (!ret)
+ ret = b53_serdes_write_blk(dev, offset, block, value);
+ if (ret)
+ dev->serdes_lane = B53_INVALID_LANE;
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
}
-static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
+static int b53_serdes_read(struct b53_device *dev, u8 lane,
u8 offset, u16 block)
{
- b53_serdes_set_lane(dev, lane);
- return b53_serdes_read_blk(dev, offset, block);
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = b53_serdes_set_lane(dev, lane);
+ if (!ret)
+ ret = b53_serdes_read_blk(dev, offset, block);
+ if (ret < 0)
+ dev->serdes_lane = B53_INVALID_LANE;
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static void b53_serdes_error(struct b53_device *dev, u8 lane, int ret)
+{
+ if (ret < 0)
+ dev_err_ratelimited(dev->dev, "SerDes lane %u access failed: %pe\n",
+ lane, ERR_PTR(ret));
}
static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int neg_mode,
@@ -72,31 +121,37 @@ static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int neg_mode,
{
struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
u8 lane = pcs_to_b53_pcs(pcs)->lane;
- u16 reg;
+ int reg;
reg = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
SERDES_DIGITAL_BLK);
+ if (reg < 0)
+ return reg;
+
if (interface == PHY_INTERFACE_MODE_1000BASEX)
reg |= FIBER_MODE_1000X;
else
reg &= ~FIBER_MODE_1000X;
- b53_serdes_write(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
- SERDES_DIGITAL_BLK, reg);
-
- return 0;
+ return b53_serdes_write(dev, lane, B53_SERDES_DIGITAL_CONTROL(1),
+ SERDES_DIGITAL_BLK, reg);
}
static void b53_serdes_an_restart(struct phylink_pcs *pcs)
{
struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
u8 lane = pcs_to_b53_pcs(pcs)->lane;
- u16 reg;
+ int reg;
reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
SERDES_MII_BLK);
+ if (reg < 0)
+ goto out;
+
reg |= BMCR_ANRESTART;
- b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
- SERDES_MII_BLK, reg);
+ reg = b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
+ SERDES_MII_BLK, reg);
+out:
+ b53_serdes_error(dev, lane, reg);
}
static void b53_serdes_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
@@ -104,12 +159,23 @@ static void b53_serdes_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
{
struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
u8 lane = pcs_to_b53_pcs(pcs)->lane;
- u16 dig, bmsr;
+ int dig, bmsr;
dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
SERDES_DIGITAL_BLK);
+ if (dig < 0) {
+ state->link = false;
+ b53_serdes_error(dev, lane, dig);
+ return;
+ }
+
bmsr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMSR),
SERDES_MII_BLK);
+ if (bmsr < 0) {
+ state->link = false;
+ b53_serdes_error(dev, lane, bmsr);
+ return;
+ }
switch ((dig >> SPEED_STATUS_SHIFT) & SPEED_STATUS_MASK) {
case SPEED_STATUS_10:
@@ -140,19 +206,24 @@ void b53_serdes_link_set(struct b53_device *dev, int port, unsigned int mode,
phy_interface_t interface, bool link_up)
{
u8 lane = b53_serdes_map_lane(dev, port);
- u16 reg;
+ int reg;
if (lane == B53_INVALID_LANE)
return;
reg = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
SERDES_MII_BLK);
+ if (reg < 0)
+ goto out;
+
if (link_up)
reg &= ~BMCR_PDOWN;
else
reg |= BMCR_PDOWN;
- b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
- SERDES_MII_BLK, reg);
+ reg = b53_serdes_write(dev, lane, B53_SERDES_MII_REG(MII_BMCR),
+ SERDES_MII_BLK, reg);
+out:
+ b53_serdes_error(dev, lane, reg);
}
EXPORT_SYMBOL(b53_serdes_link_set);
@@ -213,16 +284,25 @@ int b53_serdes_init(struct b53_device *dev, int port)
{
u8 lane = b53_serdes_map_lane(dev, port);
struct b53_pcs *pcs;
- u16 id0, msb, lsb;
+ int id0, msb, lsb;
- if (lane == B53_INVALID_LANE)
+ if (lane >= B53_N_PCS)
return -EINVAL;
id0 = b53_serdes_read(dev, lane, B53_SERDES_ID0, SERDES_ID0);
+ if (id0 < 0)
+ return id0;
+
msb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID1),
SERDES_MII_BLK);
+ if (msb < 0)
+ return msb;
+
lsb = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_PHYSID2),
SERDES_MII_BLK);
+ if (lsb < 0)
+ return lsb;
+
if (id0 == 0 || id0 == 0xffff) {
dev_err(dev->dev, "SerDes not initialized, check settings\n");
return -ENODEV;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net 3/6] net: dsa: b53: constrain SerDes interfaces to initialized PCSes
[not found] <20261021191824.752309-1-florian.fainelli@broadcom.com>
2026-10-21 19:18 ` [PATCH net 1/6] net: dsa: b53: serialize SerDes register transactions Florian Fainelli
@ 2026-10-21 19:18 ` Florian Fainelli
2026-10-21 19:18 ` [PATCH net 5/6] net: dsa: b53: report link state through the PCS Florian Fainelli
2026-10-21 19:18 ` [PATCH net 6/6] net: dsa: b53: apply resolved link settings to serial port MACs Florian Fainelli
3 siblings, 0 replies; 4+ messages in thread
From: Florian Fainelli @ 2026-10-21 19:18 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King, Russell King (Oracle),
open list
Publish the supported interface bitmap on each successfully discovered
PCS and use it for capability reporting and PCS selection. Clear the
parallel interfaces inherited from the common switch code on ports whose
mux selects a SerDes. A failed discovery must not leave a usable serial
interface advertised without its PCS.
Stop advertising 2500BASE-X: the driver neither selects its baud rate nor
supports 2.5G in the MAC override code. The downstream robo SerDes speed
setter likewise only implements 10/100/1000. Keep SGMII and 1000BASE-X on
both lanes until a complete 2.5G programming path is available.
Report discovery failures at the SRAB port to make missing capabilities
diagnosable.
Fixes: 0e01491de646 ("net: dsa: b53: Add SerDes support")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
drivers/net/dsa/b53/b53_serdes.c | 28 ++++++++--------------------
drivers/net/dsa/b53/b53_srab.c | 9 +++++++--
2 files changed, 15 insertions(+), 22 deletions(-)
diff --git a/drivers/net/dsa/b53/b53_serdes.c b/drivers/net/dsa/b53/b53_serdes.c
index 87771e3788f7..d521050d7c8f 100644
--- a/drivers/net/dsa/b53/b53_serdes.c
+++ b/drivers/net/dsa/b53/b53_serdes.c
@@ -273,24 +273,11 @@ void b53_serdes_phylink_get_caps(struct b53_device *dev, int port,
if (lane == B53_INVALID_LANE)
return;
- switch (lane) {
- case 0:
- /* It appears lane 0 supports 2500base-X and 1000base-X */
- __set_bit(PHY_INTERFACE_MODE_2500BASEX,
- config->supported_interfaces);
- config->mac_capabilities |= MAC_2500FD;
- fallthrough;
- case 1:
- /* It appears lane 1 only supports 1000base-X and SGMII */
- __set_bit(PHY_INTERFACE_MODE_1000BASEX,
- config->supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_SGMII,
- config->supported_interfaces);
- config->mac_capabilities |= MAC_1000FD;
- break;
- default:
- break;
- }
+ if (lane >= B53_N_PCS || !dev->pcs[lane].dev)
+ return;
+
+ phy_interface_copy(config->supported_interfaces,
+ dev->pcs[lane].pcs.supported_interfaces);
}
EXPORT_SYMBOL(b53_serdes_phylink_get_caps);
@@ -304,8 +291,7 @@ struct phylink_pcs *b53_serdes_phylink_mac_select_pcs(struct b53_device *dev,
!dev->pcs[lane].dev)
return NULL;
- if (!phy_interface_mode_is_8023z(interface) &&
- interface != PHY_INTERFACE_MODE_SGMII)
+ if (!test_bit(interface, dev->pcs[lane].pcs.supported_interfaces))
return NULL;
return &dev->pcs[lane].pcs;
@@ -351,6 +337,8 @@ int b53_serdes_init(struct b53_device *dev, int port)
pcs->dev = dev;
pcs->lane = lane;
pcs->pcs.ops = &b53_pcs_ops;
+ __set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, pcs->pcs.supported_interfaces);
return 0;
}
diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c
index 9988a83fa7b7..77e81e1a414a 100644
--- a/drivers/net/dsa/b53/b53_srab.c
+++ b/drivers/net/dsa/b53/b53_srab.c
@@ -451,9 +451,11 @@ static void b53_srab_phylink_get_caps(struct b53_device *dev, int port,
switch (p->mode) {
case PHY_INTERFACE_MODE_SGMII:
+ /* The mux connects this port to a SerDes, not a parallel bus. */
+ phy_interface_zero(config->supported_interfaces);
#if IS_ENABLED(CONFIG_B53_SERDES)
/* If p->mode indicates SGMII mode, that essentially means we
- * are using a serdes. As the serdes for the capabilities.
+ * are using a SerDes. Ask it for the supported interfaces.
*/
b53_serdes_phylink_get_caps(dev, port, config);
#endif
@@ -588,8 +590,11 @@ static void b53_srab_mux_init(struct platform_device *pdev)
case MUX_CONFIG_SGMII:
p->mode = PHY_INTERFACE_MODE_SGMII;
ret = b53_serdes_init(dev, port);
- if (ret)
+ if (ret) {
+ dev_err(dev->dev, "port %d SerDes unavailable: %pe\n",
+ port, ERR_PTR(ret));
continue;
+ }
break;
case MUX_CONFIG_MII_LITE:
p->mode = PHY_INTERFACE_MODE_MII;
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net 5/6] net: dsa: b53: report link state through the PCS
[not found] <20261021191824.752309-1-florian.fainelli@broadcom.com>
2026-10-21 19:18 ` [PATCH net 1/6] net: dsa: b53: serialize SerDes register transactions Florian Fainelli
2026-10-21 19:18 ` [PATCH net 3/6] net: dsa: b53: constrain SerDes interfaces to initialized PCSes Florian Fainelli
@ 2026-10-21 19:18 ` Florian Fainelli
2026-10-21 19:18 ` [PATCH net 6/6] net: dsa: b53: apply resolved link settings to serial port MACs Florian Fainelli
3 siblings, 0 replies; 4+ messages in thread
From: Florian Fainelli @ 2026-10-21 19:18 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King, Russell King (Oracle),
open list
Decode the Clause 22 BMSR and link partner word with the phylink helper.
This respects the negotiation mode, reports the partner advertisement,
and resolves Clause 37 pause from the configured advertisement instead
of unconditionally accepting the vendor status register's result.
Notify phylink through phylink_pcs_change() from the SerDes port interrupt,
using live PCS status rather than MAC link status, which can be overridden
by mac_link_up/down(). Remove the now unused MAC notification helper.
Enable PCS polling as a fallback for optional port interrupts and for
negotiation completion after the initial link-change interrupt.
Fixes: 79396934e289 ("net: dsa: b53: convert to phylink_pcs")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
drivers/net/dsa/b53/b53_common.c | 12 ------
drivers/net/dsa/b53/b53_priv.h | 1 -
drivers/net/dsa/b53/b53_serdes.c | 72 +++++++++++++++-----------------
drivers/net/dsa/b53/b53_serdes.h | 5 +++
drivers/net/dsa/b53/b53_srab.c | 2 +-
5 files changed, 40 insertions(+), 52 deletions(-)
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 09de3520e71a..b04f9d5f0525 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1522,18 +1522,6 @@ static void b53_adjust_5325_mii(struct dsa_switch *ds, int port)
}
}
-void b53_port_event(struct dsa_switch *ds, int port)
-{
- struct b53_device *dev = ds->priv;
- bool link;
- u16 sts;
-
- b53_read16(dev, B53_STAT_PAGE, B53_LINK_STAT, &sts);
- link = !!(sts & BIT(port));
- dsa_port_phylink_mac_change(ds, port, link);
-}
-EXPORT_SYMBOL(b53_port_event);
-
static void b53_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index cebbebe4ae0f..6028440e1358 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -519,7 +519,6 @@ int b53_br_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack);
int b53_setup_devlink_resources(struct dsa_switch *ds);
-void b53_port_event(struct dsa_switch *ds, int port);
int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
struct netlink_ext_ack *extack);
int b53_vlan_add(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/b53/b53_serdes.c b/drivers/net/dsa/b53/b53_serdes.c
index fc20e211e67e..c7f3e131f2de 100644
--- a/drivers/net/dsa/b53/b53_serdes.c
+++ b/drivers/net/dsa/b53/b53_serdes.c
@@ -291,50 +291,44 @@ static void b53_serdes_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
static void b53_serdes_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
struct phylink_link_state *state)
{
- struct b53_device *dev = pcs_to_b53_pcs(pcs)->dev;
- u8 lane = pcs_to_b53_pcs(pcs)->lane;
- int dig, bmsr;
-
- dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
- SERDES_DIGITAL_BLK);
- if (dig < 0) {
- state->link = false;
- b53_serdes_error(dev, lane, dig);
- return;
- }
+ struct b53_pcs *priv = pcs_to_b53_pcs(pcs);
+ struct b53_device *dev = priv->dev;
+ u8 lane = priv->lane;
+ int bmsr, lpa;
bmsr = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_BMSR),
SERDES_MII_BLK);
- if (bmsr < 0) {
- state->link = false;
- b53_serdes_error(dev, lane, bmsr);
- return;
- }
+ if (bmsr < 0)
+ goto error;
- switch ((dig >> SPEED_STATUS_SHIFT) & SPEED_STATUS_MASK) {
- case SPEED_STATUS_10:
- state->speed = SPEED_10;
- break;
- case SPEED_STATUS_100:
- state->speed = SPEED_100;
- break;
- case SPEED_STATUS_1000:
- state->speed = SPEED_1000;
- break;
- default:
- case SPEED_STATUS_2500:
- state->speed = SPEED_2500;
- break;
- }
+ lpa = b53_serdes_read(dev, lane, B53_SERDES_MII_REG(MII_LPA),
+ SERDES_MII_BLK);
+ if (lpa < 0)
+ goto error;
+
+ phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa);
+ return;
+error:
+ state->link = false;
+ state->an_complete = false;
+ b53_serdes_error(dev, lane, bmsr < 0 ? bmsr : lpa);
+}
+
+void b53_serdes_link_change(struct b53_device *dev, int port)
+{
+ u8 lane = b53_serdes_map_lane(dev, port);
+ int dig;
- state->duplex = dig & DUPLEX_STATUS ? DUPLEX_FULL : DUPLEX_HALF;
- state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
- state->link = !!(dig & LINK_STATUS);
- if (dig & PAUSE_RESOLUTION_RX_SIDE)
- state->pause |= MLO_PAUSE_RX;
- if (dig & PAUSE_RESOLUTION_TX_SIDE)
- state->pause |= MLO_PAUSE_TX;
+ if (lane >= B53_N_PCS || !dev->pcs[lane].dev)
+ return;
+
+ /* Use the live PCS status, not latched BMSR or MAC override status. */
+ dig = b53_serdes_read(dev, lane, B53_SERDES_DIGITAL_STATUS,
+ SERDES_DIGITAL_BLK);
+ b53_serdes_error(dev, lane, dig);
+ phylink_pcs_change(&dev->pcs[lane].pcs, dig >= 0 && (dig & LINK_STATUS));
}
+EXPORT_SYMBOL(b53_serdes_link_change);
static int b53_serdes_enable(struct phylink_pcs *pcs)
{
@@ -440,6 +434,8 @@ int b53_serdes_init(struct b53_device *dev, int port)
pcs->dev = dev;
pcs->lane = lane;
pcs->pcs.ops = &b53_pcs_ops;
+ /* Port interrupts are optional and may precede AN completion. */
+ pcs->pcs.poll = true;
__set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, pcs->pcs.supported_interfaces);
diff --git a/drivers/net/dsa/b53/b53_serdes.h b/drivers/net/dsa/b53/b53_serdes.h
index 2c2aa5eaa985..4016b24bcda4 100644
--- a/drivers/net/dsa/b53/b53_serdes.h
+++ b/drivers/net/dsa/b53/b53_serdes.h
@@ -119,8 +119,13 @@ struct phylink_pcs *b53_serdes_phylink_mac_select_pcs(struct b53_device *dev,
void b53_serdes_phylink_get_caps(struct b53_device *dev, int port,
struct phylink_config *config);
#if IS_ENABLED(CONFIG_B53_SERDES)
+void b53_serdes_link_change(struct b53_device *dev, int port);
int b53_serdes_init(struct b53_device *dev, int port);
#else
+static inline void b53_serdes_link_change(struct b53_device *dev, int port)
+{
+}
+
static inline int b53_serdes_init(struct b53_device *dev, int port)
{
return -ENODEV;
diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c
index 77e81e1a414a..56da3dbaf997 100644
--- a/drivers/net/dsa/b53/b53_srab.c
+++ b/drivers/net/dsa/b53/b53_srab.c
@@ -374,7 +374,7 @@ static irqreturn_t b53_srab_port_thread(int irq, void *dev_id)
struct b53_device *dev = port->dev;
if (port->mode == PHY_INTERFACE_MODE_SGMII)
- b53_port_event(dev->ds, port->num);
+ b53_serdes_link_change(dev, port->num);
return IRQ_HANDLED;
}
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net 6/6] net: dsa: b53: apply resolved link settings to serial port MACs
[not found] <20261021191824.752309-1-florian.fainelli@broadcom.com>
` (2 preceding siblings ...)
2026-10-21 19:18 ` [PATCH net 5/6] net: dsa: b53: report link state through the PCS Florian Fainelli
@ 2026-10-21 19:18 ` Florian Fainelli
3 siblings, 0 replies; 4+ messages in thread
From: Florian Fainelli @ 2026-10-21 19:18 UTC (permalink / raw)
To: netdev
Cc: Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King, Russell King (Oracle),
open list
The serial port MAC callbacks leave speed, duplex and pause under the
hardware's autonomous control. In particular, changing phylink's pause
policy cannot affect the MAC when Clause 37 negotiation is in use.
Use the existing MAC override registers for SGMII and 1000BASE-X links,
including PHY and fixed-link negotiation modes. Apply the settings passed
by phylink at link-up and force the MAC link down at link-down. PCS power
and link observation remain independent of the MAC override.
Preserve the existing parallel-port handling, EEE initialization and
BCM5301x CPU-port pause quirk.
Fixes: 0e01491de646 ("net: dsa: b53: Add SerDes support")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
drivers/net/dsa/b53/b53_common.c | 50 +++++++++++++++-----------------
1 file changed, 23 insertions(+), 27 deletions(-)
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index b04f9d5f0525..172ada124cbf 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1600,6 +1600,17 @@ static void b53_phylink_mac_config(struct phylink_config *config,
}
}
+static bool b53_phylink_mac_needs_override(struct b53_device *dev, int port,
+ unsigned int mode,
+ phy_interface_t interface)
+{
+ return mode == MLO_AN_FIXED ||
+ interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_1000BASEX ||
+ (mode == MLO_AN_PHY && is63xx(dev) &&
+ in_range(port, B53_63XX_RGMII0, 4));
+}
+
static void b53_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
@@ -1608,16 +1619,8 @@ static void b53_phylink_mac_link_down(struct phylink_config *config,
struct b53_device *dev = dp->ds->priv;
int port = dp->index;
- if (mode == MLO_AN_PHY) {
- if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4))
- b53_force_link(dev, port, false);
- return;
- }
-
- if (mode == MLO_AN_FIXED) {
+ if (b53_phylink_mac_needs_override(dev, port, mode, interface))
b53_force_link(dev, port, false);
- return;
- }
}
static void b53_phylink_mac_link_up(struct phylink_config *config,
@@ -1633,29 +1636,22 @@ static void b53_phylink_mac_link_up(struct phylink_config *config,
struct ethtool_keee *p = &dev->ports[dp->index].eee;
int port = dp->index;
- if (mode == MLO_AN_PHY) {
- /* Re-negotiate EEE if it was enabled already */
+ /* Re-negotiate EEE if it was enabled already */
+ if (mode == MLO_AN_PHY)
p->eee_enabled = b53_eee_init(ds, port, phydev);
- if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4)) {
- b53_force_port_config(dev, port, speed, duplex,
- tx_pause, rx_pause);
- b53_force_link(dev, port, true);
- }
-
+ if (!b53_phylink_mac_needs_override(dev, port, mode, interface))
return;
- }
- if (mode == MLO_AN_FIXED) {
- /* Force flow control on BCM5301x's CPU port */
- if (is5301x(dev) && dsa_is_cpu_port(ds, port))
- tx_pause = rx_pause = true;
-
- b53_force_port_config(dev, port, speed, duplex,
- tx_pause, rx_pause);
- b53_force_link(dev, port, true);
- return;
+ /* Force flow control on BCM5301x's CPU port */
+ if (mode == MLO_AN_FIXED && is5301x(dev) && dsa_is_cpu_port(ds, port)) {
+ tx_pause = true;
+ rx_pause = true;
}
+
+ /* PCS negotiation supplies state; the MAC applies phylink's policy. */
+ b53_force_port_config(dev, port, speed, duplex, tx_pause, rx_pause);
+ b53_force_link(dev, port, true);
}
int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-21 19:18 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20261021191824.752309-1-florian.fainelli@broadcom.com>
2026-10-21 19:18 ` [PATCH net 1/6] net: dsa: b53: serialize SerDes register transactions Florian Fainelli
2026-10-21 19:18 ` [PATCH net 3/6] net: dsa: b53: constrain SerDes interfaces to initialized PCSes Florian Fainelli
2026-10-21 19:18 ` [PATCH net 5/6] net: dsa: b53: report link state through the PCS Florian Fainelli
2026-10-21 19:18 ` [PATCH net 6/6] net: dsa: b53: apply resolved link settings to serial port MACs Florian Fainelli
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®