* [PATCH net-next v9 1/6] dt-bindings: net: airoha: Add GDM port ethernet child node
2026-06-03 6:00 [PATCH net-next v9 0/6] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
@ 2026-06-03 6:00 ` Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 2/6] net: airoha: Remove private net_device pointer in airoha_gdm_dev struct Lorenzo Bianconi
` (4 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-03 6:00 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 node child of a specific GDM port in order to model
a given net_device that is connected via the external arbiter to the
GDM{3,4} port. This new ethernet node is defined by the "airoha,eth-port"
compatible string. Please note GDM1 and GDM2 does not support the
connection with the external arbiter and they are represented by an
ethernet node defined by the "airoha,eth-mac" compatible string.
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
.../devicetree/bindings/net/airoha,en7581-eth.yaml | 56 +++++++++++++++++++++-
1 file changed, 55 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..17fe2edf4886 100644
--- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
+++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
@@ -130,6 +130,42 @@ patternProperties:
maximum: 4
description: GMAC port identifier
+ allOf:
+ - if:
+ properties:
+ reg:
+ contains:
+ items:
+ - enum:
+ - 3
+ - 4
+ then:
+ properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ patternProperties:
+ "^ethernet@[0-5]$":
+ type: object
+ unevaluatedProperties: false
+ $ref: ethernet-controller.yaml#
+ description: External ethernet port ID available on the GDM port
+
+ properties:
+ compatible:
+ const: airoha,eth-port
+
+ reg:
+ maximum: 5
+ description: External ethernet port identifier
+
+ required:
+ - reg
+ - compatible
+
required:
- reg
- compatible
@@ -191,9 +227,27 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- mac: ethernet@1 {
+ ethernet@1 {
compatible = "airoha,eth-mac";
reg = <1>;
};
+
+ ethernet@4 {
+ compatible = "airoha,eth-mac";
+ reg = <4>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet@0 {
+ compatible = "airoha,eth-port";
+ reg = <0>;
+ };
+
+ ethernet@1 {
+ compatible = "airoha,eth-port";
+ reg = <1>;
+ };
+ };
};
};
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH net-next v9 2/6] net: airoha: Remove private net_device pointer in airoha_gdm_dev struct
2026-06-03 6:00 [PATCH net-next v9 0/6] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 1/6] dt-bindings: net: airoha: Add GDM port ethernet child node Lorenzo Bianconi
@ 2026-06-03 6:00 ` Lorenzo Bianconi
2026-06-04 6:01 ` sashiko-bot
2026-06-03 6:00 ` [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-03 6:00 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
Remove redundant net_device pointer inside airoha_gdm_dev struct and
rely on netdev_from_priv routine instead. Please note this patch does
not introduce any logical change, just code refactoring.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 23 ++++++++++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 1 -
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 6574901ebd19..57a16de0a2ec 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -629,7 +629,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
if (!port->dev)
goto free_frag;
- netdev = port->dev->dev;
+ netdev = netdev_from_priv(port->dev);
if (!q->skb) { /* first buffer */
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -853,6 +853,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;
+ struct net_device *netdev;
int j;
if (!port)
@@ -865,11 +866,12 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
if (dev->qdma != qdma)
continue;
- for (j = 0; j < dev->dev->num_tx_queues; j++) {
+ netdev = netdev_from_priv(dev);
+ for (j = 0; j < netdev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
- netif_wake_subqueue(dev->dev, j);
+ netif_wake_subqueue(netdev, j);
}
}
q->txq_stopped = false;
@@ -1867,7 +1869,7 @@ static int airoha_dev_init(struct net_device *netdev)
/* 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;
+ netdev->irq = dev->qdma->irq_banks[0].irq;
airoha_set_macaddr(dev, netdev->dev_addr);
switch (port->id) {
@@ -3038,7 +3040,6 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
}
dev = netdev_priv(netdev);
- dev->dev = netdev;
dev->port = port;
port->dev = dev;
dev->eth = eth;
@@ -3101,7 +3102,7 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth)
if (!port)
continue;
- err = register_netdev(port->dev->dev);
+ err = register_netdev(netdev_from_priv(port->dev));
if (err)
return err;
}
@@ -3216,8 +3217,12 @@ static int airoha_probe(struct platform_device *pdev)
continue;
dev = port->dev;
- if (dev && dev->dev->reg_state == NETREG_REGISTERED)
- unregister_netdev(dev->dev);
+ if (dev) {
+ struct net_device *netdev = netdev_from_priv(dev);
+
+ if (netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(netdev);
+ }
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3245,7 +3250,7 @@ static void airoha_remove(struct platform_device *pdev)
dev = port->dev;
if (dev)
- unregister_netdev(dev->dev);
+ unregister_netdev(netdev_from_priv(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 fbb50dc73af8..1f162fa1405e 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -540,7 +540,6 @@ struct airoha_qdma {
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
struct airoha_qdma *qdma;
- struct net_device *dev;
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 047141b2d6d8..c9711bb7ef1c 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1475,8 +1475,8 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
{
+ struct net_device *netdev = netdev_from_priv(dev);
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;
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH net-next v9 2/6] net: airoha: Remove private net_device pointer in airoha_gdm_dev struct
2026-06-03 6:00 ` [PATCH net-next v9 2/6] net: airoha: Remove private net_device pointer in airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-06-04 6:01 ` sashiko-bot
2026-06-04 7:50 ` Lorenzo Bianconi
0 siblings, 1 reply; 15+ messages in thread
From: sashiko-bot @ 2026-06-04 6:01 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Potential deadlock in airoha_update_hw_stats() due to use of spin_lock() instead of spin_lock_bh().
--
commit d33fdec52e6f77bf6de4ab248729e1a868e78dc6
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Remove private net_device pointer in airoha_gdm_dev struct
This commit removes the redundant net_device pointer from the
airoha_gdm_dev struct. The code now relies on the netdev_from_priv()
routine to retrieve the net_device pointer. This is a refactoring change
with no logical modifications.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 6574901ebd19b..57a16de0a2ec7 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but I noticed a potential
deadlock in airoha_update_hw_stats():
drivers/net/ethernet/airoha/airoha_eth.c:airoha_update_hw_stats() {
...
spin_lock(&port->stats.lock);
...
}
Does this code risk a deadlock? The ndo_get_stats64 callback
(airoha_dev_get_stats64) can be executed in softirq context, while this
lock is also acquired from process context (e.g., via ethtool) without
disabling softirqs.
If a softirq interrupts the process context reader holding the lock on the
same CPU, wouldn't it spin forever trying to acquire the same lock? Should
spin_lock_bh() be used here instead to prevent this?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org?part=2
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH net-next v9 2/6] net: airoha: Remove private net_device pointer in airoha_gdm_dev struct
2026-06-04 6:01 ` sashiko-bot
@ 2026-06-04 7:50 ` Lorenzo Bianconi
0 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-04 7:50 UTC (permalink / raw)
To: sashiko-reviews; +Cc: conor+dt, robh, devicetree, netdev
[-- Attachment #1: Type: text/plain, Size: 2059 bytes --]
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] Potential deadlock in airoha_update_hw_stats() due to use of spin_lock() instead of spin_lock_bh().
> --
>
> commit d33fdec52e6f77bf6de4ab248729e1a868e78dc6
> Author: Lorenzo Bianconi <lorenzo@kernel.org>
>
> net: airoha: Remove private net_device pointer in airoha_gdm_dev struct
>
> This commit removes the redundant net_device pointer from the
> airoha_gdm_dev struct. The code now relies on the netdev_from_priv()
> routine to retrieve the net_device pointer. This is a refactoring change
> with no logical modifications.
>
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 6574901ebd19b..57a16de0a2ec7 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
>
> [ ... ]
>
> [Severity: High]
> This isn't a bug introduced by this patch, but I noticed a potential
> deadlock in airoha_update_hw_stats():
>
> drivers/net/ethernet/airoha/airoha_eth.c:airoha_update_hw_stats() {
> ...
> spin_lock(&port->stats.lock);
> ...
> }
>
> Does this code risk a deadlock? The ndo_get_stats64 callback
> (airoha_dev_get_stats64) can be executed in softirq context, while this
> lock is also acquired from process context (e.g., via ethtool) without
> disabling softirqs.
>
> If a softirq interrupts the process context reader holding the lock on the
> same CPU, wouldn't it spin forever trying to acquire the same lock? Should
> spin_lock_bh() be used here instead to prevent this?
As pointed out during previous iterations of the series, I do not think this is
a real issue since ndo_get_stats64() callback can't be run in softirq context.
Moreover, the reported issue has not been introduced by this series.
Regards,
Lorenzo
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org?part=2
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port
2026-06-03 6:00 [PATCH net-next v9 0/6] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 1/6] dt-bindings: net: airoha: Add GDM port ethernet child node Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 2/6] net: airoha: Remove private net_device pointer in airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-06-03 6:00 ` Lorenzo Bianconi
2026-06-04 6:01 ` sashiko-bot
2026-06-04 8:41 ` Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 4/6] net: airoha: Do not stop GDM port if it is shared Lorenzo Bianconi
` (2 subsequent siblings)
5 siblings, 2 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-03 6:00 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 | 280 +++++++++++++++++++++++--------
drivers/net/ethernet/airoha/airoha_eth.h | 8 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 26 ++-
3 files changed, 237 insertions(+), 77 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 57a16de0a2ec..1d088d95d5fb 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);
@@ -566,24 +566,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)
@@ -598,9 +600,9 @@ 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 airoha_gdm_dev *dev;
struct net_device *netdev;
- int data_len, len, p;
+ int data_len, len;
struct page *page;
desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
@@ -621,15 +623,11 @@ 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];
- if (!port->dev)
- goto free_frag;
-
- netdev = netdev_from_priv(port->dev);
+ netdev = netdev_from_priv(dev);
if (!q->skb) { /* first buffer */
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -659,6 +657,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
continue;
if (netdev_uses_dsa(netdev)) {
+ 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
@@ -852,26 +852,29 @@ 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;
- struct net_device *netdev;
- 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];
+ struct net_device *netdev;
+ int j;
- if (dev->qdma != qdma)
- continue;
+ if (!dev)
+ continue;
- netdev = netdev_from_priv(dev);
- for (j = 0; j < netdev->num_tx_queues; j++) {
- if (airoha_qdma_get_txq(qdma, j) != qid)
+ if (dev->qdma != qdma)
continue;
- netif_wake_subqueue(netdev, j);
+ netdev = netdev_from_priv(dev);
+ for (j = 0; j < netdev->num_tx_queues; j++) {
+ if (airoha_qdma_get_txq(qdma, j) != qid)
+ continue;
+
+ netif_wake_subqueue(netdev, j);
+ }
}
}
q->txq_stopped = false;
@@ -1742,11 +1745,9 @@ static int airoha_dev_open(struct net_device *netdev)
GLOBAL_CFG_RX_DMA_EN_MASK);
atomic_inc(&qdma->users);
- if (port->id == AIROHA_GDM2_IDX &&
- airoha_ppe_is_enabled(qdma->eth, 1)) {
- /* For PPE2 always use secondary cpu port. */
+ if (!airoha_is_lan_gdm_dev(dev) &&
+ airoha_ppe_is_enabled(qdma->eth, 1))
pse_port = FE_PSE_PORT_PPE2;
- }
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
pse_port);
@@ -1834,7 +1835,7 @@ static int airoha_enable_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;
@@ -1851,7 +1852,7 @@ static int airoha_enable_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));
@@ -1865,7 +1866,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;
struct airoha_eth *eth = dev->eth;
- int i;
+ int ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
@@ -1888,8 +1889,8 @@ static int airoha_dev_init(struct net_device *netdev)
break;
}
- for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
+ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+ airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
return 0;
}
@@ -2055,7 +2056,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];
@@ -2985,12 +2987,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;
@@ -2998,10 +3003,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),
@@ -3021,7 +3027,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 */
@@ -3039,10 +3044,24 @@ 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->port = port;
- port->dev = dev;
dev->eth = eth;
+ dev->nbq = nbq;
+ port->devs[index] = dev;
return 0;
}
@@ -3052,7 +3071,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) {
@@ -3080,15 +3100,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)
@@ -3097,14 +3153,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(netdev_from_priv(port->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(netdev_from_priv(dev));
+ if (err)
+ return err;
+ }
}
set_bit(DEV_STATE_REGISTERED, ð->state);
@@ -3211,17 +3275,22 @@ 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) {
- struct net_device *netdev = netdev_from_priv(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 = netdev_from_priv(dev);
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
+ of_node_put(netdev->dev.of_node);
}
airoha_metadata_dst_free(port);
}
@@ -3243,14 +3312,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(netdev_from_priv(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 = netdev_from_priv(dev);
+ unregister_netdev(netdev);
+ of_node_put(netdev->dev.of_node);
+ }
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3313,6 +3390,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",
@@ -3362,6 +3472,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,
@@ -3370,6 +3510,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,
},
};
@@ -3381,6 +3522,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 1f162fa1405e..92fd81bb9269 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
@@ -546,12 +547,13 @@ struct airoha_gdm_dev {
/* 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 c9711bb7ef1c..96abf451fdac 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -167,9 +167,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
PPE_FLOW_CFG_IP6_6RD_MASK);
- for (p = 0; p < ARRAY_SIZE(eth->ports); p++) {
- struct airoha_gdm_port *port = eth->ports[p];
-
+ for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
FP0_EGRESS_MTU_MASK |
FP1_EGRESS_MTU_MASK,
@@ -177,11 +175,27 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
AIROHA_MAX_MTU) |
FIELD_PREP(FP1_EGRESS_MTU_MASK,
AIROHA_MAX_MTU));
- if (!port)
+ }
+
+ 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];
+ int ppe_id;
+ u8 fport;
+
+ if (!dev)
continue;
- airoha_ppe_set_cpu_port(port->dev, i,
- airoha_get_fe_port(port->dev));
+ ppe_id = !airoha_is_lan_gdm_dev(dev) &&
+ airoha_ppe_is_enabled(eth, 1);
+ fport = airoha_get_fe_port(dev);
+ airoha_ppe_set_cpu_port(dev, ppe_id, fport);
}
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port
2026-06-03 6:00 ` [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
@ 2026-06-04 6:01 ` sashiko-bot
2026-06-04 8:33 ` Lorenzo Bianconi
2026-06-04 8:41 ` Lorenzo Bianconi
1 sibling, 1 reply; 15+ messages in thread
From: sashiko-bot @ 2026-06-04 6:01 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Hardware configuration clashing for shared GDM ports
- [Medium] Hardware statistics are incorrectly shared across all net_devices attached to the same GDM port
--
commit 18f4c136c0685846d452a3b0c6a493df8cf6dda0
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Support multiple net_devices for a single FE GDM port
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.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 57a16de0a2ec7..1d088d95d5fbc 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
[ ... ]
> @@ -1742,11 +1745,9 @@ static int airoha_dev_open(struct net_device *netdev)
> GLOBAL_CFG_RX_DMA_EN_MASK);
> atomic_inc(&qdma->users);
>
> - if (port->id == AIROHA_GDM2_IDX &&
> - airoha_ppe_is_enabled(qdma->eth, 1)) {
> - /* For PPE2 always use secondary cpu port. */
> + if (!airoha_is_lan_gdm_dev(dev) &&
> + airoha_ppe_is_enabled(qdma->eth, 1))
> pse_port = FE_PSE_PORT_PPE2;
> - }
> airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
> pse_port);
[Severity: High]
Since multiple net_devices now share a single physical GDM port, does calling
this unconditionally overwrite the shared port's forwarding configuration?
If one device on the shared port is brought up as a LAN port and another as
a WAN port, it seems the last opened device will override the forwarding
destination for all sibling devices on that port.
Are there similar issues with the ingress configuration? Earlier in
airoha_dev_open(), REG_GDM_INGRESS_CFG(port->id) is modified based on whether
the specific netdev uses DSA:
if (netdev_uses_dsa(netdev))
airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
else
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
Also, do airoha_dev_stop() and airoha_dev_change_mtu() have similar
regressions? They appear to blindly overwrite shared registers like
REG_GDM_FWD_CFG (setting it to FE_PSE_PORT_DROP) and REG_GDM_LEN_CFG(port->id).
I noticed the stop() and MTU issues might be fixed in a subsequent patch, but
should this intermediate patch be self-contained to avoid breaking
bisectability?
[ ... ]
> @@ -3080,15 +3100,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;
[Severity: Medium]
Will these multiplexed devices report duplicated, incorrect hardware statistics?
When userspace queries statistics for any single multiplexed device,
airoha_dev_get_stats64() fetches from port->stats, which is updated by
airoha_update_hw_stats() reading the clear-on-read MAC hardware counters for
the entire physical GDM port.
Since the port->stats structure is shared across all net_devices attached to
the port, each device will report the total traffic volume of all devices
sharing the port rather than its own traffic.
Should the driver rely on per-queue software counters to provide accurate
per-device statistics when hardware MACs are multiplexed?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org?part=3
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port
2026-06-04 6:01 ` sashiko-bot
@ 2026-06-04 8:33 ` Lorenzo Bianconi
0 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-04 8:33 UTC (permalink / raw)
To: sashiko-reviews; +Cc: conor+dt, robh, devicetree, netdev
[-- Attachment #1: Type: text/plain, Size: 2836 bytes --]
[...]
>
> Are there similar issues with the ingress configuration? Earlier in
> airoha_dev_open(), REG_GDM_INGRESS_CFG(port->id) is modified based on whether
> the specific netdev uses DSA:
>
> if (netdev_uses_dsa(netdev))
> airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
> GDM_STAG_EN_MASK);
> else
> airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
> GDM_STAG_EN_MASK);
This is not a real issue since just GDM1 can be used as cpu port of the dsa
switch (MT7630) and we do not support multiple net_devices for GDM1.
>
> Also, do airoha_dev_stop() and airoha_dev_change_mtu() have similar
> regressions? They appear to blindly overwrite shared registers like
> REG_GDM_FWD_CFG (setting it to FE_PSE_PORT_DROP) and REG_GDM_LEN_CFG(port->id).
>
> I noticed the stop() and MTU issues might be fixed in a subsequent patch, but
> should this intermediate patch be self-contained to avoid breaking
> bisectability?
These issues are addressed in subsequent patches on the same series.
>
> [ ... ]
> > @@ -3080,15 +3100,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;
>
> [Severity: Medium]
> Will these multiplexed devices report duplicated, incorrect hardware statistics?
hw statistics will be addressed with a dedicated patch not part of the series.
Regards,
Lorenzo
>
> When userspace queries statistics for any single multiplexed device,
> airoha_dev_get_stats64() fetches from port->stats, which is updated by
> airoha_update_hw_stats() reading the clear-on-read MAC hardware counters for
> the entire physical GDM port.
>
> Since the port->stats structure is shared across all net_devices attached to
> the port, each device will report the total traffic volume of all devices
> sharing the port rather than its own traffic.
>
> Should the driver rely on per-queue software counters to provide accurate
> per-device statistics when hardware MACs are multiplexed?
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org?part=3
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port
2026-06-03 6:00 ` [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
2026-06-04 6:01 ` sashiko-bot
@ 2026-06-04 8:41 ` Lorenzo Bianconi
1 sibling, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-04 8:41 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Xuegang Lu
[-- Attachment #1: Type: text/plain, Size: 22410 bytes --]
> 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 | 280 +++++++++++++++++++++++--------
> drivers/net/ethernet/airoha/airoha_eth.h | 8 +-
> drivers/net/ethernet/airoha/airoha_ppe.c | 26 ++-
> 3 files changed, 237 insertions(+), 77 deletions(-)
commenting on sashiko's report:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426%40kernel.org
- With multiple net_devices now allowed on the same GDM3/GDM4 port,
airoha_dev_init() runs the GDM3/GDM4 branch of the switch for every
device, which calls airoha_enable_gdm2_loopback() once per device.
Inside that function:
src_port = eth->soc->ops.get_sport(port, dev->nbq);
...
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, src_port));
the rmw clears WAN0/WAN1/WAN1_EN and writes only WAN0. When two devices
share a port (for example EN7581 GDM3 with nbq=4 and nbq=5, giving
HSGMII_LAN_7581_PCIE0_SRCPORT and HSGMII_LAN_7581_PCIE1_SRCPORT, or
ETH/USB on GDM4) wouldn't the second invocation overwrite the first
device's src_port in WAN0, leaving the first device's traffic
unrecognized as WAN even though the hardware register has WAN0/WAN1
slots specifically for two ports?
- We can run airoha_enable_gdm2_loopback() just for the single device
configured as WAN. As pointed out by the report, this is enforced by a
following patch in the series.
- Should d++ live below the of_device_is_available() check?
- This is done on purpose to highlight device-tree misconfigurations.
Regards,
Lorenzo
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 57a16de0a2ec..1d088d95d5fb 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);
> @@ -566,24 +566,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)
> @@ -598,9 +600,9 @@ 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 airoha_gdm_dev *dev;
> struct net_device *netdev;
> - int data_len, len, p;
> + int data_len, len;
> struct page *page;
>
> desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
> @@ -621,15 +623,11 @@ 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];
> - if (!port->dev)
> - goto free_frag;
> -
> - netdev = netdev_from_priv(port->dev);
> + netdev = netdev_from_priv(dev);
> if (!q->skb) { /* first buffer */
> q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
> q->buf_size);
> @@ -659,6 +657,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
> continue;
>
> if (netdev_uses_dsa(netdev)) {
> + 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
> @@ -852,26 +852,29 @@ 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;
> - struct net_device *netdev;
> - 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];
> + struct net_device *netdev;
> + int j;
>
> - if (dev->qdma != qdma)
> - continue;
> + if (!dev)
> + continue;
>
> - netdev = netdev_from_priv(dev);
> - for (j = 0; j < netdev->num_tx_queues; j++) {
> - if (airoha_qdma_get_txq(qdma, j) != qid)
> + if (dev->qdma != qdma)
> continue;
>
> - netif_wake_subqueue(netdev, j);
> + netdev = netdev_from_priv(dev);
> + for (j = 0; j < netdev->num_tx_queues; j++) {
> + if (airoha_qdma_get_txq(qdma, j) != qid)
> + continue;
> +
> + netif_wake_subqueue(netdev, j);
> + }
> }
> }
> q->txq_stopped = false;
> @@ -1742,11 +1745,9 @@ static int airoha_dev_open(struct net_device *netdev)
> GLOBAL_CFG_RX_DMA_EN_MASK);
> atomic_inc(&qdma->users);
>
> - if (port->id == AIROHA_GDM2_IDX &&
> - airoha_ppe_is_enabled(qdma->eth, 1)) {
> - /* For PPE2 always use secondary cpu port. */
> + if (!airoha_is_lan_gdm_dev(dev) &&
> + airoha_ppe_is_enabled(qdma->eth, 1))
> pse_port = FE_PSE_PORT_PPE2;
> - }
> airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
> pse_port);
>
> @@ -1834,7 +1835,7 @@ static int airoha_enable_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;
>
> @@ -1851,7 +1852,7 @@ static int airoha_enable_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));
> @@ -1865,7 +1866,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;
> struct airoha_eth *eth = dev->eth;
> - int i;
> + int ppe_id;
>
> /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
> dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
> @@ -1888,8 +1889,8 @@ static int airoha_dev_init(struct net_device *netdev)
> break;
> }
>
> - for (i = 0; i < eth->soc->num_ppe; i++)
> - airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
> + ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
> + airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
>
> return 0;
> }
> @@ -2055,7 +2056,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];
> @@ -2985,12 +2987,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;
> @@ -2998,10 +3003,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),
> @@ -3021,7 +3027,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 */
> @@ -3039,10 +3044,24 @@ 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->port = port;
> - port->dev = dev;
> dev->eth = eth;
> + dev->nbq = nbq;
> + port->devs[index] = dev;
>
> return 0;
> }
> @@ -3052,7 +3071,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) {
> @@ -3080,15 +3100,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)
> @@ -3097,14 +3153,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(netdev_from_priv(port->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(netdev_from_priv(dev));
> + if (err)
> + return err;
> + }
> }
>
> set_bit(DEV_STATE_REGISTERED, ð->state);
> @@ -3211,17 +3275,22 @@ 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) {
> - struct net_device *netdev = netdev_from_priv(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 = netdev_from_priv(dev);
> if (netdev->reg_state == NETREG_REGISTERED)
> unregister_netdev(netdev);
> + of_node_put(netdev->dev.of_node);
> }
> airoha_metadata_dst_free(port);
> }
> @@ -3243,14 +3312,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(netdev_from_priv(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 = netdev_from_priv(dev);
> + unregister_netdev(netdev);
> + of_node_put(netdev->dev.of_node);
> + }
> airoha_metadata_dst_free(port);
> }
> airoha_hw_cleanup(eth);
> @@ -3313,6 +3390,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",
> @@ -3362,6 +3472,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,
> @@ -3370,6 +3510,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,
> },
> };
>
> @@ -3381,6 +3522,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 1f162fa1405e..92fd81bb9269 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
> @@ -546,12 +547,13 @@ struct airoha_gdm_dev {
> /* 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 c9711bb7ef1c..96abf451fdac 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -167,9 +167,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
> airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
> PPE_FLOW_CFG_IP6_6RD_MASK);
>
> - for (p = 0; p < ARRAY_SIZE(eth->ports); p++) {
> - struct airoha_gdm_port *port = eth->ports[p];
> -
> + for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
> airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
> FP0_EGRESS_MTU_MASK |
> FP1_EGRESS_MTU_MASK,
> @@ -177,11 +175,27 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
> AIROHA_MAX_MTU) |
> FIELD_PREP(FP1_EGRESS_MTU_MASK,
> AIROHA_MAX_MTU));
> - if (!port)
> + }
> +
> + 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];
> + int ppe_id;
> + u8 fport;
> +
> + if (!dev)
> continue;
>
> - airoha_ppe_set_cpu_port(port->dev, i,
> - airoha_get_fe_port(port->dev));
> + ppe_id = !airoha_is_lan_gdm_dev(dev) &&
> + airoha_ppe_is_enabled(eth, 1);
> + fport = airoha_get_fe_port(dev);
> + airoha_ppe_set_cpu_port(dev, ppe_id, fport);
> }
> }
> }
>
> --
> 2.54.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH net-next v9 4/6] net: airoha: Do not stop GDM port if it is shared
2026-06-03 6:00 [PATCH net-next v9 0/6] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (2 preceding siblings ...)
2026-06-03 6:00 ` [PATCH net-next v9 3/6] net: airoha: Support multiple net_devices for a single FE GDM port Lorenzo Bianconi
@ 2026-06-03 6:00 ` Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 5/6] net: airoha: Introduce WAN device flag Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
5 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-03 6:00 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.
Moreover, always set in GDM_LONG_LEN_MASK field of REG_GDM_LEN_CFG
register the maximum MTU of all running 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 | 59 +++++++++++++++++++++++++-------
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
2 files changed, 48 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 1d088d95d5fb..44b77a5cc4c5 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1720,8 +1720,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);
@@ -1735,10 +1735,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 with different MTUs to share the same
+ * GDM 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));
+ }
+ port->users++;
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -1754,6 +1764,30 @@ static int airoha_dev_open(struct net_device *netdev)
return 0;
}
+static void airoha_set_port_mtu(struct airoha_eth *eth,
+ struct airoha_gdm_port *port)
+{
+ u32 len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ struct airoha_gdm_dev *dev = port->devs[i];
+ struct net_device *netdev;
+
+ if (!dev)
+ continue;
+
+ netdev = netdev_from_priv(dev);
+ if (netif_running(netdev))
+ len = max_t(u32, len, netdev->mtu);
+ }
+ len += ETH_HLEN + ETH_FCS_LEN;
+
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
+ GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_LONG_LEN_MASK, len));
+}
+
static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -1766,8 +1800,12 @@ 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_port_mtu(dev->eth, port);
+ else
+ 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,
@@ -1922,13 +1960,10 @@ 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;
- 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));
WRITE_ONCE(netdev->mtu, mtu);
+ if (port->users)
+ airoha_set_port_mtu(dev->eth, port);
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 92fd81bb9269..666e9246e70e 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] 15+ messages in thread* [PATCH net-next v9 5/6] net: airoha: Introduce WAN device flag
2026-06-03 6:00 [PATCH net-next v9 0/6] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (3 preceding siblings ...)
2026-06-03 6:00 ` [PATCH net-next v9 4/6] net: airoha: Do not stop GDM port if it is shared Lorenzo Bianconi
@ 2026-06-03 6:00 ` Lorenzo Bianconi
2026-06-03 6:00 ` [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
5 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-03 6:00 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.
In order to consider setups where LAN configuration is used even for
GDM3/GDM4 devices, check airoha_is_lan_gdm_dev() to select pse_port in
airoha_ppe_foe_entry_prepare().
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 | 73 ++++++++++++++++++++++++++------
drivers/net/ethernet/airoha/airoha_eth.h | 13 +++---
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 66 insertions(+), 22 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 44b77a5cc4c5..64ee526da241 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1899,36 +1899,81 @@ static int airoha_enable_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)
{
- struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ 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 net_device *netdev = netdev_from_priv(dev);
struct airoha_eth *eth = dev->eth;
int ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
netdev->irq = dev->qdma->irq_banks[0].irq;
- airoha_set_macaddr(dev, netdev->dev_addr);
+
+ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+ airoha_ppe_set_cpu_port(dev, ppe_id, 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_enable_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;
}
- ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
- airoha_ppe_set_cpu_port(dev, ppe_id, 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_enable_gdm2_loopback(dev);
+ if (err)
+ return err;
+ }
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 666e9246e70e..3e8262f583a7 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -538,6 +538,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 96abf451fdac..f54622904733 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -355,7 +355,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] 15+ messages in thread* [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
2026-06-03 6:00 [PATCH net-next v9 0/6] net: airoha: Support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (4 preceding siblings ...)
2026-06-03 6:00 ` [PATCH net-next v9 5/6] net: airoha: Introduce WAN device flag Lorenzo Bianconi
@ 2026-06-03 6:00 ` Lorenzo Bianconi
2026-06-04 6:01 ` sashiko-bot
2026-06-04 9:20 ` Lorenzo Bianconi
5 siblings, 2 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-03 6:00 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. These registers are used during FE hw acceleration to
determine whether received traffic is destined to this host (L3 traffic)
or should be switched to another device (L2 traffic).
The SoC hardware design assumes all interfaces configured as LAN (or WAN)
share the MAC address MSBs, which are programmed into the
REG_FE_{LAN,WAN}_MAC_H register. The LSBs of 'local' mac addresses can be
expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX
registers. In order to properly accelerate the traffic, FE module requires
the user to configure the REG_FE_{LAN,WAN}_MAC_H register respecting this
limitation. Please note a misconfiguration in REG_FE_{LAN,WAN}_MAC_H
will still allow the user to log into the device for debugging.
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. Since this functionality was not available
before this series, no regression is introduced.
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 77 ++++++++++++++++++++++++++++----
drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 4 +-
3 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 64ee526da241..8f2608293bb7 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -71,20 +71,76 @@ 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)
{
+ u8 ref_addr[ETH_ALEN] __aligned(2);
struct airoha_eth *eth = dev->eth;
- u32 val, reg;
+ u32 reg, val, lmin, lmax;
+ int i;
+
+ eth_zero_addr(ref_addr);
+ 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 = netdev_from_priv(iter_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 MSBs must be
+ * the same for each net_device with the same LAN/WAN
+ * configuration.
+ */
+ struct net_device *netdev = netdev_from_priv(dev);
+
+ dev_warn(eth->dev,
+ "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
+ netdev->name, ref_addr[0], ref_addr[1],
+ ref_addr[2]);
+ dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
+
+ 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,
@@ -1826,13 +1882,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;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 3e8262f583a7..8f42973f9cf5 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 f54622904733..91bcc55a6ac6 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1487,12 +1487,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 net_device *netdev = netdev_from_priv(dev);
struct airoha_gdm_port *port = dev->port;
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] 15+ messages in thread* Re: [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
2026-06-03 6:00 ` [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
@ 2026-06-04 6:01 ` sashiko-bot
2026-06-04 9:03 ` Lorenzo Bianconi
2026-06-04 9:20 ` Lorenzo Bianconi
1 sibling, 1 reply; 15+ messages in thread
From: sashiko-bot @ 2026-06-04 6:01 UTC (permalink / raw)
To: Lorenzo Bianconi; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The MAC address MSB validation introduces a configuration deadlock, permanently preventing MAC address MSB updates when multiple interfaces are registered.
- [High] Unconditional overwrite of `ref_addr` in the interface evaluation loop breaks MSB validation and corrupts hardware L2 bounds.
--
commit ca5c4a90447cc1f9bdc3a2831da1cfe304652070
Author: Lorenzo Bianconi <lorenzo@kernel.org>
net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN
MAC addresses. This commit extends the logic to derive the correct minimum
and maximum values for MAC LSB bounds when multiple interfaces are configured.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 64ee526da2414..8f2608293bb72 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -71,20 +71,76 @@ 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)
> +static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
> {
> + u8 ref_addr[ETH_ALEN] __aligned(2);
> struct airoha_eth *eth = dev->eth;
> - u32 val, reg;
> + u32 reg, val, lmin, lmax;
> + int i;
> +
> + eth_zero_addr(ref_addr);
> + 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;
[ ... ]
> + 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;
> + }
> + }
[Severity: High]
Does this loop unconditionally overwrite ref_addr with the last evaluated
interface? If there are multiple interfaces with mismatched MSBs (e.g., from
random boot MACs), it seems ref_addr will only retain the MSB of the final
interface.
Could this cause the validation check below to incorrectly pass if the new
MAC matches the last interface's MSB but not the earlier ones, while
erroneously accumulating LSBs from mismatched interfaces into lmin and
lmax?
> +
> + if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
[Severity: High]
Can this validation check create a configuration deadlock?
Since the driver requires all LAN or WAN interfaces to share the same
MAC address 3-byte MSB, and userspace APIs configure interfaces sequentially
via ndo_set_mac_address, changing the first interface's MSB will always fail
this check against the remaining interfaces.
How can a user successfully configure a unified custom MAC MSB across all
devices if the interfaces are assigned random MAC addresses at boot with
different MSBs?
> + /* According to the HW design, hw mac address MSBs must be
> + * the same for each net_device with the same LAN/WAN
> + * configuration.
> + */
> + struct net_device *netdev = netdev_from_priv(dev);
> +
> + dev_warn(eth->dev,
> + "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
> + netdev->name, ref_addr[0], ref_addr[1],
> + ref_addr[2]);
> + dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
> +
> + return -EINVAL;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org?part=6
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
2026-06-04 6:01 ` sashiko-bot
@ 2026-06-04 9:03 ` Lorenzo Bianconi
0 siblings, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-04 9:03 UTC (permalink / raw)
To: sashiko-reviews; +Cc: devicetree, robh, conor+dt, netdev
[-- Attachment #1: Type: text/plain, Size: 5092 bytes --]
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] The MAC address MSB validation introduces a configuration deadlock, permanently preventing MAC address MSB updates when multiple interfaces are registered.
> - [High] Unconditional overwrite of `ref_addr` in the interface evaluation loop breaks MSB validation and corrupts hardware L2 bounds.
> --
>
> commit ca5c4a90447cc1f9bdc3a2831da1cfe304652070
> Author: Lorenzo Bianconi <lorenzo@kernel.org>
>
> net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
>
> The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN
> MAC addresses. This commit extends the logic to derive the correct minimum
> and maximum values for MAC LSB bounds when multiple interfaces are configured.
>
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 64ee526da2414..8f2608293bb72 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -71,20 +71,76 @@ 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)
> > +static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
> > {
> > + u8 ref_addr[ETH_ALEN] __aligned(2);
> > struct airoha_eth *eth = dev->eth;
> > - u32 val, reg;
> > + u32 reg, val, lmin, lmax;
> > + int i;
> > +
> > + eth_zero_addr(ref_addr);
> > + 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;
> [ ... ]
> > + 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;
> > + }
> > + }
>
> [Severity: High]
> Does this loop unconditionally overwrite ref_addr with the last evaluated
> interface? If there are multiple interfaces with mismatched MSBs (e.g., from
> random boot MACs), it seems ref_addr will only retain the MSB of the final
> interface.
>
> Could this cause the validation check below to incorrectly pass if the new
> MAC matches the last interface's MSB but not the earlier ones, while
> erroneously accumulating LSBs from mismatched interfaces into lmin and
> lmax?
As pointed out before the hw design requires all the interfaces configured as
LAN (or WAN) to share the mac address MSBs since we have a single register to
configure it (we can't configure a range like for lmin or lamx). A
misconfiguration will just affect hw acceleration and the user will be able to
log and fix the configuration (according to the reported log) if the provided
mac addresses do not follow this hw limitation.
The choice of the reference interface is arbitrary and if multiple interfaces
have 'wrong' mac address based on the hw requirements, the user should log and
fix them one by one.
Please note this limitation is only valid if multiple net_devices are
configured as LAN (or WAN). Since in the current codebase we do not support
multiple interfaces configured as LAN or WAN, we are not introducing any
regression.
>
> > +
> > + if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
>
> [Severity: High]
> Can this validation check create a configuration deadlock?
>
> Since the driver requires all LAN or WAN interfaces to share the same
> MAC address 3-byte MSB, and userspace APIs configure interfaces sequentially
> via ndo_set_mac_address, changing the first interface's MSB will always fail
> this check against the remaining interfaces.
>
> How can a user successfully configure a unified custom MAC MSB across all
> devices if the interfaces are assigned random MAC addresses at boot with
> different MSBs?
As pointed out above, the user is supposed to configure the mac addresses based
on the hw design.
Regards,
Lorenzo
>
> > + /* According to the HW design, hw mac address MSBs must be
> > + * the same for each net_device with the same LAN/WAN
> > + * configuration.
> > + */
> > + struct net_device *netdev = netdev_from_priv(dev);
> > +
> > + dev_warn(eth->dev,
> > + "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
> > + netdev->name, ref_addr[0], ref_addr[1],
> > + ref_addr[2]);
> > + dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
> > +
> > + return -EINVAL;
> > + }
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org?part=6
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
2026-06-03 6:00 ` [PATCH net-next v9 6/6] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration Lorenzo Bianconi
2026-06-04 6:01 ` sashiko-bot
@ 2026-06-04 9:20 ` Lorenzo Bianconi
1 sibling, 0 replies; 15+ messages in thread
From: Lorenzo Bianconi @ 2026-06-04 9:20 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Madhur Agrawal
[-- Attachment #1: Type: text/plain, Size: 7479 bytes --]
On Jun 03, Lorenzo Bianconi wrote:
> The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN
> MAC addresses. These registers are used during FE hw acceleration to
> determine whether received traffic is destined to this host (L3 traffic)
> or should be switched to another device (L2 traffic).
> The SoC hardware design assumes all interfaces configured as LAN (or WAN)
> share the MAC address MSBs, which are programmed into the
> REG_FE_{LAN,WAN}_MAC_H register. The LSBs of 'local' mac addresses can be
> expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX
> registers. In order to properly accelerate the traffic, FE module requires
> the user to configure the REG_FE_{LAN,WAN}_MAC_H register respecting this
> limitation. Please note a misconfiguration in REG_FE_{LAN,WAN}_MAC_H
> will still allow the user to log into the device for debugging.
> 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. Since this functionality was not available
> before this series, no regression is introduced.
Commenting on sashiko's report:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426%40kernel.org
- How does this validation interact with the init path?
In airoha_alloc_gdm_device(), interfaces lacking a DT MAC fall back to
eth_hw_addr_random(), and airoha_dev_init() calls airoha_set_macaddr()
but discards the int return, so a netdev whose initial MAC would fail
the new MSB check still proceeds to register and reaches NETREG_REGISTERED.
- This is done on purpose to not block the device probe and allow the user
to log into the system, and based on the syslog, fix the issue manually.
Regards,
Lorenzo
>
> Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 77 ++++++++++++++++++++++++++++----
> drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
> drivers/net/ethernet/airoha/airoha_ppe.c | 4 +-
> 3 files changed, 71 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 64ee526da241..8f2608293bb7 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -71,20 +71,76 @@ 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)
> {
> + u8 ref_addr[ETH_ALEN] __aligned(2);
> struct airoha_eth *eth = dev->eth;
> - u32 val, reg;
> + u32 reg, val, lmin, lmax;
> + int i;
> +
> + eth_zero_addr(ref_addr);
> + 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 = netdev_from_priv(iter_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 MSBs must be
> + * the same for each net_device with the same LAN/WAN
> + * configuration.
> + */
> + struct net_device *netdev = netdev_from_priv(dev);
> +
> + dev_warn(eth->dev,
> + "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
> + netdev->name, ref_addr[0], ref_addr[1],
> + ref_addr[2]);
> + dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
> +
> + 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,
> @@ -1826,13 +1882,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;
> }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index 3e8262f583a7..8f42973f9cf5 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 f54622904733..91bcc55a6ac6 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -1487,12 +1487,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 net_device *netdev = netdev_from_priv(dev);
> struct airoha_gdm_port *port = dev->port;
> 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
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread