* [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port
@ 2026-05-09 13:05 Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties Lorenzo Bianconi
` (11 more replies)
0 siblings, 12 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu, Madhur Agrawal
EN7581 or AN7583 SoCs support connecting multiple external SerDes (e.g.
Ethernet or USB SerDes) to GDM3 or GDM4 ports via a hw arbiter that
manages the traffic in a TDM manner. As a result multiple net_devices can
connect to the same GDM{3,4} port and there is a theoretical "1:n"
relation between GDM ports and net_devices.
┌─────────────────────────────────┐
│ │ ┌──────┐
│ P1 GDM1 ├────►MT7530│
│ │ └──────┘
│ │ ETH0 (DSA conduit)
│ │
│ PSE/FE │
│ │
│ │
│ │ ┌─────┐
│ P0 CDM1 ├────►QDMA0│
│ P4 P9 GDM4 │ └─────┘
└──┬─────────────────────────┬────┘
│ │
┌──▼──┐ ┌────▼────┐
│ PPE │ │ ARB │
└─────┘ └─┬─────┬─┘
│ │
┌──▼──┐┌─▼───┐
│ ETH ││ USB │
└─────┘└─────┘
ETH1 ETH2
This series introduces support for multiple net_devices connected to the
same Frame Engine (FE) GDM port (GDM3 or GDM4) via an external hw
arbiter. Please note GDM1 or GDM2 does not support the connection with
the external arbiter.
---
Changes in v5:
- Move qos_sq_bmap bitmap in airoha_gdm_dev struct.
- Unregister netdevice before running of_node_put().
- Move stat MIB counters in airoha_gdm_dev struct.
- Fix airoha_ppe_init_upd_mem() mac address configuration.
- Do not return -EBUSY if we try to decrease configured MTU of a shared
GDM port, just skip hw configuration.
- use int instead of atomic_t for GDM port users.
- Add patch "net: airoha: Reserve RX headroom to avoid skb reallocation"
- Fix typos.
- Link to v4: https://lore.kernel.org/r/20260507-airoha-eth-multi-serdes-v4-0-af613b61ae02@kernel.org
Changes in v4:
- Make ethernet-port property available just for GDM3 and GDM4 in DTS
specification
- Move cpu_tx_packets, fwd_tx_packets qos_sq_bmap fields in airoha_qdma
struct
- Fix of_node leak removing the net_device in airoha_remove() or
airoha_probe() error path
- Fix nbq backward compatibility
- Link to v3: https://lore.kernel.org/r/20260406-airoha-eth-multi-serdes-v3-0-ab6ea49d59ff@kernel.org
Changes in v3:
- Fix MTU and VIP configuration when the GDM port is shared between
multiple net_devices.
- Add sanity check for nbq parameter.
- Add missing of_node_get() for net_device np node.
- Check if GDM port is shared before decresing device MTU.
- Move port forward configuration in airoha_dev_stop() before
configuring DMA tx/rx engine.
- Introduce PRIV_FLAG_WAN parameter.
- Link to v2: https://lore.kernel.org/r/20260401-airoha-eth-multi-serdes-v2-0-ac427ae4beeb@kernel.org
Changes in v2:
- Rename multiplexer in arbiter in the commit logs.
- Rebase on top of net-next main branch.
- Add missing PPE cpu port configuration for GDM2 when loopback is
enabled.
- Link to v1: https://lore.kernel.org/r/20260329-airoha-eth-multi-serdes-v1-0-00f52dc360ca@kernel.org
---
Christian Marangi (1):
net: airoha: Better handle MIB for GDM with multiple port attached
Lorenzo Bianconi (11):
dt-bindings: net: airoha: Add EN7581 ethernet-ports properties
net: airoha: Reserve RX headroom to avoid skb reallocation
net: airoha: Introduce airoha_gdm_dev struct
net: airoha: Move airoha_qdma pointer in airoha_gdm_dev struct
net: airoha: Rely on airoha_gdm_dev pointer in airhoa_is_lan_gdm_port()
net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
net: airoha: Move {cpu,fwd}_tx_packets in airoha_gdm_dev struct
net: airoha: Support multiple net_devices for a single FE GDM port
net: airoha: Do not stop GDM port if it is shared
net: airoha: Introduce WAN device flag
net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
.../devicetree/bindings/net/airoha,en7581-eth.yaml | 52 +-
drivers/net/ethernet/airoha/airoha_eth.c | 997 ++++++++++++++-------
drivers/net/ethernet/airoha/airoha_eth.h | 48 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 43 +-
4 files changed, 785 insertions(+), 355 deletions(-)
---
base-commit: 8b2feced65cd3aa0597d596ed5733a1abd4c4d78
change-id: 20260324-airoha-eth-multi-serdes-fb4b556ee756
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 02/12] net: airoha: Reserve RX headroom to avoid skb reallocation Lorenzo Bianconi
` (10 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree
EN7581 and AN7583 SoCs support connecting multiple external SerDes to GDM3
or GDM4 ports via a hw arbiter that manages the traffic in a TDM manner.
As a result multiple net_devices can connect to the same GDM{3,4} port
and there is a theoretical "1:n" relation between GDM ports and
net_devices.
Introduce the ethernet-port property in order to model a given net_device
that is connected via the external arbiter to the GDM{3,4} port (that
is represented by the ethernet property. Please note GDM1 or GDM2 does not
support the connection with the external arbiter and are represented
by ethernet property.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
.../devicetree/bindings/net/airoha,en7581-eth.yaml | 52 +++++++++++++++++++++-
1 file changed, 51 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
index fbe2ddcdd909..73ba37872247 100644
--- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
+++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
@@ -130,6 +130,38 @@ patternProperties:
maximum: 4
description: GMAC port identifier
+ '#address-cells':
+ const: 1
+ '#size-cells':
+ const: 0
+
+ allOf:
+ - if:
+ properties:
+ reg:
+ items:
+ - enum:
+ - 3
+ - 4
+ then:
+ patternProperties:
+ "^ethernet-port@[0-5]$":
+ type: object
+ unevaluatedProperties: false
+ description: External ethernet port ID available on the GDM port
+
+ properties:
+ compatible:
+ const: airoha,eth-port
+
+ reg:
+ maxItems: 1
+ description: External ethernet port identifier
+
+ required:
+ - compatible
+ - reg
+
required:
- reg
- compatible
@@ -191,9 +223,27 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- mac: ethernet@1 {
+ mac1: ethernet@1 {
compatible = "airoha,eth-mac";
reg = <1>;
};
+
+ mac4: ethernet@4 {
+ compatible = "airoha,eth-mac";
+ reg = <4>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-port@0 {
+ compatible = "airoha,eth-port";
+ reg = <0>;
+ };
+
+ ethernet-port@1 {
+ compatible = "airoha,eth-port";
+ reg = <1>;
+ };
+ };
};
};
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 02/12] net: airoha: Reserve RX headroom to avoid skb reallocation
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 03/12] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
` (9 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
Reserve NET_SKB_PAD + NET_IP_ALIGN bytes of headroom for received packets
to avoid skb head reallocation when pushing protocol headers into the skb.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++++--
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index f71fb18197ec..8a941c58d1b4 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -543,9 +543,11 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
q->queued++;
nframes++;
+ offset += AIROHA_RX_HEADROOM;
e->buf = page_address(page) + offset;
e->dma_addr = page_pool_get_dma_addr(page) + offset;
- e->dma_len = SKB_WITH_OVERHEAD(q->buf_size);
+ e->dma_len = SKB_WITH_OVERHEAD(q->buf_size -
+ AIROHA_RX_HEADROOM);
val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len);
WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
@@ -627,10 +629,12 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
port = eth->ports[p];
if (!q->skb) { /* first buffer */
- q->skb = napi_build_skb(e->buf, q->buf_size);
+ q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
+ q->buf_size);
if (!q->skb)
goto free_frag;
+ skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
q->skb->dev = port->dev;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 58530d096de7..6cd589b790b6 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -32,6 +32,7 @@
#define AIROHA_FE_MC_MAX_VLAN_TABLE 64
#define AIROHA_FE_MC_MAX_VLAN_PORT 16
#define AIROHA_NUM_TX_IRQ 2
+#define AIROHA_RX_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN)
#define HW_DSCP_NUM 2048
#define IRQ_QUEUE_LEN(_n) ((_n) ? 1024 : 2048)
#define TX_DSCP_NUM 1024
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 03/12] net: airoha: Introduce airoha_gdm_dev struct
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 02/12] net: airoha: Reserve RX headroom to avoid skb reallocation Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 04/12] net: airoha: Move airoha_qdma pointer in " Lorenzo Bianconi
` (8 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
EN7581 and AN7583 SoCs support connecting multiple external SerDes to GDM3
or GDM4 ports via a hw arbiter that manages the traffic in a TDM manner.
As a result multiple net_devices can connect to the same GDM{3,4} port
and there is a theoretical "1:n" relation between GDM port and
net_devices.
Introduce airoha_gdm_dev struct to collect net_device related info (e.g.
net_device and external phy pointer). Please note this is just a
preliminary patch and we are still supporting a single net_device for
each GDM port. Subsequent patches will add support for multiple net_devices
connected to the same GDM port.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 309 ++++++++++++++++++-------------
drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 17 +-
3 files changed, 203 insertions(+), 136 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 8a941c58d1b4..73c0ae1d8a0f 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -601,6 +601,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
struct airoha_qdma_desc *desc = &q->desc[q->tail];
u32 hash, reason, msg1, desc_ctrl;
struct airoha_gdm_port *port;
+ struct net_device *netdev;
int data_len, len, p;
struct page *page;
@@ -628,6 +629,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
goto free_frag;
port = eth->ports[p];
+ netdev = port->dev->dev;
if (!q->skb) { /* first buffer */
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -637,8 +639,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
- q->skb->dev = port->dev;
- q->skb->protocol = eth_type_trans(q->skb, port->dev);
+ q->skb->dev = netdev;
+ q->skb->protocol = eth_type_trans(q->skb, netdev);
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
} else { /* scattered frame */
@@ -656,7 +658,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
continue;
- if (netdev_uses_dsa(port->dev)) {
+ if (netdev_uses_dsa(netdev)) {
/* PPE module requires untagged packets to work
* properly and it provides DSA port index via the
* DMA descriptor. Report DSA tag to the DSA stack
@@ -850,6 +852,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct airoha_gdm_dev *dev;
int j;
if (!port)
@@ -858,11 +861,12 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
if (port->qdma != qdma)
continue;
- for (j = 0; j < port->dev->num_tx_queues; j++) {
+ dev = port->dev;
+ for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
- netif_wake_subqueue(port->dev, j);
+ netif_wake_subqueue(dev->dev, j);
}
}
q->txq_stopped = false;
@@ -1702,19 +1706,20 @@ static void airoha_update_hw_stats(struct airoha_gdm_port *port)
spin_unlock(&port->stats.lock);
}
-static int airoha_dev_open(struct net_device *dev)
+static int airoha_dev_open(struct net_device *netdev)
{
- int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
- netif_tx_start_all_queues(dev);
+ netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(port, true);
if (err)
return err;
- if (netdev_uses_dsa(dev))
+ if (netdev_uses_dsa(netdev))
airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
else
@@ -1742,16 +1747,17 @@ static int airoha_dev_open(struct net_device *dev)
return 0;
}
-static int airoha_dev_stop(struct net_device *dev)
+static int airoha_dev_stop(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
int i;
- netif_tx_disable(dev);
+ netif_tx_disable(netdev);
airoha_set_vip_for_gdm_port(port, false);
- for (i = 0; i < dev->num_tx_queues; i++)
- netdev_tx_reset_subqueue(dev, i);
+ for (i = 0; i < netdev->num_tx_queues; i++)
+ netdev_tx_reset_subqueue(netdev, i);
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
@@ -1772,16 +1778,17 @@ static int airoha_dev_stop(struct net_device *dev)
return 0;
}
-static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
+static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int err;
- err = eth_mac_addr(dev, p);
+ err = eth_mac_addr(netdev, p);
if (err)
return err;
- airoha_set_macaddr(port, dev->dev_addr);
+ airoha_set_macaddr(port, netdev->dev_addr);
return 0;
}
@@ -1845,16 +1852,17 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
return 0;
}
-static int airoha_dev_init(struct net_device *dev)
+static int airoha_dev_init(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
- struct airoha_eth *eth = port->eth;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
- port->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, dev->dev_addr);
+ dev->dev->irq = port->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(port, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -1879,10 +1887,11 @@ static int airoha_dev_init(struct net_device *dev)
return 0;
}
-static void airoha_dev_get_stats64(struct net_device *dev,
+static void airoha_dev_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *storage)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(port);
@@ -1901,36 +1910,39 @@ static void airoha_dev_get_stats64(struct net_device *dev,
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
-static int airoha_dev_change_mtu(struct net_device *dev, int mtu)
+static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = port->qdma->eth;
u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK,
FIELD_PREP(GDM_LONG_LEN_MASK, len));
- WRITE_ONCE(dev->mtu, mtu);
+ WRITE_ONCE(netdev->mtu, mtu);
return 0;
}
-static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
+static u16 airoha_dev_select_queue(struct net_device *netdev,
+ struct sk_buff *skb,
struct net_device *sb_dev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int queue, channel;
/* For dsa device select QoS channel according to the dsa user port
* index, rely on port id otherwise. Select QoS queue based on the
* skb priority.
*/
- channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
+ channel = netdev_uses_dsa(netdev) ? skb_get_queue_mapping(skb) : port->id;
channel = channel % AIROHA_NUM_QOS_CHANNELS;
queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
- return queue < dev->num_tx_queues ? queue : 0;
+ return queue < netdev->num_tx_queues ? queue : 0;
}
static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
@@ -1994,9 +2006,10 @@ int airoha_get_fe_port(struct airoha_gdm_port *port)
}
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
- struct net_device *dev)
+ struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
@@ -2009,7 +2022,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
u8 fport;
qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
- tag = airoha_get_dsa_tag(skb, dev);
+ tag = airoha_get_dsa_tag(skb, netdev);
msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
qid / AIROHA_NUM_QOS_QUEUES) |
@@ -2045,7 +2058,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
spin_lock_bh(&q->lock);
- txq = skb_get_tx_queue(dev, skb);
+ txq = skb_get_tx_queue(netdev, skb);
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
if (q->queued + nr_frags >= q->ndesc) {
@@ -2069,9 +2082,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
dma_addr_t addr;
u32 val;
- addr = dma_map_single(dev->dev.parent, data, len,
+ addr = dma_map_single(netdev->dev.parent, data, len,
DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
+ if (unlikely(dma_mapping_error(netdev->dev.parent, addr)))
goto error_unmap;
list_move_tail(&e->list, &tx_list);
@@ -2120,7 +2133,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
error_unmap:
list_for_each_entry(e, &tx_list, list) {
- dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len,
+ dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
e->dma_addr = 0;
}
@@ -2129,25 +2142,27 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
spin_unlock_bh(&q->lock);
error:
dev_kfree_skb_any(skb);
- dev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
-static void airoha_ethtool_get_drvinfo(struct net_device *dev,
+static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = port->qdma->eth;
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
}
-static void airoha_ethtool_get_mac_stats(struct net_device *dev,
+static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
struct ethtool_eth_mac_stats *stats)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(port);
@@ -2175,11 +2190,12 @@ static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
};
static void
-airoha_ethtool_get_rmon_stats(struct net_device *dev,
+airoha_ethtool_get_rmon_stats(struct net_device *netdev,
struct ethtool_rmon_stats *stats,
const struct ethtool_rmon_hist_range **ranges)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_hw_stats *hw_stats = &port->stats;
unsigned int start;
@@ -2204,11 +2220,12 @@ airoha_ethtool_get_rmon_stats(struct net_device *dev,
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
-static int airoha_qdma_set_chan_tx_sched(struct net_device *dev,
+static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
int channel, enum tx_sched_mode mode,
const u16 *weights, u8 n_weights)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int i;
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
@@ -2293,10 +2310,12 @@ static int airoha_qdma_set_tx_ets_sched(struct net_device *dev, int channel,
ARRAY_SIZE(w));
}
-static int airoha_qdma_get_tx_ets_stats(struct net_device *dev, int channel,
+static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct tc_ets_qopt_offload *opt)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+
u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
REG_CNTR_VAL(channel << 1));
u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
@@ -2558,11 +2577,12 @@ static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma,
mode, val);
}
-static int airoha_qdma_set_tx_rate_limit(struct net_device *dev,
+static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
int channel, u32 rate,
u32 bucket_size)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int i, err;
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
@@ -2582,20 +2602,22 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *dev,
return 0;
}
-static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev,
+static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
- int err, num_tx_queues = dev->real_num_tx_queues;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ int err, num_tx_queues = netdev->real_num_tx_queues;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
return -EINVAL;
}
- err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+ err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
+ opt->quantum);
if (err) {
NL_SET_ERR_MSG_MOD(opt->extack,
"failed configuring htb offload");
@@ -2605,9 +2627,10 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev,
if (opt->command == TC_HTB_NODE_MODIFY)
return 0;
- err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1);
+ err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
if (err) {
- airoha_qdma_set_tx_rate_limit(dev, channel, 0, opt->quantum);
+ airoha_qdma_set_tx_rate_limit(netdev, channel, 0,
+ opt->quantum);
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
return err;
@@ -2697,11 +2720,12 @@ static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f)
return 0;
}
-static int airoha_dev_tc_matchall(struct net_device *dev,
+static int airoha_dev_tc_matchall(struct net_device *netdev,
struct tc_cls_matchall_offload *f)
{
enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -2736,18 +2760,19 @@ static int airoha_dev_tc_matchall(struct net_device *dev,
static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
- struct net_device *dev = cb_priv;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct net_device *netdev = cb_priv;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = port->qdma->eth;
- if (!tc_can_offload(dev))
+ if (!tc_can_offload(netdev))
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_CLSFLOWER:
return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data);
case TC_SETUP_CLSMATCHALL:
- return airoha_dev_tc_matchall(dev, type_data);
+ return airoha_dev_tc_matchall(netdev, type_data);
default:
return -EOPNOTSUPP;
}
@@ -2794,47 +2819,51 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
}
}
-static void airoha_tc_remove_htb_queue(struct net_device *dev, int queue)
+static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
- netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
- airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0);
+ netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
+ airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
clear_bit(queue, port->qos_sq_bmap);
}
-static int airoha_tc_htb_delete_leaf_queue(struct net_device *dev,
+static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
if (!test_bit(channel, port->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
- airoha_tc_remove_htb_queue(dev, channel);
+ airoha_tc_remove_htb_queue(netdev, channel);
return 0;
}
-static int airoha_tc_htb_destroy(struct net_device *dev)
+static int airoha_tc_htb_destroy(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int q;
for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
- airoha_tc_remove_htb_queue(dev, q);
+ airoha_tc_remove_htb_queue(netdev, q);
return 0;
}
-static int airoha_tc_get_htb_get_leaf_queue(struct net_device *dev,
+static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
if (!test_bit(channel, port->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
@@ -2870,8 +2899,8 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
return 0;
}
-static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
- void *type_data)
+static int airoha_dev_tc_setup(struct net_device *dev,
+ enum tc_setup_type type, void *type_data)
{
switch (type) {
case TC_SETUP_QDISC_ETS:
@@ -2937,25 +2966,81 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
}
}
-bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
- struct airoha_gdm_port *port)
+bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
+ struct airoha_gdm_dev *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
- if (eth->ports[i] == port)
+ struct airoha_gdm_port *port = eth->ports[i];
+
+ if (!port)
+ continue;
+
+ if (port->dev == dev)
return true;
}
return false;
}
+static int airoha_alloc_gdm_device(struct airoha_eth *eth,
+ struct airoha_gdm_port *port,
+ struct device_node *np)
+{
+ struct airoha_gdm_dev *dev;
+ struct net_device *netdev;
+ int err;
+
+ netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
+ AIROHA_NUM_NETDEV_TX_RINGS,
+ AIROHA_NUM_RX_RING);
+ if (!netdev) {
+ dev_err(eth->dev, "alloc_etherdev failed\n");
+ return -ENOMEM;
+ }
+
+ netdev->netdev_ops = &airoha_netdev_ops;
+ netdev->ethtool_ops = &airoha_ethtool_ops;
+ netdev->max_mtu = AIROHA_MAX_MTU;
+ netdev->watchdog_timeo = 5 * HZ;
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 |
+ NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
+ NETIF_F_HW_TC;
+ netdev->features |= netdev->hw_features;
+ netdev->vlan_features = netdev->hw_features;
+ netdev->dev.of_node = np;
+ SET_NETDEV_DEV(netdev, eth->dev);
+
+ /* reserve hw queues for HTB offloading */
+ err = netif_set_real_num_tx_queues(netdev, AIROHA_NUM_TX_RING);
+ if (err)
+ return err;
+
+ err = of_get_ethdev_address(np, netdev);
+ if (err) {
+ if (err == -EPROBE_DEFER)
+ return err;
+
+ eth_hw_addr_random(netdev);
+ dev_info(eth->dev, "generated random MAC address %pM\n",
+ netdev->dev_addr);
+ }
+
+ dev = netdev_priv(netdev);
+ dev->dev = netdev;
+ dev->port = port;
+ port->dev = dev;
+ dev->eth = eth;
+
+ return 0;
+}
+
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
struct airoha_gdm_port *port;
- struct net_device *dev;
int err, p;
u32 id;
@@ -2977,53 +3062,22 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
return -EINVAL;
}
- dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
- AIROHA_NUM_NETDEV_TX_RINGS,
- AIROHA_NUM_RX_RING);
- if (!dev) {
- dev_err(eth->dev, "alloc_etherdev failed\n");
+ port = devm_kzalloc(eth->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
return -ENOMEM;
- }
-
- dev->netdev_ops = &airoha_netdev_ops;
- dev->ethtool_ops = &airoha_ethtool_ops;
- dev->max_mtu = AIROHA_MAX_MTU;
- dev->watchdog_timeo = 5 * HZ;
- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
- NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
- NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HW_TC;
- dev->features |= dev->hw_features;
- dev->vlan_features = dev->hw_features;
- dev->dev.of_node = np;
- SET_NETDEV_DEV(dev, eth->dev);
-
- /* reserve hw queues for HTB offloading */
- err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
- if (err)
- return err;
-
- err = of_get_ethdev_address(np, dev);
- if (err) {
- if (err == -EPROBE_DEFER)
- return err;
-
- eth_hw_addr_random(dev);
- dev_info(eth->dev, "generated random MAC address %pM\n",
- dev->dev_addr);
- }
- port = netdev_priv(dev);
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
- port->eth = eth;
- port->dev = dev;
port->id = id;
/* XXX: Read nbq from DTS */
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
- return airoha_metadata_dst_alloc(port);
+ err = airoha_metadata_dst_alloc(port);
+ if (err)
+ return err;
+
+ return airoha_alloc_gdm_device(eth, port, np);
}
static int airoha_register_gdm_devices(struct airoha_eth *eth)
@@ -3037,7 +3091,7 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth)
if (!port)
continue;
- err = register_netdev(port->dev);
+ err = register_netdev(port->dev->dev);
if (err)
return err;
}
@@ -3146,12 +3200,14 @@ static int airoha_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct airoha_gdm_dev *dev;
if (!port)
continue;
- if (port->dev->reg_state == NETREG_REGISTERED)
- unregister_netdev(port->dev);
+ dev = port->dev;
+ if (dev && dev->dev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(dev->dev);
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3172,11 +3228,14 @@ static void airoha_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct airoha_gdm_dev *dev;
if (!port)
continue;
- unregister_netdev(port->dev);
+ dev = port->dev;
+ if (dev)
+ unregister_netdev(dev->dev);
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 6cd589b790b6..46f93639e878 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -534,10 +534,15 @@ struct airoha_qdma {
struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
};
+struct airoha_gdm_dev {
+ struct airoha_gdm_port *port;
+ struct net_device *dev;
+ struct airoha_eth *eth;
+};
+
struct airoha_gdm_port {
struct airoha_qdma *qdma;
- struct airoha_eth *eth;
- struct net_device *dev;
+ struct airoha_gdm_dev *dev;
int id;
int nbq;
@@ -661,8 +666,8 @@ static inline bool airoha_is_7583(struct airoha_eth *eth)
}
int airoha_get_fe_port(struct airoha_gdm_port *port);
-bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
- struct airoha_gdm_port *port);
+bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
+ struct airoha_gdm_dev *dev);
void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id,
u8 fport);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 26da519236bf..af7af4097b98 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -298,12 +298,12 @@ static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br,
static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
struct airoha_foe_entry *hwe,
- struct net_device *dev, int type,
+ struct net_device *netdev, int type,
struct airoha_flow_data *data,
int l4proto)
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
- int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev);
+ int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev);
struct airoha_foe_mac_info_common *l2;
u8 smac_id = 0xf;
@@ -319,10 +319,11 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
hwe->ib1 = val;
val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f);
- if (dev) {
+ if (netdev) {
struct airoha_wdma_info info = {};
- if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) {
+ if (!airoha_ppe_get_wdma_info(netdev, data->eth.h_dest,
+ &info)) {
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) |
FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT,
FE_PSE_PORT_CDM4);
@@ -332,12 +333,14 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID,
info.wcid);
} else {
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port;
u8 pse_port, channel;
- if (!airoha_is_valid_gdm_port(eth, port))
+ if (!airoha_is_valid_gdm_dev(eth, dev))
return -EINVAL;
+ port = dev->port;
if (dsa_port >= 0 || eth->ports[1])
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
: port->id;
@@ -1473,7 +1476,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
{
struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev;
+ struct net_device *dev = port->dev->dev;
const u8 *addr = dev->dev_addr;
u32 val;
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 04/12] net: airoha: Move airoha_qdma pointer in airoha_gdm_dev struct
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (2 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 03/12] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 05/12] net: airoha: Rely on airoha_gdm_dev pointer in airhoa_is_lan_gdm_port() Lorenzo Bianconi
` (7 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
Move airoha_qdma pointer from airoha_gdm_port struct to airoha_gdm_dev
one since the QDMA block used depends on the particular net_device
WAN/LAN configuration and in the current codebase net_device pointer is
associated to airoha_gdm_dev struct.
This is a preliminary patch to support multiple net_devices connected
to the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 105 +++++++++++++++----------------
drivers/net/ethernet/airoha/airoha_eth.h | 9 ++-
drivers/net/ethernet/airoha/airoha_ppe.c | 17 ++---
3 files changed, 64 insertions(+), 67 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 73c0ae1d8a0f..434d3a5ceaa3 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -71,9 +71,10 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
+static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, reg;
reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
@@ -85,7 +86,7 @@ static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
- airoha_ppe_init_upd_mem(port);
+ airoha_ppe_init_upd_mem(dev);
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -101,10 +102,10 @@ static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
FIELD_PREP(GDM_UCFQ_MASK, val));
}
-static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port,
- bool enable)
+static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 vip_port;
vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
@@ -858,10 +859,13 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
if (!port)
continue;
- if (port->qdma != qdma)
+ dev = port->dev;
+ if (!dev)
+ continue;
+
+ if (dev->qdma != qdma)
continue;
- dev = port->dev;
for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
@@ -1562,9 +1566,10 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
}
}
-static void airoha_update_hw_stats(struct airoha_gdm_port *port)
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
spin_lock(&port->stats.lock);
@@ -1711,11 +1716,11 @@ static int airoha_dev_open(struct net_device *netdev)
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
netif_tx_start_all_queues(netdev);
- err = airoha_set_vip_for_gdm_port(port, true);
+ err = airoha_set_vip_for_gdm_port(dev, true);
if (err)
return err;
@@ -1751,11 +1756,11 @@ static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
int i;
netif_tx_disable(netdev);
- airoha_set_vip_for_gdm_port(port, false);
+ airoha_set_vip_for_gdm_port(dev, false);
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
@@ -1781,21 +1786,21 @@ static int airoha_dev_stop(struct net_device *netdev)
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int err;
err = eth_mac_addr(netdev, p);
if (err)
return err;
- airoha_set_macaddr(port, netdev->dev_addr);
+ airoha_set_macaddr(dev, netdev->dev_addr);
return 0;
}
-static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
+static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, pse_port, chan;
int i, src_port;
@@ -1840,7 +1845,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX);
+ airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
@@ -1860,9 +1865,9 @@ static int airoha_dev_init(struct net_device *netdev)
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
- dev->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, netdev->dev_addr);
+ dev->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->dev->irq = dev->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(dev, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -1871,7 +1876,7 @@ static int airoha_dev_init(struct net_device *netdev)
if (!eth->ports[1]) {
int err;
- err = airoha_set_gdm2_loopback(port);
+ err = airoha_set_gdm2_loopback(dev);
if (err)
return err;
}
@@ -1881,8 +1886,7 @@ static int airoha_dev_init(struct net_device *netdev)
}
for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(port, i,
- airoha_get_fe_port(port));
+ airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
return 0;
}
@@ -1894,7 +1898,7 @@ static void airoha_dev_get_stats64(struct net_device *netdev,
struct airoha_gdm_port *port = dev->port;
unsigned int start;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
storage->rx_packets = port->stats.rx_ok_pkts;
@@ -1914,8 +1918,8 @@ static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
+ struct airoha_eth *eth = dev->eth;
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK,
@@ -1989,10 +1993,10 @@ static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
#endif
}
-int airoha_get_fe_port(struct airoha_gdm_port *port)
+int airoha_get_fe_port(struct airoha_gdm_dev *dev)
{
- struct airoha_qdma *qdma = port->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
switch (eth->soc->version) {
case 0x7583:
@@ -2009,8 +2013,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
struct netdev_queue *txq;
@@ -2048,7 +2051,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
}
}
- fport = airoha_get_fe_port(port);
+ fport = airoha_get_fe_port(dev);
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@@ -2151,8 +2154,7 @@ static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_eth *eth = dev->eth;
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
@@ -2165,7 +2167,7 @@ static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
struct airoha_gdm_port *port = dev->port;
unsigned int start;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
@@ -2205,7 +2207,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
ARRAY_SIZE(hw_stats->rx_len) + 1);
*ranges = airoha_ethtool_rmon_ranges;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
int i;
@@ -2225,18 +2227,17 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int i;
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
- airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
+ airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
for (i = 0; i < n_weights; i++) {
u32 status;
int err;
- airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG,
+ airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
TWRR_RW_CMD_MASK |
FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2244,13 +2245,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
err = read_poll_timeout(airoha_qdma_rr, status,
status & TWRR_RW_CMD_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC,
- true, port->qdma,
- REG_TXWRR_WEIGHT_CFG);
+ true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
if (err)
return err;
}
- airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+ airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
@@ -2316,9 +2316,9 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
+ u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma,
REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
+ u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma,
REG_CNTR_VAL((channel << 1) + 1));
u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
(fwd_tx_packets - port->fwd_tx_packets);
@@ -2582,17 +2582,16 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int i, err;
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
- err = airoha_qdma_set_trtcm_config(port->qdma, channel,
+ err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
REG_EGRESS_TRTCM_CFG, i,
!!rate, TRTCM_METER_MODE);
if (err)
return err;
- err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel,
+ err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
@@ -2642,11 +2641,11 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
return 0;
}
-static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port,
+static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
u32 rate, u32 bucket_size,
enum trtcm_unit_type unit_type)
{
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -2725,7 +2724,6 @@ static int airoha_dev_tc_matchall(struct net_device *netdev,
{
enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -2750,7 +2748,7 @@ static int airoha_dev_tc_matchall(struct net_device *netdev,
fallthrough;
}
case TC_CLSMATCHALL_DESTROY:
- return airoha_qdma_set_rx_meter(port, rate, bucket_size,
+ return airoha_qdma_set_rx_meter(dev, rate, bucket_size,
unit_type);
default:
return -EOPNOTSUPP;
@@ -2762,8 +2760,7 @@ static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
{
struct net_device *netdev = cb_priv;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_eth *eth = dev->eth;
if (!tc_can_offload(netdev))
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 46f93639e878..f2b4f12ed42a 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -536,12 +536,12 @@ struct airoha_qdma {
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
+ struct airoha_qdma *qdma;
struct net_device *dev;
struct airoha_eth *eth;
};
struct airoha_gdm_port {
- struct airoha_qdma *qdma;
struct airoha_gdm_dev *dev;
int id;
int nbq;
@@ -665,19 +665,18 @@ static inline bool airoha_is_7583(struct airoha_eth *eth)
return eth->soc->version == 0x7583;
}
-int airoha_get_fe_port(struct airoha_gdm_port *port);
+int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id,
- u8 fport);
+void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan);
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port);
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index af7af4097b98..22f5f1bae730 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -84,9 +84,9 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
AIROHA_FOE_IB1_BIND_TIMESTAMP);
}
-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport)
+void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
{
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
u8 qdma_id = qdma - ð->qdma[0];
u32 fe_cpu_port;
@@ -180,8 +180,8 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
if (!port)
continue;
- airoha_ppe_set_cpu_port(port, i,
- airoha_get_fe_port(port));
+ airoha_ppe_set_cpu_port(port->dev, i,
+ airoha_get_fe_port(port->dev));
}
}
}
@@ -1473,11 +1473,12 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev->dev;
- const u8 *addr = dev->dev_addr;
+ struct airoha_gdm_port *port = dev->port;
+ struct net_device *netdev = dev->dev;
+ struct airoha_eth *eth = dev->eth;
+ const u8 *addr = netdev->dev_addr;
u32 val;
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 05/12] net: airoha: Rely on airoha_gdm_dev pointer in airhoa_is_lan_gdm_port()
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (3 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 04/12] net: airoha: Move airoha_qdma pointer in " Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 06/12] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
` (6 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
Rename airhoa_is_lan_gdm_port in airhoa_is_lan_gdm_dev. Moreover, rely
on airoha_gdm_dev pointer in airhoa_is_lan_gdm_dev() instead of
airoha_gdm_port one.
This is a preliminary patch to support multiple net_devices connected to
the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 6 ++----
drivers/net/ethernet/airoha/airoha_eth.h | 4 +++-
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 434d3a5ceaa3..f9cb49f0529a 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -73,12 +73,10 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
- struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, reg;
- reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
- : REG_FE_WAN_MAC_H;
+ reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
@@ -1865,7 +1863,7 @@ static int airoha_dev_init(struct net_device *netdev)
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
dev->dev->irq = dev->qdma->irq_banks[0].irq;
airoha_set_macaddr(dev, netdev->dev_addr);
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f2b4f12ed42a..c038e722370e 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -646,8 +646,10 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
return qid % ARRAY_SIZE(qdma->q_tx);
}
-static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
+static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
+ struct airoha_gdm_port *port = dev->port;
+
/* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
* GDM{2,3,4} can be used as wan port connected to an external
* phy module.
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 22f5f1bae730..047141b2d6d8 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -362,7 +362,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
*/
- if (airoha_is_lan_gdm_port(port))
+ if (airoha_is_lan_gdm_dev(dev))
val |= AIROHA_FOE_IB2_FAST_PATH;
if (dsa_port >= 0)
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ,
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 06/12] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (4 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 05/12] net: airoha: Rely on airoha_gdm_dev pointer in airhoa_is_lan_gdm_port() Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 07/12] net: airoha: Move {cpu,fwd}_tx_packets " Lorenzo Bianconi
` (5 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, qos_sq_bmap field can be overwritten with the
configuration obtained from a net_device connected to a different QDMA
block. In order to fix the issue move qos_sq_bmap field from
airoha_gdm_port struct to airoha_gdm_dev one.
Add qos_channel_map bitmap in airoha_qdma struct to track if a shared
QDMA channel is already in use by another net_device.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 65 ++++++++++++++++++++++----------
drivers/net/ethernet/airoha/airoha_eth.h | 6 ++-
2 files changed, 50 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index f9cb49f0529a..33a0f7b98d52 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2606,19 +2606,28 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
int err, num_tx_queues = netdev->real_num_tx_queues;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
return -EINVAL;
}
+ /* Here we need to check the requested QDMA channel is not already
+ * in use by another net_device running on the same QDMA block.
+ */
+ if (test_and_set_bit(channel, qdma->qos_channel_map)) {
+ NL_SET_ERR_MSG_MOD(opt->extack,
+ "qdma qos channel already in use");
+ return -EBUSY;
+ }
+
err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
opt->quantum);
if (err) {
NL_SET_ERR_MSG_MOD(opt->extack,
"failed configuring htb offload");
- return err;
+ goto error;
}
if (opt->command == TC_HTB_NODE_MODIFY)
@@ -2630,13 +2639,17 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
opt->quantum);
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
- return err;
+ goto error;
}
- set_bit(channel, port->qos_sq_bmap);
+ set_bit(channel, dev->qos_sq_bmap);
opt->qid = AIROHA_NUM_TX_RING + channel;
return 0;
+error:
+ clear_bit(channel, qdma->qos_channel_map);
+
+ return err;
}
static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
@@ -2814,14 +2827,28 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
}
}
-static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
+static int airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
+ int err;
- netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
- airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
- clear_bit(queue, port->qos_sq_bmap);
+ err = netif_set_real_num_tx_queues(netdev,
+ netdev->real_num_tx_queues - 1);
+ if (err)
+ return err;
+
+ err = airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
+ if (err) {
+ netif_set_real_num_tx_queues(netdev,
+ netdev->real_num_tx_queues + 1);
+ return err;
+ }
+
+ clear_bit(queue, qdma->qos_channel_map);
+ clear_bit(queue, dev->qos_sq_bmap);
+
+ return 0;
}
static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
@@ -2829,26 +2856,27 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- if (!test_bit(channel, port->qos_sq_bmap)) {
+ if (!test_bit(channel, dev->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
- airoha_tc_remove_htb_queue(netdev, channel);
-
- return 0;
+ return airoha_tc_remove_htb_queue(netdev, channel);
}
static int airoha_tc_htb_destroy(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int q;
- for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
- airoha_tc_remove_htb_queue(netdev, q);
+ for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) {
+ int err;
+
+ err = airoha_tc_remove_htb_queue(netdev, q);
+ if (err)
+ return err;
+ }
return 0;
}
@@ -2858,9 +2886,8 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- if (!test_bit(channel, port->qos_sq_bmap)) {
+ if (!test_bit(channel, dev->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index c038e722370e..475f5c64bfcb 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -532,6 +532,8 @@ struct airoha_qdma {
struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
+
+ DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
struct airoha_gdm_dev {
@@ -539,6 +541,8 @@ struct airoha_gdm_dev {
struct airoha_qdma *qdma;
struct net_device *dev;
struct airoha_eth *eth;
+
+ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
};
struct airoha_gdm_port {
@@ -548,8 +552,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
- DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
-
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 07/12] net: airoha: Move {cpu,fwd}_tx_packets in airoha_gdm_dev struct
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (5 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 06/12] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 08/12] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
` (4 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, cpu_tx_packets and fwd_tx_packets fields can
be overwritten with the value from a different QDMA block. In order to
fix the issue move cpu_tx_packets and fwd_tx_packets fields from
airoha_gdm_port struct to airoha_gdm_dev one.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 16 +++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +++----
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 33a0f7b98d52..904f8da93600 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2312,19 +2312,17 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
- u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma,
- REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma,
+ u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+ u64 fwd_tx_packets = airoha_qdma_rr(qdma,
REG_CNTR_VAL((channel << 1) + 1));
- u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
- (fwd_tx_packets - port->fwd_tx_packets);
+ u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
+ (fwd_tx_packets - dev->fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
-
- port->cpu_tx_packets = cpu_tx_packets;
- port->fwd_tx_packets = fwd_tx_packets;
+ dev->cpu_tx_packets = cpu_tx_packets;
+ dev->fwd_tx_packets = fwd_tx_packets;
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 475f5c64bfcb..694e83bfd1e7 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -543,6 +543,9 @@ struct airoha_gdm_dev {
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+ /* qos stats counters */
+ u64 cpu_tx_packets;
+ u64 fwd_tx_packets;
};
struct airoha_gdm_port {
@@ -552,10 +555,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
- /* qos stats counters */
- u64 cpu_tx_packets;
- u64 fwd_tx_packets;
-
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 08/12] net: airoha: Support multiple net_devices for a single FE GDM port
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (6 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 07/12] net: airoha: Move {cpu,fwd}_tx_packets " Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 09/12] net: airoha: Do not stop GDM port if it is shared Lorenzo Bianconi
` (3 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
EN7581 or AN7583 SoCs support connecting multiple external SerDes (e.g.
Ethernet or USB SerDes) to GDM3 or GDM4 ports via a hw arbiter that
manages the traffic in a TDM manner. As a result multiple net_devices can
connect to the same GDM{3,4} port and there is a theoretical "1:n"
relation between GDM ports and net_devices.
┌─────────────────────────────────┐
│ │ ┌──────┐
│ P1 GDM1 ├────►MT7530│
│ │ └──────┘
│ │ ETH0 (DSA conduit)
│ │
│ PSE/FE │
│ │
│ │
│ │ ┌─────┐
│ P0 CDM1 ├────►QDMA0│
│ P4 P9 GDM4 │ └─────┘
└──┬─────────────────────────┬────┘
│ │
┌──▼──┐ ┌────▼────┐
│ PPE │ │ ARB │
└─────┘ └─┬─────┬─┘
│ │
┌──▼──┐┌─▼───┐
│ ETH ││ USB │
└─────┘└─────┘
ETH1 ETH2
Introduce support for multiple net_devices connected to the same Frame
Engine (FE) GDM port (GDM3 or GDM4) via an external hw arbiter.
Please note GDM1 or GDM2 does not support the connection with the external
arbiter.
Add get_dev_from_sport callback since EN7581 and AN7583 have different
logics for the net_device type connected to GDM3 or GDM4.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 271 ++++++++++++++++++++++++-------
drivers/net/ethernet/airoha/airoha_eth.h | 10 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 13 +-
3 files changed, 228 insertions(+), 66 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 904f8da93600..be31f639f4be 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -106,7 +106,7 @@ static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable)
struct airoha_eth *eth = dev->eth;
u32 vip_port;
- vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
+ vip_port = eth->soc->ops.get_vip_port(port, dev->nbq);
if (enable) {
airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
@@ -567,24 +567,26 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
return nframes;
}
-static int airoha_qdma_get_gdm_port(struct airoha_eth *eth,
- struct airoha_qdma_desc *desc)
+static struct airoha_gdm_dev *
+airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc)
{
- u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
+ struct airoha_gdm_port *port;
+ u16 p, d;
- sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
- switch (sport) {
- case 0x10 ... 0x14:
- port = 0;
- break;
- case 0x2 ... 0x4:
- port = sport - 1;
- break;
- default:
- return -EINVAL;
- }
+ if (eth->soc->ops.get_dev_from_sport(desc, &p, &d))
+ return ERR_PTR(-ENODEV);
- return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
+ if (p >= ARRAY_SIZE(eth->ports))
+ return ERR_PTR(-ENODEV);
+
+ port = eth->ports[p];
+ if (!port)
+ return ERR_PTR(-ENODEV);
+
+ if (d >= ARRAY_SIZE(port->devs))
+ return ERR_PTR(-ENODEV);
+
+ return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
}
static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
@@ -599,9 +601,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
struct airoha_queue_entry *e = &q->entry[q->tail];
struct airoha_qdma_desc *desc = &q->desc[q->tail];
u32 hash, reason, msg1, desc_ctrl;
- struct airoha_gdm_port *port;
- struct net_device *netdev;
- int data_len, len, p;
+ struct airoha_gdm_dev *dev;
+ int data_len, len;
struct page *page;
desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
@@ -623,12 +624,10 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
if (!len || data_len < len)
goto free_frag;
- p = airoha_qdma_get_gdm_port(eth, desc);
- if (p < 0 || !eth->ports[p])
+ dev = airoha_qdma_get_gdm_dev(eth, desc);
+ if (IS_ERR(dev))
goto free_frag;
- port = eth->ports[p];
- netdev = port->dev->dev;
if (!q->skb) { /* first buffer */
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -638,8 +637,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
- q->skb->dev = netdev;
- q->skb->protocol = eth_type_trans(q->skb, netdev);
+ q->skb->dev = dev->dev;
+ q->skb->protocol = eth_type_trans(q->skb, dev->dev);
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
} else { /* scattered frame */
@@ -657,7 +656,9 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
continue;
- if (netdev_uses_dsa(netdev)) {
+ if (netdev_uses_dsa(dev->dev)) {
+ struct airoha_gdm_port *port = dev->port;
+
/* PPE module requires untagged packets to work
* properly and it provides DSA port index via the
* DMA descriptor. Report DSA tag to the DSA stack
@@ -851,24 +852,27 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- struct airoha_gdm_dev *dev;
- int j;
+ int d;
if (!port)
continue;
- dev = port->dev;
- if (!dev)
- continue;
+ for (d = 0; d < ARRAY_SIZE(port->devs); d++) {
+ struct airoha_gdm_dev *dev = port->devs[d];
+ int j;
- if (dev->qdma != qdma)
- continue;
+ if (!dev)
+ continue;
- for (j = 0; j < dev->dev->num_tx_queues; j++) {
- if (airoha_qdma_get_txq(qdma, j) != qid)
+ if (dev->qdma != qdma)
continue;
- netif_wake_subqueue(dev->dev, j);
+ for (j = 0; j < dev->dev->num_tx_queues; j++) {
+ if (airoha_qdma_get_txq(qdma, j) != qid)
+ continue;
+
+ netif_wake_subqueue(dev->dev, j);
+ }
}
}
q->txq_stopped = false;
@@ -1829,7 +1833,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
- src_port = eth->soc->ops.get_sport(port, port->nbq);
+ src_port = eth->soc->ops.get_sport(port, dev->nbq);
if (src_port < 0)
return src_port;
@@ -1846,7 +1850,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
- u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
__field_prep(mask, AIROHA_GDM2_IDX));
@@ -2050,7 +2054,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
}
fport = airoha_get_fe_port(dev);
- msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
+ msg1 = FIELD_PREP(QDMA_ETH_TXMSG_NBOQ_MASK, dev->nbq) |
+ FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
q = &qdma->q_tx[qid];
@@ -2993,12 +2998,15 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ int j;
if (!port)
continue;
- if (port->dev == dev)
- return true;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ if (port->devs[j] == dev)
+ return true;
+ }
}
return false;
@@ -3006,10 +3014,11 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
static int airoha_alloc_gdm_device(struct airoha_eth *eth,
struct airoha_gdm_port *port,
- struct device_node *np)
+ int nbq, struct device_node *np)
{
- struct airoha_gdm_dev *dev;
struct net_device *netdev;
+ struct airoha_gdm_dev *dev;
+ u8 index;
int err;
netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
@@ -3029,7 +3038,6 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
NETIF_F_HW_TC;
netdev->features |= netdev->hw_features;
netdev->vlan_features = netdev->hw_features;
- netdev->dev.of_node = np;
SET_NETDEV_DEV(netdev, eth->dev);
/* reserve hw queues for HTB offloading */
@@ -3047,11 +3055,25 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
netdev->dev_addr);
}
+ /* Allowed nbq for EN7581 on GDM3 port are 4 and 5 for PCIE0
+ * and PCIE1 respectively.
+ */
+ index = nbq;
+ if (index && airoha_is_7581(eth) && port->id == AIROHA_GDM3_IDX)
+ index -= 4;
+
+ if (index >= ARRAY_SIZE(port->devs) || port->devs[index]) {
+ dev_err(eth->dev, "invalid nbq id: %d\n", nbq);
+ return -EINVAL;
+ }
+
+ netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
dev->dev = netdev;
dev->port = port;
- port->dev = dev;
dev->eth = eth;
+ dev->nbq = nbq;
+ port->devs[index] = dev;
return 0;
}
@@ -3061,7 +3083,8 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
struct airoha_gdm_port *port;
- int err, p;
+ struct device_node *node;
+ int err, nbq, p, d = 0;
u32 id;
if (!id_ptr) {
@@ -3089,15 +3112,51 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
port->id = id;
- /* XXX: Read nbq from DTS */
- port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
if (err)
return err;
- return airoha_alloc_gdm_device(eth, port, np);
+ /* Default nbq value to ensure backward compatibility */
+ nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
+
+ for_each_child_of_node(np, node) {
+ /* Multiple external serdes connected to the FE GDM port via an
+ * external arbiter.
+ */
+ const __be32 *nbq_ptr;
+
+ if (!of_device_is_compatible(node, "airoha,eth-port"))
+ continue;
+
+ d++;
+ if (!of_device_is_available(node))
+ continue;
+
+ nbq_ptr = of_get_property(node, "reg", NULL);
+ if (!nbq_ptr) {
+ dev_err(eth->dev, "missing nbq id\n");
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ /* Verify the provided nbq parameter is valid */
+ nbq = be32_to_cpup(nbq_ptr);
+ err = eth->soc->ops.get_sport(port, nbq);
+ if (err < 0) {
+ of_node_put(node);
+ return err;
+ }
+
+ err = airoha_alloc_gdm_device(eth, port, nbq, node);
+ if (err) {
+ of_node_put(node);
+ return err;
+ }
+ }
+
+ return !d ? airoha_alloc_gdm_device(eth, port, nbq, np) : 0;
}
static int airoha_register_gdm_devices(struct airoha_eth *eth)
@@ -3106,14 +3165,22 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- int err;
+ int j;
if (!port)
continue;
- err = register_netdev(port->dev->dev);
- if (err)
- return err;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ int err;
+
+ if (!dev)
+ continue;
+
+ err = register_netdev(dev->dev);
+ if (err)
+ return err;
+ }
}
set_bit(DEV_STATE_REGISTERED, ð->state);
@@ -3220,14 +3287,23 @@ static int airoha_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- struct airoha_gdm_dev *dev;
+ int j;
if (!port)
continue;
- dev = port->dev;
- if (dev && dev->dev->reg_state == NETREG_REGISTERED)
- unregister_netdev(dev->dev);
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ netdev = dev->dev;
+ if (netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(netdev);
+ of_node_put(netdev->dev.of_node);
+ }
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3248,14 +3324,22 @@ static void airoha_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
- struct airoha_gdm_dev *dev;
+ int j;
if (!port)
continue;
- dev = port->dev;
- if (dev)
- unregister_netdev(dev->dev);
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ netdev = dev->dev;
+ unregister_netdev(netdev);
+ of_node_put(netdev->dev.of_node);
+ }
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3318,6 +3402,39 @@ static u32 airoha_en7581_get_vip_port(struct airoha_gdm_port *port, int nbq)
return 0;
}
+static int airoha_en7581_get_dev_from_sport(struct airoha_qdma_desc *desc,
+ u16 *port, u16 *dev)
+{
+ u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK,
+ le32_to_cpu(READ_ONCE(desc->msg1)));
+
+ *dev = 0;
+ switch (sport) {
+ case 0x10 ... 0x14:
+ *port = 0; /* GDM1 */
+ break;
+ case 0x2 ... 0x4:
+ *port = sport - 1;
+ break;
+ case HSGMII_LAN_7581_PCIE1_SRCPORT:
+ *dev = 1;
+ fallthrough;
+ case HSGMII_LAN_7581_PCIE0_SRCPORT:
+ *port = 2; /* GDM3 */
+ break;
+ case HSGMII_LAN_7581_USB_SRCPORT:
+ *dev = 1;
+ fallthrough;
+ case HSGMII_LAN_7581_ETH_SRCPORT:
+ *port = 3; /* GDM4 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const char * const an7583_xsi_rsts_names[] = {
"xsi-mac",
"hsi0-mac",
@@ -3367,6 +3484,36 @@ static u32 airoha_an7583_get_vip_port(struct airoha_gdm_port *port, int nbq)
return 0;
}
+static int airoha_an7583_get_dev_from_sport(struct airoha_qdma_desc *desc,
+ u16 *port, u16 *dev)
+{
+ u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK,
+ le32_to_cpu(READ_ONCE(desc->msg1)));
+
+ *dev = 0;
+ switch (sport) {
+ case 0x10 ... 0x14:
+ *port = 0; /* GDM1 */
+ break;
+ case 0x2 ... 0x4:
+ *port = sport - 1;
+ break;
+ case HSGMII_LAN_7583_ETH_SRCPORT:
+ *port = 2; /* GDM3 */
+ break;
+ case HSGMII_LAN_7583_USB_SRCPORT:
+ *dev = 1;
+ fallthrough;
+ case HSGMII_LAN_7583_PCIE_SRCPORT:
+ *port = 3; /* GDM4 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct airoha_eth_soc_data en7581_soc_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
@@ -3375,6 +3522,7 @@ static const struct airoha_eth_soc_data en7581_soc_data = {
.ops = {
.get_sport = airoha_en7581_get_sport,
.get_vip_port = airoha_en7581_get_vip_port,
+ .get_dev_from_sport = airoha_en7581_get_dev_from_sport,
},
};
@@ -3386,6 +3534,7 @@ static const struct airoha_eth_soc_data an7583_soc_data = {
.ops = {
.get_sport = airoha_an7583_get_sport,
.get_vip_port = airoha_an7583_get_vip_port,
+ .get_dev_from_sport = airoha_an7583_get_dev_from_sport,
},
};
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 694e83bfd1e7..8d6887e33413 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -17,6 +17,7 @@
#include <net/dsa.h>
#define AIROHA_MAX_NUM_GDM_PORTS 4
+#define AIROHA_MAX_NUM_GDM_DEVS 2
#define AIROHA_MAX_NUM_QDMA 2
#define AIROHA_MAX_NUM_IRQ_BANKS 4
#define AIROHA_MAX_DSA_PORTS 7
@@ -539,19 +540,20 @@ struct airoha_qdma {
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
struct airoha_qdma *qdma;
- struct net_device *dev;
struct airoha_eth *eth;
+ struct net_device *dev;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
+
+ int nbq;
};
struct airoha_gdm_port {
- struct airoha_gdm_dev *dev;
+ struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
int id;
- int nbq;
struct airoha_hw_stats stats;
@@ -587,6 +589,8 @@ struct airoha_eth_soc_data {
struct {
int (*get_sport)(struct airoha_gdm_port *port, int nbq);
u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq);
+ int (*get_dev_from_sport)(struct airoha_qdma_desc *desc,
+ u16 *port, u16 *dev);
} ops;
};
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 047141b2d6d8..c4086d29d984 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -169,6 +169,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
for (p = 0; p < ARRAY_SIZE(eth->ports); p++) {
struct airoha_gdm_port *port = eth->ports[p];
+ int j;
airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
FP0_EGRESS_MTU_MASK |
@@ -180,8 +181,16 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
if (!port)
continue;
- airoha_ppe_set_cpu_port(port->dev, i,
- airoha_get_fe_port(port->dev));
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ u8 fport;
+
+ if (!dev)
+ continue;
+
+ fport = airoha_get_fe_port(dev);
+ airoha_ppe_set_cpu_port(dev, i, fport);
+ }
}
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 09/12] net: airoha: Do not stop GDM port if it is shared
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (7 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 08/12] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 10/12] net: airoha: Introduce WAN device flag Lorenzo Bianconi
` (2 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
Theoretically, in the current codebase, two independent net_devices can
be connected to the same GDM port so we need to check the GDM port is not
used by any other running net_device before setting the forward
configuration to FE_PSE_PORT_DROP.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 39 +++++++++++++++++++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
2 files changed, 29 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index be31f639f4be..ebb24e60f1de 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1718,8 +1718,8 @@ static int airoha_dev_open(struct net_device *netdev)
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
+ u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
- u32 pse_port = FE_PSE_PORT_PPE1;
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
@@ -1733,10 +1733,20 @@ static int airoha_dev_open(struct net_device *netdev)
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
+ cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ GDM_LONG_LEN_MASK);
+ if (!port->users || len > cur_len) {
+ /* Opening a sibling net_device with a larger MTU updates the
+ * MTU of already running devices. This is required to allow
+ * multiple net_devices wht different MTUs to share the same
+ * QDMA block.
+ */
+ airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+ FIELD_PREP(GDM_LONG_LEN_MASK, len));
+ }
+ port->users++;
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -1766,8 +1776,10 @@ static int airoha_dev_stop(struct net_device *netdev)
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
- airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
- FE_PSE_PORT_DROP);
+ if (!--port->users)
+ airoha_set_gdm_port_fwd_cfg(qdma->eth,
+ REG_GDM_FWD_CFG(port->id),
+ FE_PSE_PORT_DROP);
if (atomic_dec_and_test(&qdma->users)) {
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
@@ -1919,13 +1931,18 @@ static void airoha_dev_get_stats64(struct net_device *netdev,
static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ u32 cur_len, len = ETH_HLEN + mtu + ETH_FCS_LEN;
struct airoha_gdm_port *port = dev->port;
- u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
struct airoha_eth *eth = dev->eth;
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
- GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_LONG_LEN_MASK, len));
+ cur_len = airoha_fe_get(eth, REG_GDM_LEN_CFG(port->id),
+ GDM_LONG_LEN_MASK);
+ if (len > cur_len || !port->users ||
+ (port->users == 1 && netif_running(netdev)))
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
+ GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_LONG_LEN_MASK, len));
+
WRITE_ONCE(netdev->mtu, mtu);
return 0;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 8d6887e33413..809f63a5d211 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -554,6 +554,7 @@ struct airoha_gdm_dev {
struct airoha_gdm_port {
struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
int id;
+ int users;
struct airoha_hw_stats stats;
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 10/12] net: airoha: Introduce WAN device flag
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (8 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 09/12] net: airoha: Do not stop GDM port if it is shared Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached Lorenzo Bianconi
11 siblings, 0 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
Introduce WAN flag to specify if a given device is used to transmit/receive
WAN or LAN traffic. Current codebase supports specifying LAN/WAN device
configuration in ndo_init() callback during device bootstrap.
Please note after this patch, it will be possible to specify multiple LAN
devices but just a single WAN one. Please note this change is not visible
to the user since airoha_eth driver currently supports just the internal
phy available via the MT7530 DSA switch and there are no WAN interfaces
officially supported since PCS/external phy is not merged mainline yet
(it will be posted with following patches).
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 74 ++++++++++++++++++++++++++------
drivers/net/ethernet/airoha/airoha_eth.h | 13 +++---
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 67 insertions(+), 22 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index ebb24e60f1de..fa37dbd76d57 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1871,36 +1871,82 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
return 0;
}
-static int airoha_dev_init(struct net_device *netdev)
+static struct airoha_gdm_dev *
+airoha_get_wan_gdm_dev(struct airoha_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+
+ if (dev && !airoha_is_lan_gdm_dev(dev))
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
{
- struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
dev->dev->irq = dev->qdma->irq_banks[0].irq;
- airoha_set_macaddr(dev, netdev->dev_addr);
+
+ for (i = 0; i < eth->soc->num_ppe; i++)
+ airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
+}
+
+static int airoha_dev_init(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
switch (port->id) {
case AIROHA_GDM3_IDX:
- case AIROHA_GDM4_IDX:
- /* If GDM2 is active we can't enable loopback */
- if (!eth->ports[1]) {
- int err;
+ case AIROHA_GDM4_IDX: {
+ struct airoha_eth *eth = dev->eth;
- err = airoha_set_gdm2_loopback(dev);
- if (err)
- return err;
- }
+ /* GDM2 supports a single net_device */
+ if (eth->ports[1] && eth->ports[1]->devs[0])
+ break;
+
+ if (airoha_get_wan_gdm_dev(eth))
+ break;
+
+ fallthrough;
+ }
+ case AIROHA_GDM2_IDX:
+ /* GDM2 is always used as wan */
+ dev->flags |= AIROHA_PRIV_F_WAN;
break;
default:
break;
}
- for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
+ airoha_dev_set_qdma(dev);
+ airoha_set_macaddr(dev, netdev->dev_addr);
+
+ if (!airoha_is_lan_gdm_dev(dev) &&
+ (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
+ int err;
+
+ err = airoha_set_gdm2_loopback(dev);
+ if (err) {
+ dev->flags &= ~AIROHA_PRIV_F_WAN;
+ return err;
+ }
+ }
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 809f63a5d211..4972dffc9c62 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -537,6 +537,10 @@ struct airoha_qdma {
DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
+enum airoha_priv_flags {
+ AIROHA_PRIV_F_WAN = BIT(0),
+};
+
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
struct airoha_qdma *qdma;
@@ -548,6 +552,7 @@ struct airoha_gdm_dev {
u64 cpu_tx_packets;
u64 fwd_tx_packets;
+ u32 flags;
int nbq;
};
@@ -654,13 +659,7 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
- struct airoha_gdm_port *port = dev->port;
-
- /* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
- * GDM{2,3,4} can be used as wan port connected to an external
- * phy module.
- */
- return port->id == 1;
+ return !(dev->flags & AIROHA_PRIV_F_WAN);
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index c4086d29d984..194cd50b2c74 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -350,7 +350,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
return -EINVAL;
port = dev->port;
- if (dsa_port >= 0 || eth->ports[1])
+ if (dsa_port >= 0 || airoha_is_lan_gdm_dev(dev))
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
: port->id;
else
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (9 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 10/12] net: airoha: Introduce WAN device flag Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached Lorenzo Bianconi
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Madhur Agrawal
The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN
MAC addresses, used to determine whether received traffic is destined for
this host or should be forwarded to another device.
The SoC hardware design assumes all interfaces configured as LAN (or WAN)
share a common upper MAC address, which is programmed into the
REG_FE_{LAN,WAN}_MAC_H register. The lower bytes of 'local' addresses can
be expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX
registers.
Previously, only a single interface was considered when programming these
registers. Extend the logic to derive the correct minimum and maximum
values for REG_FE_MAC_LMIN/REG_FE_MAC_LMAX when two or more interfaces are
configured as LAN or WAN.
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 75 +++++++++++++++++++++++++++-----
drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 4 +-
3 files changed, 66 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index fa37dbd76d57..623cac42dfb7 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -71,20 +71,67 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
-static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
+static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
struct airoha_eth *eth = dev->eth;
- u32 val, reg;
+ u8 ref_addr[ETH_ALEN] = {};
+ u32 reg, val, lmin, lmax;
+ int i;
+
+ lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5];
+ lmax = lmin;
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *iter_dev;
+ struct net_device *netdev;
+
+ iter_dev = port->devs[j];
+ if (!iter_dev || iter_dev == dev)
+ continue;
+
+ if (airoha_is_lan_gdm_dev(iter_dev) !=
+ airoha_is_lan_gdm_dev(dev))
+ continue;
+
+ netdev = iter_dev->dev;
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
+
+ ether_addr_copy(ref_addr, netdev->dev_addr);
+ val = (netdev->dev_addr[3] << 16) |
+ (netdev->dev_addr[4] << 8) | netdev->dev_addr[5];
+ if (val < lmin)
+ lmin = val;
+ if (val > lmax)
+ lmax = val;
+ }
+ }
+
+ if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
+ /* According to the HW design, hw mac address MS bits
+ * must be the same for each net_device with the same
+ * LAN/WAN configuration.
+ */
+ return -EINVAL;
+ }
reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
- val = (addr[3] << 16) | (addr[4] << 8) | addr[5];
- airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
- airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
+ airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin);
+ airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax);
- airoha_ppe_init_upd_mem(dev);
+ airoha_ppe_init_upd_mem(dev, addr);
+
+ return 0;
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -1800,13 +1847,18 @@ static int airoha_dev_stop(struct net_device *netdev)
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct sockaddr *addr = p;
int err;
- err = eth_mac_addr(netdev, p);
+ err = eth_prepare_mac_addr_change(netdev, p);
if (err)
return err;
- airoha_set_macaddr(dev, netdev->dev_addr);
+ err = airoha_set_macaddr(dev, addr->sa_data);
+ if (err)
+ return err;
+
+ eth_commit_mac_addr_change(netdev, p);
return 0;
}
@@ -1911,6 +1963,7 @@ static int airoha_dev_init(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
+ int err;
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -1935,12 +1988,12 @@ static int airoha_dev_init(struct net_device *netdev)
}
airoha_dev_set_qdma(dev);
- airoha_set_macaddr(dev, netdev->dev_addr);
+ err = airoha_set_macaddr(dev, netdev->dev_addr);
+ if (err)
+ return err;
if (!airoha_is_lan_gdm_dev(dev) &&
(port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
- int err;
-
err = airoha_set_gdm2_loopback(dev);
if (err) {
dev->flags &= ~AIROHA_PRIV_F_WAN;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 4972dffc9c62..4564a581d138 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -683,7 +683,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr);
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 194cd50b2c74..531ce33528b9 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1482,12 +1482,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr)
{
struct airoha_gdm_port *port = dev->port;
- struct net_device *netdev = dev->dev;
struct airoha_eth *eth = dev->eth;
- const u8 *addr = netdev->dev_addr;
u32 val;
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (10 preceding siblings ...)
2026-05-09 13:05 ` [PATCH net-next v5 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
@ 2026-05-09 13:05 ` Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
11 siblings, 1 reply; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-09 13:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree
From: Christian Marangi <ansuelsmth@gmail.com>
In the context of a GDM that can have multiple port attached (GDM3/4)
the HW counter (MIB) are global for the GDM port. This cause duplicated
stats reported to the kernel for the related interface.
The SoC supports a split MIB feature where each counter is tracked based
on the relevant HW channel (NBQ) to account for this scenario and
provide a way to select the related counter on accessing the MIB
registers.
Enable this feature for GDM3 and GDM4 and configure the relevant HW
channel before updating the HW stats to report correct HW counter to the
kernel for the related interface.
Also move the stats struct from port to dev since HW counter are
now specific to the network interface instead of the GDM port.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 169 ++++++++++++++++---------------
drivers/net/ethernet/airoha/airoha_eth.h | 4 +-
2 files changed, 91 insertions(+), 82 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 623cac42dfb7..b1a658629345 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1621,143 +1621,150 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
- spin_lock(&port->stats.lock);
- u64_stats_update_begin(&port->stats.syncp);
+ spin_lock(&dev->stats.lock);
+ u64_stats_update_begin(&dev->stats.syncp);
+
+ /* Read relevant MIB for GDM with multiple port attached */
+ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
+ airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
+ FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
+ FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
/* TX */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
- port->stats.tx_ok_pkts += ((u64)val << 32);
+ dev->stats.tx_ok_pkts += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
- port->stats.tx_ok_pkts += val;
+ dev->stats.tx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
- port->stats.tx_ok_bytes += ((u64)val << 32);
+ dev->stats.tx_ok_bytes += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
- port->stats.tx_ok_bytes += val;
+ dev->stats.tx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
- port->stats.tx_drops += val;
+ dev->stats.tx_drops += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
- port->stats.tx_broadcast += val;
+ dev->stats.tx_broadcast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
- port->stats.tx_multicast += val;
+ dev->stats.tx_multicast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
- port->stats.tx_len[i] += val;
+ dev->stats.tx_len[i] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
/* RX */
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
- port->stats.rx_ok_pkts += ((u64)val << 32);
+ dev->stats.rx_ok_pkts += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
- port->stats.rx_ok_pkts += val;
+ dev->stats.rx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
- port->stats.rx_ok_bytes += ((u64)val << 32);
+ dev->stats.rx_ok_bytes += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
- port->stats.rx_ok_bytes += val;
+ dev->stats.rx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
- port->stats.rx_drops += val;
+ dev->stats.rx_drops += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
- port->stats.rx_broadcast += val;
+ dev->stats.rx_broadcast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
- port->stats.rx_multicast += val;
+ dev->stats.rx_multicast += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
- port->stats.rx_errors += val;
+ dev->stats.rx_errors += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
- port->stats.rx_crc_error += val;
+ dev->stats.rx_crc_error += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
- port->stats.rx_over_errors += val;
+ dev->stats.rx_over_errors += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
- port->stats.rx_fragment += val;
+ dev->stats.rx_fragment += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
- port->stats.rx_jabber += val;
+ dev->stats.rx_jabber += val;
i = 0;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
- port->stats.rx_len[i] += val;
+ dev->stats.rx_len[i] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
/* reset mib counters */
airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
- u64_stats_update_end(&port->stats.syncp);
- spin_unlock(&port->stats.lock);
+ u64_stats_update_end(&dev->stats.syncp);
+ spin_unlock(&dev->stats.lock);
}
static int airoha_dev_open(struct net_device *netdev)
@@ -1970,6 +1977,11 @@ static int airoha_dev_init(struct net_device *netdev)
case AIROHA_GDM4_IDX: {
struct airoha_eth *eth = dev->eth;
+ /* Enable split MIB for GDM with multiple port attached */
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_GDM_TX_MIB_SPLIT_EN_MASK |
+ FE_GDM_RX_MIB_SPLIT_EN_MASK);
+
/* GDM2 supports a single net_device */
if (eth->ports[1] && eth->ports[1]->devs[0])
break;
@@ -2008,23 +2020,22 @@ static void airoha_dev_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *storage)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- storage->rx_packets = port->stats.rx_ok_pkts;
- storage->tx_packets = port->stats.tx_ok_pkts;
- storage->rx_bytes = port->stats.rx_ok_bytes;
- storage->tx_bytes = port->stats.tx_ok_bytes;
- storage->multicast = port->stats.rx_multicast;
- storage->rx_errors = port->stats.rx_errors;
- storage->rx_dropped = port->stats.rx_drops;
- storage->tx_dropped = port->stats.tx_drops;
- storage->rx_crc_errors = port->stats.rx_crc_error;
- storage->rx_over_errors = port->stats.rx_over_errors;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ storage->rx_packets = dev->stats.rx_ok_pkts;
+ storage->tx_packets = dev->stats.tx_ok_pkts;
+ storage->rx_bytes = dev->stats.rx_ok_bytes;
+ storage->tx_bytes = dev->stats.tx_ok_bytes;
+ storage->multicast = dev->stats.rx_multicast;
+ storage->rx_errors = dev->stats.rx_errors;
+ storage->rx_dropped = dev->stats.rx_drops;
+ storage->tx_dropped = dev->stats.tx_drops;
+ storage->rx_crc_errors = dev->stats.rx_crc_error;
+ storage->rx_over_errors = dev->stats.rx_over_errors;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
@@ -2283,20 +2294,19 @@ static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
struct ethtool_eth_mac_stats *stats)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
- stats->OctetsTransmittedOK = port->stats.tx_ok_bytes;
- stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
- stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
- stats->FramesReceivedOK = port->stats.rx_ok_pkts;
- stats->OctetsReceivedOK = port->stats.rx_ok_bytes;
- stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ stats->FramesTransmittedOK = dev->stats.tx_ok_pkts;
+ stats->OctetsTransmittedOK = dev->stats.tx_ok_bytes;
+ stats->MulticastFramesXmittedOK = dev->stats.tx_multicast;
+ stats->BroadcastFramesXmittedOK = dev->stats.tx_broadcast;
+ stats->FramesReceivedOK = dev->stats.rx_ok_pkts;
+ stats->OctetsReceivedOK = dev->stats.rx_ok_bytes;
+ stats->BroadcastFramesReceivedOK = dev->stats.rx_broadcast;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
@@ -2316,8 +2326,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
const struct ethtool_rmon_hist_range **ranges)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_hw_stats *hw_stats = &port->stats;
+ struct airoha_hw_stats *hw_stats = &dev->stats;
unsigned int start;
BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
@@ -2330,7 +2339,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
do {
int i;
- start = u64_stats_fetch_begin(&port->stats.syncp);
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
stats->fragments = hw_stats->rx_fragment;
stats->jabbers = hw_stats->rx_jabber;
for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
@@ -2338,7 +2347,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
stats->hist[i] = hw_stats->rx_len[i];
stats->hist_tx[i] = hw_stats->tx_len[i];
}
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
@@ -3185,6 +3194,8 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
+ u64_stats_init(&dev->stats.syncp);
+ spin_lock_init(&dev->stats.lock);
dev->dev = netdev;
dev->port = port;
dev->eth = eth;
@@ -3225,8 +3236,6 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
if (!port)
return -ENOMEM;
- u64_stats_init(&port->stats.syncp);
- spin_lock_init(&port->stats.lock);
port->id = id;
eth->ports[p] = port;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 4564a581d138..921d8aa01ac0 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -554,6 +554,8 @@ struct airoha_gdm_dev {
u32 flags;
int nbq;
+
+ struct airoha_hw_stats stats;
};
struct airoha_gdm_port {
@@ -561,8 +563,6 @@ struct airoha_gdm_port {
int id;
int users;
- struct airoha_hw_stats stats;
-
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties
2026-05-09 13:05 ` [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: devicetree, robh, krzk+dt, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The `reg` property check in the conditional `if` block uses an invalid JSON schema sequence that will always evaluate to false, causing valid device trees to fail validation.
- [High] The `ethernet-port@[0-5]` nodes model a `net_device` but strictly forbid standard network configuration properties like `mac-address` and `phy-handle`.
- [Low] Missing `#address-cells` and `#size-cells` in the `required` list for conditional child nodes.
--
commit 270a641148bef0e32520efd84b62bf6b97aa5a63
Author: Lorenzo Bianconi <lorenzo@kernel.org>
dt-bindings: net: airoha: Add EN7581 ethernet-ports properties
This commit introduces the ethernet-port property for EN7581 and AN7583
SoCs to model multiple net_devices connected via an external arbiter to
GDM3 or GDM4 ports.
> diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
> --- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
> +++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
[ ... ]
> @@ -130,6 +130,38 @@ patternProperties:
> maximum: 4
> description: GMAC port identifier
>
> + '#address-cells':
> + const: 1
> + '#size-cells':
> + const: 0
> +
> + allOf:
> + - if:
> + properties:
> + reg:
> + items:
> + - enum:
> + - 3
> + - 4
Will this schema condition evaluate as expected?
In dt-schema, reg properties are parsed into a matrix format, which is an
array of arrays of integers like [[3]]. This schema checks if the first
element of the outer array matches the scalar values 3 or 4, but since
the first element is an inner array, it won't match a scalar.
Could this cause the if block to always evaluate to false, causing valid
ethernet-port nodes to be incorrectly rejected because the parent node
sets unevaluatedProperties: false?
> + then:
Since #address-cells and #size-cells are needed for the ethernet@[3-4]
variants to act as buses for the new ethernet-port children, should they
be included in a required list within this conditional block?
Without them, an incomplete device tree could omit these properties and
silently pass schema validation.
> + patternProperties:
> + "^ethernet-port@[0-5]$":
> + type: object
> + unevaluatedProperties: false
Does setting unevaluatedProperties: false here without referencing
ethernet-controller.yaml# forbid standard network properties?
Since this models a net_device, driver code often expects to parse standard
properties like mac-address or phy-handle. Device tree authors might not
be able to configure them without triggering schema validation errors.
> + description: External ethernet port ID available on the GDM port
> +
> + properties:
> + compatible:
> + const: airoha,eth-port
> +
> + reg:
> + maxItems: 1
> + description: External ethernet port identifier
> +
> + required:
> + - compatible
> + - reg
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=1
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 02/12] net: airoha: Reserve RX headroom to avoid skb reallocation
2026-05-09 13:05 ` [PATCH net-next v5 02/12] net: airoha: Reserve RX headroom to avoid skb reallocation Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: conor+dt, devicetree, krzk+dt, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing bounds check adjustment for RX packet length allows buffer overflows
--
commit f34e8812cb62dd63f96801a39b0d65ee905cd258
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Reserve RX headroom to avoid skb reallocation
This commit reserves NET_SKB_PAD + NET_IP_ALIGN bytes of headroom for
received packets to avoid skb head reallocation when pushing protocol
headers into the skb.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
[ ... ]
> @@ -627,10 +629,12 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
>
> port = eth->ports[p];
> if (!q->skb) { /* first buffer */
> - q->skb = napi_build_skb(e->buf, q->buf_size);
> + q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
> + q->buf_size);
> if (!q->skb)
> goto free_frag;
>
> + skb_reserve(q->skb, AIROHA_RX_HEADROOM);
> __skb_put(q->skb, len);
Does this code overlook adjusting the bounds check for the RX packet
length?
Earlier in airoha_qdma_rx_process(), the software validation check
uses the unadjusted lengths:
data_len = q->skb ? q->buf_size
: SKB_WITH_OVERHEAD(q->buf_size);
if (!len || data_len < len)
goto free_frag;
If the hardware descriptor returns a len that is larger than the newly
adjusted dma_len but smaller than or equal to the old data_len, the
check will pass.
Since skb_reserve() shifts the skb->tail forward by AIROHA_RX_HEADROOM,
will calling __skb_put(q->skb, len) with this unvalidated oversized len
push skb->tail past skb->end and overflow the skb_shared_info structure?
Similarly for scattered fragments, could an oversized len passed to
skb_add_rx_frag() cause out-of-bounds memory accesses beyond the allocated
page fragment?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 08/12] net: airoha: Support multiple net_devices for a single FE GDM port
2026-05-09 13:05 ` [PATCH net-next v5 08/12] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Bringing up a device overwrites the global WAN port and GDM loopback configuration of previously opened devices sharing the same port.
- [Medium] Standard netdev statistics and ethtool MAC/RMON stats return the combined hardware counters for the entire GDM port instead of per-interface statistics.
--
commit 6b561eda8762bee9a19daa9304286dbd64cc6eac
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Support multiple net_devices for a single FE GDM port
Introduce support for multiple net_devices connected to the same Frame Engine
(FE) GDM port (GDM3 or GDM4) via an external hw arbiter.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 904f8da936005..be31f639f4be3 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
[ ... ]
> @@ -1829,7 +1833,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
> airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
> airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
>
> - src_port = eth->soc->ops.get_sport(port, port->nbq);
> + src_port = eth->soc->ops.get_sport(port, dev->nbq);
Does bringing up a device overwrite the global WAN port and GDM loopback
configuration of previously opened devices sharing the same port?
Since multiple devices can now share the same GDM port and each has a
different nbq (and therefore a different src_port), wouldn't the
initialization of the second device clobber the WAN port configuration of the
first one, breaking its forwarding behavior?
I noticed this is fixed later in the series in commit 8e8f8223af5be ("net:
airoha: Introduce WAN device flag"), but wanted to confirm the regression
between commits.
> if (src_port < 0)
> return src_port;
>
[ ... ]
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index 694e83bfd1e70..8d6887e334130 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
[ ... ]
> @@ -539,19 +540,20 @@ struct airoha_qdma {
> struct airoha_gdm_dev {
> struct airoha_gdm_port *port;
> struct airoha_qdma *qdma;
> - struct net_device *dev;
> struct airoha_eth *eth;
> + struct net_device *dev;
>
> DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> /* qos stats counters */
> u64 cpu_tx_packets;
> u64 fwd_tx_packets;
> +
> + int nbq;
> };
>
> struct airoha_gdm_port {
> - struct airoha_gdm_dev *dev;
> + struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
> int id;
> - int nbq;
>
> struct airoha_hw_stats stats;
Since standard netdev statistics and ethtool MAC/RMON stats are tied to the
hardware counters on the GDM port, do they now return the combined hardware
counters for the entire GDM port instead of per-interface statistics?
Prior to this patch, each GDM port mapped to a single net_device, making
port-wide hardware stats accurate for the interface. With multiple net_devices
sharing a single GDM port, querying statistics for one interface might
incorrectly return the aggregated traffic of all interfaces connected to that
port, violating standard rtnl_link_stats64 semantics.
I noticed this is fixed later in the series in commit 29334a9bde18f ("net:
airoha: Better handle MIB for GDM with multiple port attached").
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=8
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 06/12] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
2026-05-09 13:05 ` [PATCH net-next v5 06/12] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: krzk+dt, conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] HTB node modification unconditionally fails with -EBUSY and its error path can silently corrupt global channel ownership.
- [High] Hardware teardown failures cause a permanent leak of shared QDMA channels.
--
commit 1b146a034660fa016267e6a142564dd1ddeb17b1
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
This commit moves the qos_sq_bmap field from airoha_gdm_port to airoha_gdm_dev
to prevent configuration overwrites when multiple net_devices share a GDM port.
It also adds a qos_channel_map bitmap to airoha_qdma to track shared QDMA
channels.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index f9cb49f0529a0..33a0f7b98d52e 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2606,19 +2606,28 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
> int err, num_tx_queues = netdev->real_num_tx_queues;
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
> + struct airoha_qdma *qdma = dev->qdma;
>
> if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
> return -EINVAL;
> }
>
> + /* Here we need to check the requested QDMA channel is not already
> + * in use by another net_device running on the same QDMA block.
> + */
> + if (test_and_set_bit(channel, qdma->qos_channel_map)) {
> + NL_SET_ERR_MSG_MOD(opt->extack,
> + "qdma qos channel already in use");
> + return -EBUSY;
> + }
> +
Since this function handles both TC_HTB_LEAF_ALLOC_QUEUE and
TC_HTB_NODE_MODIFY, won't the bit already be set in qdma->qos_channel_map
for modify operations?
If so, will modify operations now always fail with -EBUSY here,
breaking HTB rate limit updates?
> err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
> opt->quantum);
> if (err) {
> NL_SET_ERR_MSG_MOD(opt->extack,
> "failed configuring htb offload");
> - return err;
> + goto error;
> }
>
> if (opt->command == TC_HTB_NODE_MODIFY)
> @@ -2630,13 +2639,17 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> opt->quantum);
> NL_SET_ERR_MSG_MOD(opt->extack,
> "failed setting real_num_tx_queues");
> - return err;
> + goto error;
> }
>
> - set_bit(channel, port->qos_sq_bmap);
> + set_bit(channel, dev->qos_sq_bmap);
> opt->qid = AIROHA_NUM_TX_RING + channel;
>
> return 0;
> +error:
> + clear_bit(channel, qdma->qos_channel_map);
> +
> + return err;
> }
If a modify operation (TC_HTB_NODE_MODIFY) fails at
airoha_qdma_set_tx_rate_limit(), it jumps to the error label.
Won't this unconditionally clear the bit in the shared qdma->qos_channel_map?
Could this accidentally release ownership of the shared QDMA channel while
the device is still using it, allowing another net_device to claim it?
[ ... ]
> @@ -2814,14 +2827,28 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
> }
> }
>
> -static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
> +static int airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
> + struct airoha_qdma *qdma = dev->qdma;
> + int err;
>
> - netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
> - airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
> - clear_bit(queue, port->qos_sq_bmap);
> + err = netif_set_real_num_tx_queues(netdev,
> + netdev->real_num_tx_queues - 1);
> + if (err)
> + return err;
> +
> + err = airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
> + if (err) {
> + netif_set_real_num_tx_queues(netdev,
> + netdev->real_num_tx_queues + 1);
> + return err;
> + }
> +
> + clear_bit(queue, qdma->qos_channel_map);
> + clear_bit(queue, dev->qos_sq_bmap);
> +
> + return 0;
> }
If a hardware teardown failure occurs and airoha_tc_remove_htb_queue()
returns early with an error, the bits in qdma->qos_channel_map and
dev->qos_sq_bmap are never cleared.
> static int airoha_tc_htb_destroy(struct net_device *netdev)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
> int q;
>
> - for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> - airoha_tc_remove_htb_queue(netdev, q);
> + for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) {
> + int err;
> +
> + err = airoha_tc_remove_htb_queue(netdev, q);
> + if (err)
> + return err;
> + }
>
> return 0;
> }
In this loop, an error on a single queue immediately aborts the loop, leaving
any remaining queues unprocessed.
Since the core networking stack (net/sched/sch_htb.c) ignores the return value
of TC_HTB_DESTROY and fully destroys the software qdisc regardless, will this
permanently leak the limited QDMA hardware channels?
Could these bits remain set forever in the globally shared qdma->qos_channel_map,
preventing other net_devices from using them until the driver is reloaded?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=6
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 09/12] net: airoha: Do not stop GDM port if it is shared
2026-05-09 13:05 ` [PATCH net-next v5 09/12] net: airoha: Do not stop GDM port if it is shared Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: devicetree, conor+dt, robh, krzk+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The `GDM_STAG_EN_MASK` configuration is port-global but is modified unconditionally per net_device, leading to silent clobbering when multiple devices share a port.
- [Medium] The hardware MTU (`REG_GDM_LEN_CFG`) is not recalculated when a device sharing the port is stopped, leaving the MAC configured to accept inappropriately large frames.
--
commit b896e320dcf0d56db7e0654191126c23e6f485fd
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Do not stop GDM port if it is shared
This commit introduces a users counter to the GDM port structure to allow
multiple independent net_device instances to share the same hardware port
without prematurely stopping it. It also conditionally updates the MTU so
that the largest MTU requested by any sharing device is used.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index be31f639f4be3..ebb24e60f1de3 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -1733,10 +1733,20 @@ static int airoha_dev_open(struct net_device *netdev)
> airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
> GDM_STAG_EN_MASK);
Since GDM_STAG_EN_MASK is a port-global configuration, will this clear the
flag for the entire port if a secondary non-DSA net_device is brought up?
If the primary master device relies on this hardware tag parsing, it appears
a secondary device could silently break it by clearing the mask during
its open routine. Should this configuration be protected or refcounted when
multiple devices share a port?
> - airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
> - GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
> - FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
> - FIELD_PREP(GDM_LONG_LEN_MASK, len));
> + cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
> + GDM_LONG_LEN_MASK);
> + if (!port->users || len > cur_len) {
> + /* Opening a sibling net_device with a larger MTU updates the
> + * MTU of already running devices. This is required to allow
> + * multiple net_devices wht different MTUs to share the same
> + * QDMA block.
> + */
> + airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
> + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
> + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
> + FIELD_PREP(GDM_LONG_LEN_MASK, len));
> + }
> + port->users++;
[ ... ]
> @@ -1766,8 +1776,10 @@ static int airoha_dev_stop(struct net_device *netdev)
> for (i = 0; i < netdev->num_tx_queues; i++)
> netdev_tx_reset_subqueue(netdev, i);
>
> - airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
> - FE_PSE_PORT_DROP);
> + if (!--port->users)
> + airoha_set_gdm_port_fwd_cfg(qdma->eth,
> + REG_GDM_FWD_CFG(port->id),
> + FE_PSE_PORT_DROP);
If multiple devices are sharing the port and the device with the largest MTU
is stopped, the hardware MTU in REG_GDM_LEN_CFG doesn't appear to be
recalculated or shrunk.
Since the MTU is only expanded in airoha_dev_open() and airoha_dev_change_mtu(),
does this leave the MAC configured to accept inappropriately large frames
for the remaining devices indefinitely?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=9
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
2026-05-09 13:05 ` [PATCH net-next v5 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: robh, conor+dt, krzk+dt, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Probe fails completely when devices use randomly generated MAC addresses due to strict MSB validation.
- [Medium] Strict MAC address MSB validation creates a configuration deadlock, preventing users from modifying MAC addresses.
--
commit d9f17b062a5dbe5adb3be07d9c3f501106b25dd8
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
This commit extends the hardware MAC address configuration in the airoha
Ethernet driver to support multiple LAN or WAN interfaces. It calculates
the minimum and maximum lower MAC address bytes across all devices with the
same configuration, and enforces that they share the same upper 3 MAC
address bytes.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index fa37dbd76d579..623cac42dfb71 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
[ ... ]
> -static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
> +static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
> {
> struct airoha_eth *eth = dev->eth;
> - u32 val, reg;
> + u8 ref_addr[ETH_ALEN] = {};
> + u32 reg, val, lmin, lmax;
> + int i;
> +
> + lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5];
> + lmax = lmin;
> +
> + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
[ ... ]
> + ether_addr_copy(ref_addr, netdev->dev_addr);
> + val = (netdev->dev_addr[3] << 16) |
> + (netdev->dev_addr[4] << 8) | netdev->dev_addr[5];
> + if (val < lmin)
> + lmin = val;
> + if (val > lmax)
> + lmax = val;
> + }
> + }
> +
> + if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
> + /* According to the HW design, hw mac address MS bits
> + * must be the same for each net_device with the same
> + * LAN/WAN configuration.
> + */
> + return -EINVAL;
> + }
Does this strict MSB validation cause device initialization to fail if the
device tree does not provide MAC addresses?
If the driver falls back to assigning addresses via eth_hw_addr_random(),
the randomly generated addresses will almost certainly have different upper
3 bytes. Will this cause airoha_set_macaddr() to return -EINVAL for the
second interface, aborting initialization and failing the driver probe?
Could this also create a configuration deadlock when users try to change
MAC addresses from userspace?
Since tools like 'ip link' configure interfaces sequentially, attempting to
change eth0 to a new OUI would be rejected because its new MSBs won't match
eth1's current MSBs. Attempting to change eth1 first would similarly fail
against eth0. Is there a way for users to successfully change the MAC
address MSBs once the interfaces are initialized?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=11
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached
2026-05-09 13:05 ` [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached Lorenzo Bianconi
@ 2026-05-10 14:05 ` sashiko-bot
2026-05-11 6:51 ` Lorenzo Bianconi
0 siblings, 1 reply; 21+ messages in thread
From: sashiko-bot @ 2026-05-10 14:05 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: krzk+dt, robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Moving the statistics lock from the port level to the device level introduces a race condition on shared hardware registers, causing cross-interface statistics corruption.
--
commit 29334a9bde18f0777df549f0d135356a4b8bd3ee
Author: Christian Marangi <ansuelsmth@gmail.com>
net: airoha: Better handle MIB for GDM with multiple port attached
This commit enables a split MIB feature for GDM3/4 to track counters per
hardware channel instead of per GDM port. It moves the statistics structure,
including its protecting lock, from the port level to the device level.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 623cac42dfb71..b1a6586293457 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -1621,143 +1621,150 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
> struct airoha_eth *eth = dev->eth;
> u32 val, i = 0;
>
> - spin_lock(&port->stats.lock);
> - u64_stats_update_begin(&port->stats.syncp);
> + spin_lock(&dev->stats.lock);
> + u64_stats_update_begin(&dev->stats.syncp);
> +
> + /* Read relevant MIB for GDM with multiple port attached */
> + if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
> + airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
> + FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
> + FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
> + FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
Does moving the lock from the port to the device level introduce a race
condition on the shared port MIB registers?
Because ndo_get_stats64 can be called concurrently without the RTNL lock, if two
CPUs request stats for two different devices on the same port:
CPU1 calls airoha_update_hw_stats() for dev_A:
spin_lock(&dev_A->stats.lock);
airoha_fe_rmw(..., REG_FE_GDM_MIB_CFG(port->id), ... dev_A->nbq);
CPU2 concurrently calls airoha_update_hw_stats() for dev_B:
spin_lock(&dev_B->stats.lock);
airoha_fe_rmw(..., REG_FE_GDM_MIB_CFG(port->id), ... dev_B->nbq);
Could CPU1 then read the hardware counters for dev_B instead of dev_A?
[ ... ]
> /* reset mib counters */
> airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
> FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
When CPU1 then clears the counters here, could it wipe out the unread
hardware statistics for dev_B?
Is a port-level lock still needed to exclusively protect access to these shared
hardware MIB registers across multiple devices?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=12
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached
2026-05-10 14:05 ` sashiko-bot
@ 2026-05-11 6:51 ` Lorenzo Bianconi
0 siblings, 0 replies; 21+ messages in thread
From: Lorenzo Bianconi @ 2026-05-11 6:51 UTC (permalink / raw)
To: sashiko; +Cc: krzk+dt, robh, conor+dt, devicetree
[-- Attachment #1: Type: text/plain, Size: 3014 bytes --]
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Moving the statistics lock from the port level to the device level introduces a race condition on shared hardware registers, causing cross-interface statistics corruption.
> --
>
> commit 29334a9bde18f0777df549f0d135356a4b8bd3ee
> Author: Christian Marangi <ansuelsmth@gmail.com>
>
> net: airoha: Better handle MIB for GDM with multiple port attached
>
> This commit enables a split MIB feature for GDM3/4 to track counters per
> hardware channel instead of per GDM port. It moves the statistics structure,
> including its protecting lock, from the port level to the device level.
>
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 623cac42dfb71..b1a6586293457 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -1621,143 +1621,150 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
> > struct airoha_eth *eth = dev->eth;
> > u32 val, i = 0;
> >
> > - spin_lock(&port->stats.lock);
> > - u64_stats_update_begin(&port->stats.syncp);
> > + spin_lock(&dev->stats.lock);
> > + u64_stats_update_begin(&dev->stats.syncp);
> > +
> > + /* Read relevant MIB for GDM with multiple port attached */
> > + if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
> > + airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
> > + FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
> > + FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
> > + FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
>
> Does moving the lock from the port to the device level introduce a race
> condition on the shared port MIB registers?
>
> Because ndo_get_stats64 can be called concurrently without the RTNL lock, if two
> CPUs request stats for two different devices on the same port:
>
> CPU1 calls airoha_update_hw_stats() for dev_A:
> spin_lock(&dev_A->stats.lock);
> airoha_fe_rmw(..., REG_FE_GDM_MIB_CFG(port->id), ... dev_A->nbq);
>
> CPU2 concurrently calls airoha_update_hw_stats() for dev_B:
> spin_lock(&dev_B->stats.lock);
> airoha_fe_rmw(..., REG_FE_GDM_MIB_CFG(port->id), ... dev_B->nbq);
>
> Could CPU1 then read the hardware counters for dev_B instead of dev_A?
ack, I will fix it in v6.
>
> [ ... ]
>
> > /* reset mib counters */
> > airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
> > FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
>
> When CPU1 then clears the counters here, could it wipe out the unread
> hardware statistics for dev_B?
>
> Is a port-level lock still needed to exclusively protect access to these shared
> hardware MIB registers across multiple devices?
ack, I will fix it in v6.
Regards,
Lorenzo
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260509-airoha-eth-multi-serdes-v5-0-805e38edc2aa@kernel.org?part=12
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-05-11 6:51 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-09 13:05 [PATCH net-next v5 00/12] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 01/12] dt-bindings: net: airoha: Add EN7581 ethernet-ports properties Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 02/12] net: airoha: Reserve RX headroom to avoid skb reallocation Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 03/12] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 04/12] net: airoha: Move airoha_qdma pointer in " Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 05/12] net: airoha: Rely on airoha_gdm_dev pointer in airhoa_is_lan_gdm_port() Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 06/12] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 07/12] net: airoha: Move {cpu,fwd}_tx_packets " Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 08/12] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 09/12] net: airoha: Do not stop GDM port if it is shared Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 10/12] net: airoha: Introduce WAN device flag Lorenzo Bianconi
2026-05-09 13:05 ` [PATCH net-next v5 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-09 13:05 ` [PATCH net-next v5 12/12] net: airoha: Better handle MIB for GDM with multiple port attached Lorenzo Bianconi
2026-05-10 14:05 ` sashiko-bot
2026-05-11 6:51 ` Lorenzo Bianconi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox