* [PATCH net-next v4 0/3] net: dsa: qca8k: add a QCA8337 CPU PHY consumer
@ 2026-09-28 22:08 Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 1/3] net: dsa: pass PHY flags when connecting shared ports Yongzhao Chen
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Yongzhao Chen @ 2026-09-28 22:08 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
Woojung Huh, UNGLinuxDriver, Russell King, linux-doc,
Christian Marangi, linux-kernel, Ziyang Huang
This series lets a QCA8337 use one of its internal PHYs (ports 1-5) as
the CPU port, with a PHY-to-PHY connection to the SoC's own PHY.
Patch 1 makes DSA pass get_phy_flags() to PHYs on CPU and DSA ports, so
that the internal CPU PHY receives the switch revision and gets the same
revision-specific initialization as the user ports. Patch 2 pauses
internal CPU ports as well as ports 0 and 6 during MTU changes and
serializes that sequence with reg_mutex. Patch 3 accepts an internal PHY
as the CPU port on QCA8337.
Changes since v3:
- Dropped v3 patches 4/5 and 5/5, which disabled SmartSpeed on internal
CPU PHYs, and did not add the DT workaround property discussed in the
v3 4/5 thread. The lost 1000BASE-T advertisement on the RA74 CPU link
came from the IPQ5018 GE PHY's initialization order, not from the
QCA8337. The IPQ5018 driver writes its analog settings (LDO, EEE, MSE,
DAC) only in config_init() when the MAC attaches, about 38 s after
PHY4 starts autonegotiating. Until then the IPQ5018 PHY negotiates with
its reset defaults, 1000BASE-T does not come up, and SmartSpeed on
both sides drops the 1000BASE-T advertisement. Nothing restores it on
PHY4. On RA74, applying the settings at probe brought the CPU link up
at 1 Gb/s with SmartSpeed left enabled. That fix is a separate net
series:
net: phy: qcom: at803x: Fix IPQ5018 short-cable DAC values
net: phy: qcom: at803x: Apply IPQ5018 analog settings at probe
<https://lore.kernel.org/netdev/20260928220717.939-1-yongzhao.derek@gmail.com/>
- Patch 1: removed the SmartSpeed reference from the commit message.
The code is unchanged, so I kept Florian's Reviewed-by.
- Patch 3: the commit message no longer refers to an RFC. Internal CPU
port selection is still limited to QCA8337; QCA8327 support has not
been established. The code is unchanged.
- Patch 2 is unchanged.
- Posted as a regular patch series instead of an RFC.
Dependencies:
This series depends on the net series "[PATCH net v2 0/3] net: dsa:
qca8k: fix MDIO error handling" <https://lore.kernel.org/netdev/20260928220629.238-1-yongzhao.derek@gmail.com/>. It applies to
net-next without that series, but patch 2 aborts and restores the
paused ports only when register writes report MDIO failures, which
needs its first patch. Please take the net series first.
Brandon Mahdavi's multi-CPU RFC v2 [1] also replaces
qca8k_find_cpu_port() and restricts CPU ports to 0 and 6. It is not
merged. Whichever series lands second will need to handle the other's
CPU port selection.
Testing:
Each patch builds with W=1 for arm64 (drivers/net/dsa/qca, net/dsa,
drivers/net/phy/qcom, bcm_sf2, microchip) on the net-next base below
with the MDIO series, and checkpatch --strict is clean apart from
sign-offs.
The MTU/pause model test from v3 passes on patch 2 on top of the MDIO
series. A model test of patch 3 checks CPU port selection for every
CPU port mask on QCA8337 and QCA8327, and which ports count for the
MDIO bus choice. The code of all three patches is unchanged from v3.
On hardware, a Redmi AX5400 (IPQ5018 GE PHY to QCA8337 PHY4 as the CPU
port, plus port 6 as a second CPU port) was tested on OpenWrt's Linux
6.18.52 with a backport of all three patches, the MDIO series and the at803x
fixes, and no SmartSpeed change. OpenWrt's multi-CPU patches stay on
top and remove qca8k_find_cpu_port(), so on this board patch 3 only
contributes its MDIO scope change; its CPU port fallback is covered by
the model test only. The CPU link was verified at 1 Gb/s with SmartSpeed
enabled after a first boot, three reboots, a power-off cold boot, three
interface down/up cycles, three renegotiations and a network restart.
During a separate 10-minute observation, sampled link status remained
at 1 Gb/s with SmartSpeed enabled and no new CPU PHY link-down events
were logged. Raising a user port MTU to 1504 and back exercises patch
2's pause sequence. After each change, WAN HTTPS through CPU port 5
and SSH through CPU port 6 worked. Packet loss during the changes was
not measured. An MTU above the maximum was rejected. The switch
registers are not readable on that image, so MAX_FRAME_SIZE and the
MAC enable bits were not checked directly. There is no net-next boot
on this board, as it has no upstream DTS.
Thanks to Andrew Lunn for his detailed reviews of v3 4/5, and to Ziyang
Huang for the OpenWrt PHY-to-PHY CPU port code that patch 3 is based on
and for pointing me at the IPQ5018 DAC settings.
v3: https://lore.kernel.org/netdev/20260923215858.1653-1-yongzhao.derek@gmail.com/
v2: https://lore.kernel.org/netdev/20260922202653.1153-1-yongzhao.derek@gmail.com/
v1: https://lore.kernel.org/netdev/20260919085406.1395-1-yongzhao.derek@gmail.com/
[1] https://lore.kernel.org/netdev/20260802034701.3339052-1-brandon.mahdavi@mahcom.com/
Yongzhao Chen (2):
net: dsa: pass PHY flags when connecting shared ports
net: dsa: qca8k: serialize CPU MAC pause during MTU changes
Ziyang Huang (1):
net: dsa: qca8k: support QCA8337 internal PHY CPU links
Documentation/networking/dsa/dsa.rst | 2 +
drivers/net/dsa/bcm_sf2.c | 4 ++
drivers/net/dsa/microchip/ksz8.c | 4 ++
drivers/net/dsa/qca/qca8k-8xxx.c | 20 +++++--
drivers/net/dsa/qca/qca8k-common.c | 79 ++++++++++++++++++++++------
net/dsa/port.c | 6 ++-
6 files changed, 94 insertions(+), 21 deletions(-)
base-commit: 014d795c73837ea2339a4ea8e8f82c6e959b845d
prerequisite-patch-id: aad3a7fefa50cc4a8d29711256b182827e5b3bf2
prerequisite-patch-id: 92a623b4a20a1d2d516476558e3904d7a1c17b8f
prerequisite-patch-id: 2819bb7310717899a1fb577b9f4c71a6443a4340
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v4 1/3] net: dsa: pass PHY flags when connecting shared ports
2026-09-28 22:08 [PATCH net-next v4 0/3] net: dsa: qca8k: add a QCA8337 CPU PHY consumer Yongzhao Chen
@ 2026-09-28 22:08 ` Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 2/3] net: dsa: qca8k: serialize CPU MAC pause during MTU changes Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 3/3] net: dsa: qca8k: support QCA8337 internal PHY CPU links Yongzhao Chen
2 siblings, 0 replies; 6+ messages in thread
From: Yongzhao Chen @ 2026-09-28 22:08 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, Randy Dunlap,
Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
Woojung Huh, UNGLinuxDriver, Russell King, linux-doc,
linux-kernel, Ziyang Huang
DSA calls get_phy_flags() for user ports, but passes zero when connecting
CPU or DSA port PHYs. Pass the callback result before PHY initialization
for shared ports too. Drivers without the callback still pass zero.
Keep bcm_sf2 and ksz88xx shared-port flags at zero, preserving their
existing behavior. Document the extended callback scope.
This lets qca8k pass its switch revision to an internal PHY used as a
CPU link, so the PHY driver applies the same revision-specific
initialization as on user ports. A later patch in this series adds
that user.
Assisted-by: LLM
Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
Documentation/networking/dsa/dsa.rst | 2 ++
drivers/net/dsa/bcm_sf2.c | 4 ++++
drivers/net/dsa/microchip/ksz8.c | 4 ++++
net/dsa/port.c | 6 +++++-
4 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst
index 7edfdd555f0..647f952e397 100644
--- a/Documentation/networking/dsa/dsa.rst
+++ b/Documentation/networking/dsa/dsa.rst
@@ -668,6 +668,8 @@ PHY devices and link management
on its own (e.g.: coming from switch memory mapped registers), this function
should return a 32-bit bitmask of "flags" that is private between the switch
driver and the Ethernet PHY driver in ``drivers/net/phy/\*``.
+ It is called when connecting PHYs for user, CPU and DSA ports. Drivers
+ should return zero for ports that do not need switch-specific PHY flags.
- ``phy_read``: Function invoked by the DSA user MDIO bus when attempting to read
the switch port MDIO registers. If unavailable, return 0xffff for each read.
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 9e571301518..f516fc396c8 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -709,6 +709,10 @@ static u32 bcm_sf2_sw_get_phy_flags(struct dsa_switch *ds, int port)
{
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ /* Shared ports previously received no PHY flags. */
+ if (!dsa_is_user_port(ds, port))
+ return 0;
+
/* The BCM7xxx PHY driver expects to find the integrated PHY revision
* in bits 15:8 and the patch level in bits 7:0 which is exactly what
* the REG_PHY_REVISION register layout is.
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index d7498132064..be8861a7a76 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -3076,6 +3076,10 @@ static u32 ksz88xx_get_phy_flags(struct dsa_switch *ds, int port)
{
struct ksz_device *dev = ds->priv;
+ /* Shared ports previously received no PHY flags. */
+ if (!dsa_is_user_port(ds, port))
+ return 0;
+
switch (dev->chip_id) {
case KSZ88X3_CHIP_ID:
/* Silicon Errata Sheet (DS80000830A):
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 1f5536c0dff..4db7e6f9ce5 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1666,6 +1666,7 @@ static int dsa_shared_port_phylink_register(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
struct device_node *port_dn = dp->dn;
+ u32 phy_flags = 0;
int err;
dp->pl_config.dev = ds->dev;
@@ -1675,7 +1676,10 @@ static int dsa_shared_port_phylink_register(struct dsa_port *dp)
if (err)
return err;
- err = phylink_of_phy_connect(dp->pl, port_dn, 0);
+ if (ds->ops->get_phy_flags)
+ phy_flags = ds->ops->get_phy_flags(ds, dp->index);
+
+ err = phylink_of_phy_connect(dp->pl, port_dn, phy_flags);
if (err && err != -ENODEV) {
pr_err("could not attach to PHY: %d\n", err);
goto err_phy_connect;
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v4 2/3] net: dsa: qca8k: serialize CPU MAC pause during MTU changes
2026-09-28 22:08 [PATCH net-next v4 0/3] net: dsa: qca8k: add a QCA8337 CPU PHY consumer Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 1/3] net: dsa: pass PHY flags when connecting shared ports Yongzhao Chen
@ 2026-09-28 22:08 ` Yongzhao Chen
2026-09-29 5:57 ` Christian Marangi
2026-09-28 22:08 ` [PATCH net-next v4 3/3] net: dsa: qca8k: support QCA8337 internal PHY CPU links Yongzhao Chen
2 siblings, 1 reply; 6+ messages in thread
From: Yongzhao Chen @ 2026-09-28 22:08 UTC (permalink / raw)
To: netdev
Cc: Christian Marangi, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
Florian Fainelli, linux-kernel, Ziyang Huang
The global maximum frame size must be updated with CPU MACs disabled.
The previous logic only paused ports 0 and 6, leaving an internal PHY
CPU port enabled while modifying the register.
Include enabled internal CPU ports in the pause sequence. Use the
existing reg_mutex to serialize the MTU update against port enable, port
disable, and phylink link-up and link-down transitions. Read and restore
each port's original TXMAC and RXMAC bits, ensuring ports that were down
remain down and preserving LINK_AUTO. Retain existing handling for ports
0 and 6.
Abort before updating the frame size if reading port status or pausing
the MAC fails. Attempt to restore all ports already modified, and report
any restoration failures even if an earlier error occurred.
The standalone qca8k MDIO error-propagation fix is a prerequisite for
this series; that error-handling bug predates this locking change.
Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
Assisted-by: LLM
---
drivers/net/dsa/qca/qca8k-8xxx.c | 2 +
drivers/net/dsa/qca/qca8k-common.c | 79 ++++++++++++++++++++++++------
2 files changed, 66 insertions(+), 15 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 89113d22d5d..7bd9d9abcef 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1495,7 +1495,9 @@ qca8k_phylink_mac_link_up(struct phylink_config *config,
reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
+ mutex_lock(&priv->reg_mutex);
qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
+ mutex_unlock(&priv->reg_mutex);
}
static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs)
diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c
index 13005f10edb..6b32bdd75ea 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->reg_mutex);
+ qca8k_port_set_status_locked(priv, port, enable);
+ mutex_unlock(&priv->reg_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->reg_mutex);
+ qca8k_port_set_status_locked(priv, port, 1);
priv->port_enabled_map |= BIT(port);
+ mutex_unlock(&priv->reg_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->reg_mutex);
+ qca8k_port_set_status_locked(priv, port, 0);
priv->port_enabled_map &= ~BIT(port);
+ mutex_unlock(&priv->reg_mutex);
}
int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
{
+ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
struct qca8k_priv *priv = ds->priv;
- int ret;
+ u32 status[QCA8K_NUM_PORTS] = { 0 };
+ int ret, restore_ret, i;
+ u32 stopped = 0;
+ u32 ports;
/* 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->reg_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->reg_mutex);
return ret;
}
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v4 3/3] net: dsa: qca8k: support QCA8337 internal PHY CPU links
2026-09-28 22:08 [PATCH net-next v4 0/3] net: dsa: qca8k: add a QCA8337 CPU PHY consumer Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 1/3] net: dsa: pass PHY flags when connecting shared ports Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 2/3] net: dsa: qca8k: serialize CPU MAC pause during MTU changes Yongzhao Chen
@ 2026-09-28 22:08 ` Yongzhao Chen
2026-09-29 6:04 ` Christian Marangi
2 siblings, 1 reply; 6+ messages in thread
From: Yongzhao Chen @ 2026-09-28 22:08 UTC (permalink / raw)
To: netdev
Cc: Ziyang Huang, Christian Marangi, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Florian Fainelli, linux-kernel
From: Ziyang Huang <hzyitc@outlook.com>
A PHY-to-PHY CPU link connects the SoC PHY directly to an internal
switch PHY. The QCA8337 supports header mode on these ports, and
existing phylink callbacks already handle their internal interfaces.
Allow QCA8337 CPU port selection to fall back to ports 1 through 5 after
checking the dedicated MAC-only ports. Preserve the preference for CPU
ports 0 and 6 across all switch models, limiting the internal-port
fallback to QCA8337. Support for QCA8327 internal CPU links has not
been established and is not enabled. Include internal CPU PHYs when
selecting the PHY access method, without altering the handling of
MAC-only or external user ports.
This enables a single internal CPU PHY configured with an explicit
phy-handle and phy-mode = "internal". The conduit interface uses its own
PHY on the opposite side of the MDI connection. Existing single-CPU DSA
forwarding uses the selected port for default flooding and membership
without extra routing changes.
Adapted from the OpenWrt PHY-to-PHY CPU link patch, narrowing the MDIO
filter adjustments to preserve handling for external user ports.
[yongzhao: preserve external user-port handling on port 6 and limit
internal CPU PHY support to QCA8337]
Assisted-by: LLM
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 7bd9d9abcef..cd36efd1c4f 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1026,7 +1026,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv)
return ret;
}
- if (!dsa_is_user_port(priv->ds, reg))
+ if (!dsa_is_user_port(priv->ds, reg) &&
+ !(reg > 0 && reg < 6 && dsa_is_cpu_port(priv->ds, reg)))
continue;
of_get_phy_mode(port, &mode);
@@ -1102,16 +1103,23 @@ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
static int qca8k_find_cpu_port(struct dsa_switch *ds)
{
struct qca8k_priv *priv = ds->priv;
+ int port;
- /* Find the connected cpu port. Valid port are 0 or 6 */
if (dsa_is_cpu_port(ds, 0))
return 0;
- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
-
if (dsa_is_cpu_port(ds, 6))
return 6;
+ /* Internal PHY CPU port selection is currently enabled for QCA8337. */
+ if (priv->switch_id != QCA8K_ID_QCA8337)
+ return -EINVAL;
+
+ /* An internal PHY can provide a PHY-to-PHY CPU link. */
+ for (port = 1; port < 6; port++)
+ if (dsa_is_cpu_port(ds, port))
+ return port;
+
return -EINVAL;
}
@@ -1863,7 +1871,7 @@ qca8k_setup(struct dsa_switch *ds)
cpu_port = qca8k_find_cpu_port(ds);
if (cpu_port < 0) {
- dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
+ dev_err(priv->dev, "No CPU port configured");
return cpu_port;
}
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v4 2/3] net: dsa: qca8k: serialize CPU MAC pause during MTU changes
2026-09-28 22:08 ` [PATCH net-next v4 2/3] net: dsa: qca8k: serialize CPU MAC pause during MTU changes Yongzhao Chen
@ 2026-09-29 5:57 ` Christian Marangi
0 siblings, 0 replies; 6+ messages in thread
From: Christian Marangi @ 2026-09-29 5:57 UTC (permalink / raw)
To: Yongzhao Chen
Cc: netdev, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King,
Florian Fainelli, linux-kernel, Ziyang Huang
On Tue, Sep 29, 2026 at 12:08:10AM +0200, Yongzhao Chen wrote:
> The global maximum frame size must be updated with CPU MACs disabled.
> The previous logic only paused ports 0 and 6, leaving an internal PHY
> CPU port enabled while modifying the register.
>
> Include enabled internal CPU ports in the pause sequence. Use the
> existing reg_mutex to serialize the MTU update against port enable, port
> disable, and phylink link-up and link-down transitions. Read and restore
> each port's original TXMAC and RXMAC bits, ensuring ports that were down
> remain down and preserving LINK_AUTO. Retain existing handling for ports
> 0 and 6.
>
> Abort before updating the frame size if reading port status or pausing
> the MAC fails. Attempt to restore all ports already modified, and report
> any restoration failures even if an earlier error occurred.
>
> The standalone qca8k MDIO error-propagation fix is a prerequisite for
> this series; that error-handling bug predates this locking change.
>
> Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
> Assisted-by: LLM
> ---
> drivers/net/dsa/qca/qca8k-8xxx.c | 2 +
> drivers/net/dsa/qca/qca8k-common.c | 79 ++++++++++++++++++++++++------
> 2 files changed, 66 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
> index 89113d22d5d..7bd9d9abcef 100644
> --- a/drivers/net/dsa/qca/qca8k-8xxx.c
> +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
> @@ -1495,7 +1495,9 @@ qca8k_phylink_mac_link_up(struct phylink_config *config,
>
> reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
>
> + mutex_lock(&priv->reg_mutex);
> qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg);
> + mutex_unlock(&priv->reg_mutex);
> }
I'm not entirely sure we need to use the reg mutex here since each port
have their own register and is independent... a dedicated mutex should be
considered for the task...
>
> static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs)
> diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c
> index 13005f10edb..6b32bdd75ea 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)
Personal taste but I always feel this might be confusing...
_locked may imply that the function will lock, not that you should lock
before calling... I know it's already pattern in the kernel, your choice to
use this or __ variant.
I would use __qca8k_port_set.. and add tag to enforce that the mutex should be
locked here.
> {
> 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->reg_mutex);
> + qca8k_port_set_status_locked(priv, port, enable);
> + mutex_unlock(&priv->reg_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->reg_mutex);
> + qca8k_port_set_status_locked(priv, port, 1);
> priv->port_enabled_map |= BIT(port);
> + mutex_unlock(&priv->reg_mutex);
>
Can port be enabled concurrently and corrupt the port enable map? Can you
check with AI if this case is possible? If yes then this might be a good
idea to make a separate prereq patch introducing a dedicated mutex for
port status and protect it accordingly. (might also be worth for net)
> 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->reg_mutex);
> + qca8k_port_set_status_locked(priv, port, 0);
> priv->port_enabled_map &= ~BIT(port);
> + mutex_unlock(&priv->reg_mutex);
> }
ditto.
>
> int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
> {
> + u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
> struct qca8k_priv *priv = ds->priv;
> - int ret;
> + u32 status[QCA8K_NUM_PORTS] = { 0 };
nit. Reverse tree.
> + int ret, restore_ret, i;
> + u32 stopped = 0;
> + u32 ports;
>
> /* 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);
In the context of internal PHY CPU port port 0 and port 6 won't be
connected... Should we check that and create a mask of the cpu port right
from the start?
>
> - if (priv->port_enabled_map & BIT(6))
> - qca8k_port_set_status(priv, 6, 0);
> + mutex_lock(&priv->reg_mutex);
> + ports &= priv->port_enabled_map;
> +
> + for (i = 0; i < QCA8K_NUM_PORTS; i++) {
for_each_set_bit might be better?
> + 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++) {
ditto.
> + 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->reg_mutex);
> return ret;
> }
>
> --
> 2.43.0
>
--
Ansuel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v4 3/3] net: dsa: qca8k: support QCA8337 internal PHY CPU links
2026-09-28 22:08 ` [PATCH net-next v4 3/3] net: dsa: qca8k: support QCA8337 internal PHY CPU links Yongzhao Chen
@ 2026-09-29 6:04 ` Christian Marangi
0 siblings, 0 replies; 6+ messages in thread
From: Christian Marangi @ 2026-09-29 6:04 UTC (permalink / raw)
To: Yongzhao Chen
Cc: netdev, Ziyang Huang, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Florian Fainelli, linux-kernel
On Tue, Sep 29, 2026 at 12:08:11AM +0200, Yongzhao Chen wrote:
> From: Ziyang Huang <hzyitc@outlook.com>
>
> A PHY-to-PHY CPU link connects the SoC PHY directly to an internal
> switch PHY. The QCA8337 supports header mode on these ports, and
> existing phylink callbacks already handle their internal interfaces.
>
> Allow QCA8337 CPU port selection to fall back to ports 1 through 5 after
> checking the dedicated MAC-only ports. Preserve the preference for CPU
> ports 0 and 6 across all switch models, limiting the internal-port
> fallback to QCA8337. Support for QCA8327 internal CPU links has not
> been established and is not enabled. Include internal CPU PHYs when
> selecting the PHY access method, without altering the handling of
> MAC-only or external user ports.
>
> This enables a single internal CPU PHY configured with an explicit
> phy-handle and phy-mode = "internal". The conduit interface uses its own
> PHY on the opposite side of the MDI connection. Existing single-CPU DSA
> forwarding uses the selected port for default flooding and membership
> without extra routing changes.
>
> Adapted from the OpenWrt PHY-to-PHY CPU link patch, narrowing the MDIO
> filter adjustments to preserve handling for external user ports.
>
Can you put an example DT for this? Also no additional register are needed
to this special mode?
> [yongzhao: preserve external user-port handling on port 6 and limit
> internal CPU PHY support to QCA8337]
>
> Assisted-by: LLM
> Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
> Signed-off-by: Yongzhao Chen <yongzhao.derek@gmail.com>
> ---
> drivers/net/dsa/qca/qca8k-8xxx.c | 18 +++++++++++++-----
> 1 file changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
> index 7bd9d9abcef..cd36efd1c4f 100644
> --- a/drivers/net/dsa/qca/qca8k-8xxx.c
> +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
> @@ -1026,7 +1026,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv)
> return ret;
> }
>
> - if (!dsa_is_user_port(priv->ds, reg))
> + if (!dsa_is_user_port(priv->ds, reg) &&
> + !(reg > 0 && reg < 6 && dsa_is_cpu_port(priv->ds, reg)))
> continue;
>
> of_get_phy_mode(port, &mode);
> @@ -1102,16 +1103,23 @@ qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv)
> static int qca8k_find_cpu_port(struct dsa_switch *ds)
> {
> struct qca8k_priv *priv = ds->priv;
> + int port;
>
> - /* Find the connected cpu port. Valid port are 0 or 6 */
> if (dsa_is_cpu_port(ds, 0))
> return 0;
>
> - dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
> -
> if (dsa_is_cpu_port(ds, 6))
> return 6;
>
> + /* Internal PHY CPU port selection is currently enabled for QCA8337. */
> + if (priv->switch_id != QCA8K_ID_QCA8337)
> + return -EINVAL;
> +
> + /* An internal PHY can provide a PHY-to-PHY CPU link. */
> + for (port = 1; port < 6; port++)
> + if (dsa_is_cpu_port(ds, port))
> + return port;
> +
> return -EINVAL;
> }
>
> @@ -1863,7 +1871,7 @@ qca8k_setup(struct dsa_switch *ds)
>
> cpu_port = qca8k_find_cpu_port(ds);
> if (cpu_port < 0) {
> - dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6");
> + dev_err(priv->dev, "No CPU port configured");
> return cpu_port;
> }
>
> --
> 2.43.0
>
--
Ansuel
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-29 6:04 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-28 22:08 [PATCH net-next v4 0/3] net: dsa: qca8k: add a QCA8337 CPU PHY consumer Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 1/3] net: dsa: pass PHY flags when connecting shared ports Yongzhao Chen
2026-09-28 22:08 ` [PATCH net-next v4 2/3] net: dsa: qca8k: serialize CPU MAC pause during MTU changes Yongzhao Chen
2026-09-29 5:57 ` Christian Marangi
2026-09-28 22:08 ` [PATCH net-next v4 3/3] net: dsa: qca8k: support QCA8337 internal PHY CPU links Yongzhao Chen
2026-09-29 6:04 ` 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®