The global maximum frame size must be changed with CPU MACs disabled. The old sequence only handled ports 0 and 6, leaving an internal PHY CPU port enabled while the register was updated. Include enabled internal CPU ports in the pause. Serialize the whole MTU update with port enable/disable and phylink link-up/down callbacks, so a link-up cannot re-enable a CPU MAC during the frame-size write. Read and restore each port's original TXMAC/RXMAC bits instead of enabling both unconditionally; a link-down port remains down and LINK_AUTO is preserved. Abort before updating the frame size if a status read or MAC pause fails, attempt to restore any port already touched, and report a restore error when the frame-size write itself succeeded. Keep the existing treatment of ports 0 and 6, including when either is a user port. Assisted-by: LLM Signed-off-by: Yongzhao Chen --- drivers/net/dsa/qca/qca8k-8xxx.c | 3 ++ drivers/net/dsa/qca/qca8k-common.c | 79 ++++++++++++++++++++++++------ drivers/net/dsa/qca/qca8k.h | 2 + 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index 17704cb53..787f30495 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -1486,7 +1486,9 @@ qca8k_phylink_mac_link_up(struct phylink_config *config, reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + mutex_lock(&priv->port_status_mutex); qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); + mutex_unlock(&priv->port_status_mutex); } static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs) @@ -2119,6 +2121,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->ds->ops = &qca8k_switch_ops; priv->ds->phylink_mac_ops = &qca8k_phylink_mac_ops; mutex_init(&priv->reg_mutex); + mutex_init(&priv->port_status_mutex); dev_set_drvdata(&mdiodev->dev, priv); return dsa_register_switch(priv->ds); diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 13005f10e..a584ddf72 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -463,7 +463,8 @@ int qca8k_mib_init(struct qca8k_priv *priv) return ret; } -void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +static void qca8k_port_set_status_locked(struct qca8k_priv *priv, int port, + int enable) { u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; @@ -477,6 +478,13 @@ void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); } +void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +{ + mutex_lock(&priv->port_status_mutex); + qca8k_port_set_status_locked(priv, port, enable); + mutex_unlock(&priv->port_status_mutex); +} + void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) { @@ -751,8 +759,10 @@ int qca8k_port_enable(struct dsa_switch *ds, int port, { struct qca8k_priv *priv = ds->priv; - qca8k_port_set_status(priv, port, 1); + mutex_lock(&priv->port_status_mutex); + qca8k_port_set_status_locked(priv, port, 1); priv->port_enabled_map |= BIT(port); + mutex_unlock(&priv->port_status_mutex); if (dsa_is_user_port(ds, port)) phy_support_asym_pause(phy); @@ -764,14 +774,20 @@ void qca8k_port_disable(struct dsa_switch *ds, int port) { struct qca8k_priv *priv = ds->priv; - qca8k_port_set_status(priv, port, 0); + mutex_lock(&priv->port_status_mutex); + qca8k_port_set_status_locked(priv, port, 0); priv->port_enabled_map &= ~BIT(port); + mutex_unlock(&priv->port_status_mutex); } int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { struct qca8k_priv *priv = ds->priv; - int ret; + u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + u32 status[QCA8K_NUM_PORTS] = { 0 }; + u32 ports; + u32 stopped = 0; + int ret, restore_ret, i; /* We have only have a general MTU setting. * DSA always set the CPU port's MTU to the largest MTU of the user @@ -784,25 +800,58 @@ int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) /* To change the MAX_FRAME_SIZE the cpu ports must be off or * the switch panics. - * Turn off both cpu ports before applying the new value to prevent - * this. + * Include internal PHY CPU ports as well as the two MAC-only ports. + * Toggle only MAC enables, preserving the phylink link-control mode. */ - if (priv->port_enabled_map & BIT(0)) - qca8k_port_set_status(priv, 0, 0); + ports = BIT(0) | BIT(6); + for (i = 1; i < 6; i++) + if (dsa_is_cpu_port(ds, i)) + ports |= BIT(i); - if (priv->port_enabled_map & BIT(6)) - qca8k_port_set_status(priv, 6, 0); + mutex_lock(&priv->port_status_mutex); + ports &= priv->port_enabled_map; + + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + if (!(ports & BIT(i))) + continue; + + ret = regmap_read(priv->regmap, QCA8K_REG_PORT_STATUS(i), + &status[i]); + if (ret) + goto unlock; + } + + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + if (!(ports & BIT(i)) || !(status[i] & mask)) + continue; + + stopped |= BIT(i); + ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(i), + mask); + if (ret) + goto restore; + } /* Include L2 header / FCS length */ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); - if (priv->port_enabled_map & BIT(0)) - qca8k_port_set_status(priv, 0, 1); - - if (priv->port_enabled_map & BIT(6)) - qca8k_port_set_status(priv, 6, 1); +restore: + for (i = 0; i < QCA8K_NUM_PORTS; i++) + if (stopped & BIT(i)) { + restore_ret = regmap_update_bits(priv->regmap, + QCA8K_REG_PORT_STATUS(i), + mask, status[i] & mask); + if (restore_ret) { + dev_err(priv->dev, "failed to restore MAC state on port %d: %d\n", + i, restore_ret); + if (!ret) + ret = restore_ret; + } + } +unlock: + mutex_unlock(&priv->port_status_mutex); return ret; } diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index 956338893..6cb060204 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -459,6 +459,8 @@ struct qca8k_priv { struct mii_bus *internal_mdio_bus; struct dsa_switch *ds; struct mutex reg_mutex; + /* Serializes port MAC state changes with global MTU updates. */ + struct mutex port_status_mutex; struct device *dev; struct gpio_desc *reset_gpio; struct net_device *mgmt_conduit; /* Track if mdio/mib Ethernet is available */ -- 2.43.0