airoha: replace multi serdes patch with upstream kernel version

Now that multi serdes patch are accepted upstream, reorder the current
patch in the 1xx numbering and add the kernel version tag.

Also replace the downstream patch with the exact version merged upstream.

Downstream PCS implementation is updated with a new revision for the
ethernet code.

Update any patch affected by these changes.

Link: https://github.com/openwrt/openwrt/pull/23849
(cherry picked from commit 3ed5f08733)
[ fix conflict error ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
Christian Marangi committed 2026-07-30 15:41:43 +02:00
1 parent 9e612796cd
commit 7865e4bfae
34 files changed
+1205 -2387

No files matched your search

@@ -1,8 +1,7 @@
From e15783f7c987e199ecf80b3d858ed5a86d33c508 Mon Sep 17 00:00:00 2001
Message-ID: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From ec6c391bcca748bca041d5db92ff3bc4c99b3572 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 1 Nov 2025 11:25:52 +0100
Subject: [PATCH 01/13] net: airoha: Introduce airoha_gdm_dev struct
Date: Wed, 27 May 2026 12:21:15 +0200
Subject: [PATCH 1/6] net: airoha: Introduce airoha_gdm_dev struct
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.
@@ -17,6 +16,8 @@ connected to the same GDM port.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260527-airoha-eth-multi-serdes-preliminary-v1-1-ec6ed73ef7fc@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 312 ++++++++++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
@@ -25,18 +26,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -483,8 +483,9 @@ static bool airoha_fe_lro_is_enabled(str
LRO_RXQ_EN_MASK);
}
-static void airoha_dev_lro_enable(struct airoha_gdm_port *port)
+static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
{
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
@@ -843,6 +844,7 @@ static int airoha_qdma_rx_process(struct
@@ -600,6 +600,7 @@ static int airoha_qdma_rx_process(struct
struct airoha_qdma_desc *desc = &q->desc[q->tail];
u32 hash, reason, msg1, desc_ctrl;
struct airoha_gdm_port *port;
@@ -44,21 +34,29 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int data_len, len, p;
struct page *page;
@@ -869,8 +871,12 @@ static int airoha_qdma_rx_process(struct
@@ -626,6 +627,10 @@ static int airoha_qdma_rx_process(struct
goto free_frag;
port = eth->ports[p];
+ if (!port->dev)
+ goto free_frag;
+
+ netdev = netdev_from_priv(port->dev);
+ netdev = port->dev->dev;
if (!q->skb) { /* first buffer */
- q->skb = airoha_qdma_build_rx_skb(q, desc, e, port->dev);
+ q->skb = airoha_qdma_build_rx_skb(q, desc, e, netdev);
if (!q->skb)
goto free_frag;
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -635,8 +640,8 @@ static int airoha_qdma_rx_process(struct
skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
- q->skb->dev = port->dev;
- q->skb->protocol = eth_type_trans(q->skb, port->dev);
+ q->skb->dev = netdev;
+ q->skb->protocol = eth_type_trans(q->skb, netdev);
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
} else { /* scattered frame */
@@ -888,7 +894,7 @@ static int airoha_qdma_rx_process(struct
@@ -654,7 +659,7 @@ static int airoha_qdma_rx_process(struct
if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
continue;
@@ -67,55 +65,33 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
/* 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
@@ -1091,6 +1097,7 @@ static void airoha_qdma_wake_netdev_txqs
@@ -848,6 +853,7 @@ static void airoha_qdma_wake_netdev_txqs
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct net_device *netdev;
+ struct airoha_gdm_dev *dev;
int j;
if (!port)
@@ -1099,11 +1106,12 @@ static void airoha_qdma_wake_netdev_txqs
@@ -856,11 +862,12 @@ static void airoha_qdma_wake_netdev_txqs
if (port->qdma != qdma)
continue;
- for (j = 0; j < port->dev->num_tx_queues; j++) {
+ netdev = netdev_from_priv(port->dev);
+ for (j = 0; j < netdev->num_tx_queues; j++) {
+ dev = port->dev;
+ for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
- netif_wake_subqueue(port->dev, j);
+ netif_wake_subqueue(netdev, j);
+ netif_wake_subqueue(dev->dev, j);
}
}
q->txq_stopped = false;
@@ -1947,9 +1955,10 @@ static void airoha_update_hw_stats(struc
@@ -1700,19 +1707,20 @@ static void airoha_update_hw_stats(struc
spin_unlock(&port->stats.lock);
}
-static void airoha_update_netdev_features(struct airoha_gdm_port *port)
+static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
int i;
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
@@ -1959,7 +1968,7 @@ static void airoha_update_netdev_feature
if (!p)
continue;
- netdev = p->dev;
+ netdev = netdev_from_priv(p->dev);
if (netdev->reg_state != NETREG_REGISTERED)
continue;
@@ -1967,10 +1976,11 @@ static void airoha_update_netdev_feature
}
}
-static int airoha_dev_open(struct net_device *dev)
+static int airoha_dev_open(struct net_device *netdev)
{
@@ -125,35 +101,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
@@ -1981,31 +1991,32 @@ static int airoha_dev_open(struct net_de
* the same QDMA if LRO is enabled on any device sharing it.
*/
if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
- netdev_warn(dev, "required to disable HW GRO on QDMA%d\n",
+ netdev_warn(netdev, "required to disable HW GRO on QDMA%d\n",
qdma_id);
return -EBUSY;
}
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
- err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
+ err = phylink_of_phy_connect(dev->phylink,
+ netdev->dev.of_node, 0);
if (err) {
- netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
- err);
+ netdev_err(netdev, "%s: could not attach PHY: %d\n",
+ __func__, err);
return err;
}
- phylink_start(port->phylink);
+ phylink_start(dev->phylink);
}
#endif
u32 pse_port = FE_PSE_PORT_PPE1;
- netif_tx_start_all_queues(dev);
+ netif_tx_start_all_queues(netdev);
@@ -161,24 +109,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (err)
return err;
/* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
- if (netdev_uses_dsa(dev) || port->id > 2)
+ if (netdev_uses_dsa(netdev) || port->id > 2)
airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id),
- if (netdev_uses_dsa(dev))
+ if (netdev_uses_dsa(netdev))
airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
else
@@ -2029,25 +2040,26 @@ static int airoha_dev_open(struct net_de
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
pse_port);
- if (dev->features & NETIF_F_GRO_HW)
- airoha_dev_lro_enable(port);
+ if (netdev->features & NETIF_F_GRO_HW)
+ airoha_dev_lro_enable(dev);
- airoha_update_netdev_features(port);
+ airoha_update_netdev_features(dev);
@@ -1740,16 +1748,17 @@ static int airoha_dev_open(struct net_de
return 0;
}
@@ -189,7 +125,6 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
int i;
- netif_tx_disable(dev);
@@ -200,24 +135,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ for (i = 0; i < netdev->num_tx_queues; i++)
+ netdev_tx_reset_subqueue(netdev, i);
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
@@ -2065,28 +2077,29 @@ static int airoha_dev_stop(struct net_de
}
}
- airoha_update_netdev_features(port);
+ airoha_update_netdev_features(dev);
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
- phylink_stop(port->phylink);
- phylink_disconnect_phy(port->phylink);
+ phylink_stop(dev->phylink);
+ phylink_disconnect_phy(dev->phylink);
}
#endif
@@ -1770,16 +1779,17 @@ static int airoha_dev_stop(struct net_de
return 0;
}
@@ -239,7 +159,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
return 0;
}
@@ -2152,16 +2165,17 @@ static int airoha_set_gdm2_loopback(stru
@@ -1845,16 +1855,17 @@ static int airoha_set_gdm2_loopback(stru
return 0;
}
@@ -257,12 +177,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
- port->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, dev->dev_addr);
+ netdev->irq = port->qdma->irq_banks[0].irq;
+ dev->dev->irq = port->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(port, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -2186,10 +2200,11 @@ static int airoha_dev_init(struct net_de
@@ -1879,10 +1890,11 @@ static int airoha_dev_init(struct net_de
return 0;
}
@@ -276,7 +196,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
unsigned int start;
airoha_update_hw_stats(port);
@@ -2208,36 +2223,39 @@ static void airoha_dev_get_stats64(struc
@@ -1901,36 +1913,39 @@ static void airoha_dev_get_stats64(struc
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
@@ -323,46 +243,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
@@ -2300,10 +2318,11 @@ int airoha_get_fe_port(struct airoha_gdm
}
}
-static netdev_features_t airoha_dev_fix_features(struct net_device *dev,
+static netdev_features_t airoha_dev_fix_features(struct net_device *netdev,
netdev_features_t features)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
if (qdma->users > 1)
@@ -2312,11 +2331,12 @@ static netdev_features_t airoha_dev_fix_
return features;
}
-static int airoha_dev_set_features(struct net_device *dev,
+static int airoha_dev_set_features(struct net_device *netdev,
netdev_features_t features)
{
- netdev_features_t diff = dev->features ^ features;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ netdev_features_t diff = netdev->features ^ features;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
@@ -2325,7 +2345,7 @@ static int airoha_dev_set_features(struc
return 0;
if (features & NETIF_F_GRO_HW)
- airoha_dev_lro_enable(port);
+ airoha_dev_lro_enable(dev);
else
airoha_fe_lro_disable(eth, qdma_id);
@@ -2333,9 +2353,10 @@ static int airoha_dev_set_features(struc
@@ -1994,9 +2009,10 @@ int airoha_get_fe_port(struct airoha_gdm
}
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
@@ -375,7 +256,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_qdma *qdma = port->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
@@ -2348,7 +2369,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2009,7 +2025,7 @@ static netdev_tx_t airoha_dev_xmit(struc
u8 fport;
qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
@@ -384,7 +265,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
qid / AIROHA_NUM_QOS_QUEUES) |
@@ -2384,7 +2405,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2045,7 +2061,7 @@ static netdev_tx_t airoha_dev_xmit(struc
spin_lock_bh(&q->lock);
@@ -393,7 +274,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
if (q->queued + nr_frags >= q->ndesc) {
@@ -2408,9 +2429,9 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2069,9 +2085,9 @@ static netdev_tx_t airoha_dev_xmit(struc
dma_addr_t addr;
u32 val;
@@ -405,7 +286,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
goto error_unmap;
list_move_tail(&e->list, &tx_list);
@@ -2459,7 +2480,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2120,7 +2136,7 @@ static netdev_tx_t airoha_dev_xmit(struc
error_unmap:
list_for_each_entry(e, &tx_list, list) {
@@ -414,7 +295,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
DMA_TO_DEVICE);
e->dma_addr = 0;
}
@@ -2468,25 +2489,27 @@ error_unmap:
@@ -2129,25 +2145,27 @@ error_unmap:
spin_unlock_bh(&q->lock);
error:
dev_kfree_skb_any(skb);
@@ -447,7 +328,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
unsigned int start;
airoha_update_hw_stats(port);
@@ -2514,11 +2537,12 @@ static const struct ethtool_rmon_hist_ra
@@ -2175,11 +2193,12 @@ static const struct ethtool_rmon_hist_ra
};
static void
@@ -462,7 +343,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_hw_stats *hw_stats = &port->stats;
unsigned int start;
@@ -2543,11 +2567,12 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2204,11 +2223,12 @@ airoha_ethtool_get_rmon_stats(struct net
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
@@ -477,7 +358,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int i;
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
@@ -2632,10 +2657,12 @@ static int airoha_qdma_set_tx_ets_sched(
@@ -2293,10 +2313,12 @@ static int airoha_qdma_set_tx_ets_sched(
ARRAY_SIZE(w));
}
@@ -492,7 +373,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
REG_CNTR_VAL(channel << 1));
u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
@@ -2897,11 +2924,12 @@ static int airoha_qdma_set_trtcm_token_b
@@ -2558,11 +2580,12 @@ static int airoha_qdma_set_trtcm_token_b
mode, val);
}
@@ -507,7 +388,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int i, err;
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
@@ -2921,20 +2949,22 @@ static int airoha_qdma_set_tx_rate_limit
@@ -2582,20 +2605,22 @@ static int airoha_qdma_set_tx_rate_limit
return 0;
}
@@ -534,7 +415,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (err) {
NL_SET_ERR_MSG_MOD(opt->extack,
"failed configuring htb offload");
@@ -2944,9 +2974,10 @@ static int airoha_tc_htb_alloc_leaf_queu
@@ -2605,9 +2630,10 @@ static int airoha_tc_htb_alloc_leaf_queu
if (opt->command == TC_HTB_NODE_MODIFY)
return 0;
@@ -547,7 +428,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
return err;
@@ -3036,11 +3067,12 @@ static int airoha_tc_matchall_act_valida
@@ -2697,11 +2723,12 @@ static int airoha_tc_matchall_act_valida
return 0;
}
@@ -562,7 +443,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -3075,18 +3107,19 @@ static int airoha_dev_tc_matchall(struct
@@ -2736,18 +2763,19 @@ static int airoha_dev_tc_matchall(struct
static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
@@ -586,7 +467,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
default:
return -EOPNOTSUPP;
}
@@ -3133,47 +3166,51 @@ static int airoha_dev_setup_tc_block(str
@@ -2794,47 +2822,51 @@ static int airoha_dev_setup_tc_block(str
}
}
@@ -650,7 +531,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (!test_bit(channel, port->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
@@ -3209,8 +3246,8 @@ static int airoha_tc_setup_qdisc_htb(str
@@ -2870,8 +2902,8 @@ static int airoha_tc_setup_qdisc_htb(str
return 0;
}
@@ -661,7 +542,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
{
switch (type) {
case TC_SETUP_QDISC_ETS:
@@ -3283,13 +3320,18 @@ static void airoha_metadata_dst_free(str
@@ -2937,25 +2969,81 @@ static void airoha_metadata_dst_free(str
}
}
@@ -683,96 +564,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
return true;
}
@@ -3301,8 +3343,9 @@ static void airoha_mac_link_up(struct ph
unsigned int mode, phy_interface_t interface,
int speed, int duplex, bool tx_pause, bool rx_pause)
{
- struct airoha_gdm_port *port = container_of(config, struct airoha_gdm_port,
- phylink_config);
+ struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
+ phylink_config);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
u32 frag_size_tx, frag_size_rx;
@@ -3358,65 +3401,121 @@ static int airoha_fill_available_pcs(str
&num_available_pcs);
return false;
}
-static int airoha_setup_phylink(struct net_device *dev)
+static int airoha_setup_phylink(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
- struct device_node *np = dev->dev.of_node;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct device_node *np = netdev->dev.of_node;
phy_interface_t phy_mode;
struct phylink *phylink;
int err;
err = of_get_phy_mode(np, &phy_mode);
if (err) {
- dev_err(&dev->dev, "incorrect phy-mode\n");
+ dev_err(&netdev->dev, "incorrect phy-mode\n");
return err;
}
- port->phylink_config.dev = &dev->dev;
- port->phylink_config.type = PHYLINK_NETDEV;
- port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
- MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD |
- MAC_5000FD | MAC_10000FD;
+ dev->phylink_config.dev = &netdev->dev;
+ dev->phylink_config.type = PHYLINK_NETDEV;
+ dev->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD |
+ MAC_5000FD | MAC_10000FD;
- err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL,
- &port->phylink_config.num_available_pcs);
+ err = fwnode_phylink_pcs_parse(dev_fwnode(&netdev->dev), NULL,
+ &dev->phylink_config.num_available_pcs);
if (err)
return err;
- port->phylink_config.fill_available_pcs = airoha_fill_available_pcs;
+ dev->phylink_config.fill_available_pcs = airoha_fill_available_pcs;
__set_bit(PHY_INTERFACE_MODE_SGMII,
- port->phylink_config.supported_interfaces);
+ dev->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
- port->phylink_config.supported_interfaces);
+ dev->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
- port->phylink_config.supported_interfaces);
+ dev->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER,
- port->phylink_config.supported_interfaces);
+ dev->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_USXGMII,
- port->phylink_config.supported_interfaces);
+ dev->phylink_config.supported_interfaces);
- phy_interface_copy(port->phylink_config.pcs_interfaces,
- port->phylink_config.supported_interfaces);
+ phy_interface_copy(dev->phylink_config.pcs_interfaces,
+ dev->phylink_config.supported_interfaces);
- phylink = phylink_create(&port->phylink_config,
+ phylink = phylink_create(&dev->phylink_config,
of_fwnode_handle(np),
phy_mode, &airoha_phylink_ops);
if (IS_ERR(phylink))
return PTR_ERR(phylink);
- port->phylink = phylink;
+ dev->phylink = phylink;
return err;
}
#endif
+static int airoha_alloc_gdm_device(struct airoha_eth *eth,
+ struct airoha_gdm_port *port,
+ struct device_node *np)
@@ -793,9 +587,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ netdev->ethtool_ops = &airoha_ethtool_ops;
+ netdev->max_mtu = AIROHA_MAX_MTU;
+ netdev->watchdog_timeo = 5 * HZ;
+ netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
+ netdev->features |= AIROHA_HW_FEATURES;
+ netdev->vlan_features = AIROHA_HW_FEATURES;
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 |
+ NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
+ NETIF_F_HW_TC;
+ netdev->features |= netdev->hw_features;
+ netdev->vlan_features = netdev->hw_features;
+ netdev->dev.of_node = np;
+ SET_NETDEV_DEV(netdev, eth->dev);
+
@@ -815,18 +611,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ }
+
+ dev = netdev_priv(netdev);
+ dev->dev = netdev;
+ dev->port = port;
+ port->dev = dev;
+ dev->eth = eth;
+
+#if defined(CONFIG_PCS_AIROHA)
+ if (airhoa_is_phy_external(port)) {
+ err = airoha_setup_phylink(netdev);
+ if (err)
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
@@ -839,7 +628,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int err, p;
u32 id;
@@ -3438,58 +3537,22 @@ static int airoha_alloc_gdm_port(struct
@@ -2977,53 +3065,22 @@ static int airoha_alloc_gdm_port(struct
return -EINVAL;
}
@@ -857,17 +646,20 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- dev->ethtool_ops = &airoha_ethtool_ops;
- dev->max_mtu = AIROHA_MAX_MTU;
- dev->watchdog_timeo = 5 * HZ;
- dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
- dev->features |= AIROHA_HW_FEATURES;
- dev->vlan_features = AIROHA_HW_FEATURES;
- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
- NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
- NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HW_TC;
- dev->features |= dev->hw_features;
- dev->vlan_features = dev->hw_features;
- dev->dev.of_node = np;
- SET_NETDEV_DEV(dev, eth->dev);
-
- /* reserve hw queues for HTB offloading */
- err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
- if (err)
- return err;
-
- err = of_get_ethdev_address(np, dev);
- if (err) {
- if (err == -EPROBE_DEFER)
@@ -888,32 +680,25 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
-#if defined(CONFIG_PCS_AIROHA)
- if (airhoa_is_phy_external(port)) {
- err = airoha_setup_phylink(dev);
- if (err)
- return err;
- }
-#endif
- return airoha_metadata_dst_alloc(port);
+ err = airoha_metadata_dst_alloc(port);
+ if (err)
+ return err;
- return airoha_metadata_dst_alloc(port);
+
+ return airoha_alloc_gdm_device(eth, port, np);
}
static int airoha_register_gdm_devices(struct airoha_eth *eth)
@@ -3503,7 +3566,7 @@ static int airoha_register_gdm_devices(s
@@ -3037,7 +3094,7 @@ static int airoha_register_gdm_devices(s
if (!port)
continue;
- err = register_netdev(port->dev);
+ err = register_netdev(netdev_from_priv(port->dev));
+ err = register_netdev(port->dev->dev);
if (err)
return err;
}
@@ -3612,16 +3675,22 @@ error_napi_stop:
@@ -3146,12 +3203,14 @@ error_napi_stop:
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -922,25 +707,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (!port)
continue;
- if (port->dev->reg_state == NETREG_REGISTERED) {
+ dev = port->dev;
+ if (dev) {
+ struct net_device *netdev = netdev_from_priv(dev);
+
+ if (netdev->reg_state == NETREG_REGISTERED) {
#if defined(CONFIG_PCS_AIROHA)
- if (airhoa_is_phy_external(port))
- phylink_destroy(port->phylink);
+ if (airhoa_is_phy_external(port))
+ phylink_destroy(dev->phylink);
#endif
- if (port->dev->reg_state == NETREG_REGISTERED)
- unregister_netdev(port->dev);
+ unregister_netdev(netdev);
+ }
}
+ dev = port->dev;
+ if (dev && dev->dev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(dev->dev);
airoha_metadata_dst_free(port);
}
@@ -3643,15 +3712,19 @@ static void airoha_remove(struct platfor
airoha_hw_cleanup(eth);
@@ -3172,11 +3231,14 @@ static void airoha_remove(struct platfor
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -949,49 +724,34 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (!port)
continue;
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port))
- phylink_destroy(port->phylink);
+ phylink_destroy(dev->phylink);
#endif
- unregister_netdev(port->dev);
+
+ dev = port->dev;
+ if (dev)
+ unregister_netdev(netdev_from_priv(dev));
+ unregister_netdev(dev->dev);
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -547,17 +547,21 @@ struct airoha_qdma {
@@ -535,10 +535,15 @@ struct airoha_qdma {
struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
};
-struct airoha_gdm_port {
- struct airoha_qdma *qdma;
+struct airoha_gdm_dev {
+ struct airoha_gdm_port *port;
struct airoha_eth *eth;
- struct net_device *dev;
- int id;
- int nbq;
#if defined(CONFIG_PCS_AIROHA)
struct phylink *phylink;
struct phylink_config phylink_config;
#endif
+ struct net_device *dev;
+ struct airoha_eth *eth;
+};
+
+struct airoha_gdm_port {
+ struct airoha_qdma *qdma;
struct airoha_gdm_port {
struct airoha_qdma *qdma;
- struct airoha_eth *eth;
- struct net_device *dev;
+ struct airoha_gdm_dev *dev;
+ int id;
+ int nbq;
int id;
int nbq;
struct airoha_hw_stats stats;
@@ -691,8 +695,8 @@ static inline bool airoha_qdma_is_lro_qu
@@ -662,8 +667,8 @@ static inline bool airoha_is_7583(struct
}
int airoha_get_fe_port(struct airoha_gdm_port *port);
@@ -1004,14 +764,14 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u8 fport);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -297,12 +297,12 @@ static void airoha_ppe_foe_set_bridge_ad
@@ -298,12 +298,12 @@ static void airoha_ppe_foe_set_bridge_ad
static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
struct airoha_foe_entry *hwe,
- struct net_device *dev, int type,
+ struct net_device *netdev, int type,
struct airoha_flow_data *data,
int l4proto, u8 dsfield)
int l4proto)
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
- int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev);
@@ -1019,7 +779,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_foe_mac_info_common *l2;
u8 smac_id = 0xf;
@@ -318,10 +318,11 @@ static int airoha_ppe_foe_entry_prepare(
@@ -319,10 +319,11 @@ static int airoha_ppe_foe_entry_prepare(
hwe->ib1 = val;
val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f);
@@ -1033,14 +793,14 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) |
FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT,
FE_PSE_PORT_CDM4);
@@ -331,12 +332,14 @@ static int airoha_ppe_foe_entry_prepare(
@@ -332,12 +333,14 @@ static int airoha_ppe_foe_entry_prepare(
FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID,
info.wcid);
} else {
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
u8 pse_port, channel, priority;
+ struct airoha_gdm_port *port;
u8 pse_port, channel;
- if (!airoha_is_valid_gdm_port(eth, port))
+ if (!airoha_is_valid_gdm_dev(eth, dev))
@@ -1050,33 +810,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (dsa_port >= 0 || eth->ports[1])
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
: port->id;
@@ -1484,8 +1487,8 @@ void airoha_ppe_check_skb(struct airoha_
@@ -1473,7 +1476,7 @@ void airoha_ppe_check_skb(struct airoha_
void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
{
+ struct net_device *dev = netdev_from_priv(port->dev);
struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev;
+ struct net_device *dev = port->dev->dev;
const u8 *addr = dev->dev_addr;
u32 val;
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2760,6 +2760,17 @@ static inline void *netdev_priv(const st
return (void *)dev->priv;
}
+/**
+ * netdev_from_priv() - get network device from priv
+ * @priv: network device private data
+ *
+ * Returns: net_device to which @priv belongs
+ */
+static inline struct net_device *netdev_from_priv(const void *priv)
+{
+ return container_of(priv, struct net_device, priv);
+}
+
/* Set the sysfs physical device reference for the network logical device
* if set prior to registration will cause a symlink during initialization.
*/
@@ -1,10 +1,7 @@
From f62cea6483cc55360863d66300790a5fb9de5f7c Mon Sep 17 00:00:00 2001
Message-ID: <f62cea6483cc55360863d66300790a5fb9de5f7c.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From 528c5153a557f69482d91609a2a002ded22a3441 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Tue, 24 Feb 2026 18:43:05 +0100
Subject: [PATCH 02/13] net: airoha: Move airoha_qdma pointer in airoha_gdm_dev
Date: Wed, 27 May 2026 12:21:16 +0200
Subject: [PATCH 2/6] net: airoha: Move airoha_qdma pointer in airoha_gdm_dev
struct
Move airoha_qdma pointer from airoha_gdm_port struct to airoha_gdm_dev
@@ -16,6 +13,8 @@ to the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260527-airoha-eth-multi-serdes-preliminary-v1-2-ec6ed73ef7fc@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 105 +++++++++++------------
drivers/net/ethernet/airoha/airoha_eth.h | 9 +-
@@ -24,9 +23,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -81,9 +81,10 @@ static bool airhoa_is_phy_external(struc
@@ -71,9 +71,10 @@ static void airoha_qdma_irq_disable(stru
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
#endif
-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
+static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
@@ -37,7 +36,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 val, reg;
reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
@@ -95,7 +96,7 @@ static void airoha_set_macaddr(struct ai
@@ -85,7 +86,7 @@ static void airoha_set_macaddr(struct ai
airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
@@ -46,7 +45,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -111,10 +112,10 @@ static void airoha_set_gdm_port_fwd_cfg(
@@ -101,10 +102,10 @@ static void airoha_set_gdm_port_fwd_cfg(
FIELD_PREP(GDM_UCFQ_MASK, val));
}
@@ -60,24 +59,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 vip_port;
vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
@@ -485,8 +486,7 @@ static bool airoha_fe_lro_is_enabled(str
static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
{
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
int i, lro_queue_index = 0;
@@ -1097,13 +1097,18 @@ static void airoha_qdma_wake_netdev_txqs
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;
@@ -859,10 +860,13 @@ static void airoha_qdma_wake_netdev_txqs
if (!port)
continue;
@@ -89,8 +71,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ if (dev->qdma != qdma)
continue;
netdev = netdev_from_priv(port->dev);
@@ -1811,9 +1816,10 @@ static void airoha_qdma_stop_napi(struct
- dev = port->dev;
for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
@@ -1563,9 +1567,10 @@ static void airoha_qdma_stop_napi(struct
}
}
@@ -103,17 +88,13 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 val, i = 0;
spin_lock(&port->stats.lock);
@@ -1981,7 +1987,7 @@ static int airoha_dev_open(struct net_de
@@ -1712,11 +1717,11 @@ static int airoha_dev_open(struct net_de
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
u32 pse_port = FE_PSE_PORT_PPE1;
@@ -2011,7 +2017,7 @@ static int airoha_dev_open(struct net_de
#endif
netif_tx_start_all_queues(netdev);
- err = airoha_set_vip_for_gdm_port(port, true);
@@ -121,13 +102,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (err)
return err;
@@ -2052,12 +2058,12 @@ static int airoha_dev_stop(struct net_de
@@ -1752,11 +1757,11 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
int i;
netif_tx_disable(netdev);
@@ -136,7 +116,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
@@ -2092,21 +2098,21 @@ static int airoha_dev_stop(struct net_de
@@ -1782,21 +1787,21 @@ static int airoha_dev_stop(struct net_de
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -162,7 +142,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 val, pse_port, chan;
int i, src_port;
@@ -2153,7 +2159,7 @@ static int airoha_set_gdm2_loopback(stru
@@ -1843,7 +1848,7 @@ static int airoha_set_gdm2_loopback(stru
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
for (i = 0; i < eth->soc->num_ppe; i++)
@@ -171,20 +151,20 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
@@ -2033,9 +2038,9 @@ static int airoha_dev_init(struct net_de
@@ -1863,9 +1868,9 @@ static int airoha_dev_init(struct net_de
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
- netdev->irq = port->qdma->irq_banks[0].irq;
- dev->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, netdev->dev_addr);
+ dev->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
+ netdev->irq = dev->qdma->irq_banks[0].irq;
+ dev->dev->irq = dev->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(dev, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -2184,7 +2190,7 @@ static int airoha_dev_init(struct net_de
@@ -1874,7 +1879,7 @@ static int airoha_dev_init(struct net_de
if (!eth->ports[1]) {
int err;
@@ -193,7 +173,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (err)
return err;
}
@@ -2194,8 +2200,7 @@ static int airoha_dev_init(struct net_de
@@ -1884,8 +1889,7 @@ static int airoha_dev_init(struct net_de
}
for (i = 0; i < eth->soc->num_ppe; i++)
@@ -203,7 +183,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
return 0;
}
@@ -2207,7 +2212,7 @@ static void airoha_dev_get_stats64(struc
@@ -1897,7 +1901,7 @@ static void airoha_dev_get_stats64(struc
struct airoha_gdm_port *port = dev->port;
unsigned int start;
@@ -212,7 +192,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
storage->rx_packets = port->stats.rx_ok_pkts;
@@ -2227,8 +2232,8 @@ static int airoha_dev_change_mtu(struct
@@ -1917,8 +1921,8 @@ static int airoha_dev_change_mtu(struct
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
@@ -222,7 +202,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK,
@@ -2302,10 +2307,10 @@ static u32 airoha_get_dsa_tag(struct sk_
@@ -1992,10 +1996,10 @@ static u32 airoha_get_dsa_tag(struct sk_
#endif
}
@@ -236,27 +216,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
switch (eth->soc->version) {
case 0x7583:
@@ -2322,8 +2327,7 @@ static netdev_features_t airoha_dev_fix_
netdev_features_t features)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
if (qdma->users > 1)
features &= ~NETIF_F_GRO_HW;
@@ -2336,8 +2340,7 @@ static int airoha_dev_set_features(struc
{
netdev_features_t diff = netdev->features ^ features;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
@@ -2356,8 +2359,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2012,8 +2016,7 @@ static netdev_tx_t airoha_dev_xmit(struc
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -266,7 +226,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
struct netdev_queue *txq;
@@ -2395,7 +2397,7 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2051,7 +2054,7 @@ static netdev_tx_t airoha_dev_xmit(struc
}
}
@@ -275,7 +235,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@@ -2498,8 +2500,7 @@ static void airoha_ethtool_get_drvinfo(s
@@ -2154,8 +2157,7 @@ static void airoha_ethtool_get_drvinfo(s
struct ethtool_drvinfo *info)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -285,7 +245,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
@@ -2512,7 +2513,7 @@ static void airoha_ethtool_get_mac_stats
@@ -2168,7 +2170,7 @@ static void airoha_ethtool_get_mac_stats
struct airoha_gdm_port *port = dev->port;
unsigned int start;
@@ -294,7 +254,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
@@ -2552,7 +2553,7 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2208,7 +2210,7 @@ airoha_ethtool_get_rmon_stats(struct net
ARRAY_SIZE(hw_stats->rx_len) + 1);
*ranges = airoha_ethtool_rmon_ranges;
@@ -303,7 +263,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
do {
int i;
@@ -2572,18 +2573,17 @@ static int airoha_qdma_set_chan_tx_sched
@@ -2228,18 +2230,17 @@ static int airoha_qdma_set_chan_tx_sched
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -324,7 +284,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
TWRR_RW_CMD_MASK |
FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2591,13 +2591,12 @@ static int airoha_qdma_set_chan_tx_sched
@@ -2247,13 +2248,12 @@ static int airoha_qdma_set_chan_tx_sched
err = read_poll_timeout(airoha_qdma_rr, status,
status & TWRR_RW_CMD_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC,
@@ -340,7 +300,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
CHAN_QOS_MODE_MASK(channel),
__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
@@ -2663,9 +2662,9 @@ static int airoha_qdma_get_tx_ets_stats(
@@ -2319,9 +2319,9 @@ static int airoha_qdma_get_tx_ets_stats(
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
@@ -352,7 +312,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
REG_CNTR_VAL((channel << 1) + 1));
u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
(fwd_tx_packets - port->fwd_tx_packets);
@@ -2929,17 +2928,16 @@ static int airoha_qdma_set_tx_rate_limit
@@ -2585,17 +2585,16 @@ static int airoha_qdma_set_tx_rate_limit
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -372,7 +332,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
@@ -2989,11 +2987,11 @@ static int airoha_tc_htb_alloc_leaf_queu
@@ -2645,11 +2644,11 @@ static int airoha_tc_htb_alloc_leaf_queu
return 0;
}
@@ -386,7 +346,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -3072,7 +3070,6 @@ static int airoha_dev_tc_matchall(struct
@@ -2728,7 +2727,6 @@ static int airoha_dev_tc_matchall(struct
{
enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -394,7 +354,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -3097,7 +3094,7 @@ static int airoha_dev_tc_matchall(struct
@@ -2753,7 +2751,7 @@ static int airoha_dev_tc_matchall(struct
fallthrough;
}
case TC_CLSMATCHALL_DESTROY:
@@ -403,7 +363,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
unit_type);
default:
return -EOPNOTSUPP;
@@ -3109,8 +3106,7 @@ static int airoha_dev_setup_tc_block_cb(
@@ -2765,8 +2763,7 @@ static int airoha_dev_setup_tc_block_cb(
{
struct net_device *netdev = cb_priv;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -413,26 +373,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (!tc_can_offload(netdev))
return -EOPNOTSUPP;
@@ -3346,7 +3342,7 @@ static void airoha_mac_link_up(struct ph
struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
phylink_config);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
u32 frag_size_tx, frag_size_rx;
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -549,6 +549,7 @@ struct airoha_qdma {
@@ -537,12 +537,12 @@ struct airoha_qdma {
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
+ struct airoha_qdma *qdma;
struct net_device *dev;
struct airoha_eth *eth;
#if defined(CONFIG_PCS_AIROHA)
@@ -558,7 +559,6 @@ struct airoha_gdm_dev {
};
struct airoha_gdm_port {
@@ -440,8 +389,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_gdm_dev *dev;
int id;
int nbq;
@@ -694,19 +694,18 @@ static inline bool airoha_qdma_is_lro_qu
return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
@@ -666,19 +666,18 @@ static inline bool airoha_is_7583(struct
return eth->soc->version == 0x7583;
}
-int airoha_get_fe_port(struct airoha_gdm_port *port);
@@ -465,7 +414,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 hash);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -85,9 +85,9 @@ static u32 airoha_ppe_get_timestamp(stru
@@ -84,9 +84,9 @@ static u32 airoha_ppe_get_timestamp(stru
AIROHA_FOE_IB1_BIND_TIMESTAMP);
}
@@ -477,7 +426,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_eth *eth = qdma->eth;
u8 qdma_id = qdma - &eth->qdma[0];
u32 fe_cpu_port;
@@ -181,8 +181,8 @@ static void airoha_ppe_hw_init(struct ai
@@ -180,8 +180,8 @@ static void airoha_ppe_hw_init(struct ai
if (!port)
continue;
@@ -488,18 +437,18 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
}
}
@@ -1483,11 +1483,12 @@ void airoha_ppe_check_skb(struct airoha_
@@ -1473,11 +1473,12 @@ void airoha_ppe_check_skb(struct airoha_
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
{
- struct net_device *dev = netdev_from_priv(port->dev);
- struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev->dev;
- const u8 *addr = dev->dev_addr;
+ 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;
@@ -1,10 +1,7 @@
From 32bfd008c19f9ad55514181d8cd02e14bf384475 Mon Sep 17 00:00:00 2001
Message-ID: <32bfd008c19f9ad55514181d8cd02e14bf384475.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From eca4f59b536780a56bd22db03fe5465e8e978f36 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 13 Dec 2025 10:06:45 +0100
Subject: [PATCH 03/13] net: airoha: Rely on airoha_gdm_dev pointer in
Date: Wed, 27 May 2026 12:21:17 +0200
Subject: [PATCH 3/6] net: airoha: Rely on airoha_gdm_dev pointer in
airoha_is_lan_gdm_port()
Rename airoha_is_lan_gdm_port in airoha_is_lan_gdm_dev. Moreover, rely
@@ -15,6 +12,8 @@ the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260527-airoha-eth-multi-serdes-preliminary-v1-3-ec6ed73ef7fc@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 6 ++----
drivers/net/ethernet/airoha/airoha_eth.h | 4 +++-
@@ -23,7 +22,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -83,12 +83,10 @@ static bool airhoa_is_phy_external(struc
@@ -73,12 +73,10 @@ static void airoha_qdma_irq_disable(stru
static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
@@ -37,18 +36,18 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
@@ -2179,7 +2177,7 @@ static int airoha_dev_init(struct net_de
@@ -1868,7 +1866,7 @@ static int airoha_dev_init(struct net_de
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
netdev->irq = dev->qdma->irq_banks[0].irq;
dev->dev->irq = dev->qdma->irq_banks[0].irq;
airoha_set_macaddr(dev, netdev->dev_addr);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -663,8 +663,10 @@ static inline u16 airoha_qdma_get_txq(st
@@ -647,8 +647,10 @@ static inline u16 airoha_qdma_get_txq(st
return qid % ARRAY_SIZE(qdma->q_tx);
}
@@ -62,7 +61,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
* phy module.
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -365,7 +365,7 @@ static int airoha_ppe_foe_entry_prepare(
@@ -362,7 +362,7 @@ static int airoha_ppe_foe_entry_prepare(
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
*/
@@ -1,10 +1,7 @@
From 634d75285db77f3385aa85a1bf2b185396225100 Mon Sep 17 00:00:00 2001
Message-ID: <634d75285db77f3385aa85a1bf2b185396225100.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From 069626af6dfaa26b82119900f4aff1fec38d5983 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 10 Apr 2026 14:35:32 +0200
Subject: [PATCH 04/13] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
Date: Wed, 27 May 2026 12:21:18 +0200
Subject: [PATCH 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, qos_sq_bmap field can be overwritten with the
@@ -14,7 +11,10 @@ airoha_gdm_port struct to airoha_gdm_dev one.
Add qos_channel_map bitmap in airoha_qdma struct to track if a shared
QDMA channel is already in use by another net_device.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260527-airoha-eth-multi-serdes-preliminary-v1-4-ec6ed73ef7fc@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 58 +++++++++++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 6 ++-
@@ -22,7 +22,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2945,30 +2945,46 @@ static int airoha_qdma_set_tx_rate_limit
@@ -2602,30 +2602,40 @@ static int airoha_qdma_set_tx_rate_limit
return 0;
}
@@ -36,7 +36,6 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- int err, num_tx_queues = netdev->real_num_tx_queues;
- struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ int err;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
@@ -46,13 +45,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
- opt->quantum);
- if (err) {
+ err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+ if (err)
NL_SET_ERR_MSG_MOD(opt->extack,
"failed configuring htb offload");
- return err;
+
+ return err;
+ return airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+}
+
+static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
@@ -67,7 +60,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ * in use by another net_device running on the same QDMA block.
+ */
+ if (test_and_set_bit(channel, qdma->qos_channel_map)) {
+ NL_SET_ERR_MSG_MOD(opt->extack,
NL_SET_ERR_MSG_MOD(opt->extack,
- "failed configuring htb offload");
- return err;
+ "qdma qos channel already in use");
+ return -EBUSY;
}
@@ -80,7 +75,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
if (err) {
@@ -2976,13 +2992,17 @@ static int airoha_tc_htb_alloc_leaf_queu
@@ -2633,13 +2643,17 @@ static int airoha_tc_htb_alloc_leaf_queu
opt->quantum);
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
@@ -100,7 +95,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
@@ -3163,11 +3183,13 @@ static int airoha_dev_setup_tc_block(str
@@ -2820,11 +2834,13 @@ static int airoha_dev_setup_tc_block(str
static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -116,7 +111,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
@@ -3175,9 +3197,8 @@ static int airoha_tc_htb_delete_leaf_que
@@ -2832,9 +2848,8 @@ static int airoha_tc_htb_delete_leaf_que
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -127,7 +122,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
@@ -3190,10 +3211,9 @@ static int airoha_tc_htb_delete_leaf_que
@@ -2847,10 +2862,9 @@ static int airoha_tc_htb_delete_leaf_que
static int airoha_tc_htb_destroy(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -139,7 +134,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_tc_remove_htb_queue(netdev, q);
return 0;
@@ -3204,9 +3224,8 @@ static int airoha_tc_get_htb_get_leaf_qu
@@ -2861,9 +2875,8 @@ static int airoha_tc_get_htb_get_leaf_qu
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -150,7 +145,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
@@ -3225,6 +3244,7 @@ static int airoha_tc_setup_qdisc_htb(str
@@ -2882,6 +2895,7 @@ static int airoha_tc_setup_qdisc_htb(str
case TC_HTB_DESTROY:
return airoha_tc_htb_destroy(dev);
case TC_HTB_NODE_MODIFY:
@@ -160,7 +155,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
case TC_HTB_LEAF_DEL:
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -545,6 +545,8 @@ struct airoha_qdma {
@@ -533,6 +533,8 @@ struct airoha_qdma {
struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
@@ -169,16 +164,16 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
};
struct airoha_gdm_dev {
@@ -556,6 +558,8 @@ struct airoha_gdm_dev {
struct phylink *phylink;
struct phylink_config phylink_config;
#endif
@@ -540,6 +542,8 @@ struct airoha_gdm_dev {
struct airoha_qdma *qdma;
struct net_device *dev;
struct airoha_eth *eth;
+
+ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
};
struct airoha_gdm_port {
@@ -565,8 +569,6 @@ struct airoha_gdm_port {
@@ -549,8 +553,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
@@ -1,11 +1,8 @@
From 00272dbf6a52241a21145631f22dc5f03891078b Mon Sep 17 00:00:00 2001
Message-ID: <00272dbf6a52241a21145631f22dc5f03891078b.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From 962c17dd287887aa87a6f30d64239d7e1e4e05fa Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 10 Apr 2026 14:47:08 +0200
Subject: [PATCH 05/13] net: airoha: Move {cpu,fwd}_tx_packets in
airoha_gdm_dev struct
Date: Wed, 27 May 2026 12:21:19 +0200
Subject: [PATCH 5/6] net: airoha: Move {cpu,fwd}_tx_packets in airoha_gdm_dev
struct
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, cpu_tx_packets and fwd_tx_packets fields can
@@ -13,7 +10,10 @@ be overwritten with the value from a different QDMA block. In order to
fix the issue move cpu_tx_packets and fwd_tx_packets fields from
airoha_gdm_port struct to airoha_gdm_dev one.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260527-airoha-eth-multi-serdes-preliminary-v1-5-ec6ed73ef7fc@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 16 +++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +++----
@@ -21,7 +21,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2658,19 +2658,17 @@ static int airoha_qdma_get_tx_ets_stats(
@@ -2315,19 +2315,17 @@ static int airoha_qdma_get_tx_ets_stats(
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -50,8 +50,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -560,6 +560,9 @@ struct airoha_gdm_dev {
#endif
@@ -544,6 +544,9 @@ struct airoha_gdm_dev {
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+ /* qos stats counters */
@@ -60,7 +60,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
};
struct airoha_gdm_port {
@@ -569,10 +572,6 @@ struct airoha_gdm_port {
@@ -553,10 +556,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
@@ -0,0 +1,38 @@
From 842a7ee50d68d66afef4536bf3abdaaed2e0e5f5 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 27 May 2026 12:21:20 +0200
Subject: [PATCH 6/6] net: airoha: Rename airoha_set_gdm2_loopback in
airoha_enable_gdm2_loopback
This is a preliminary patch in order to allow the user to select if the
configured device will be used as hw lan or wan.
Please not this patch does not introduce any logical changes, just
cosmetic ones.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260527-airoha-eth-multi-serdes-preliminary-v1-6-ec6ed73ef7fc@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1796,7 +1796,7 @@ static int airoha_dev_set_macaddr(struct
return 0;
}
-static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
+static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
@@ -1877,7 +1877,7 @@ static int airoha_dev_init(struct net_de
if (!eth->ports[1]) {
int err;
- err = airoha_set_gdm2_loopback(dev);
+ err = airoha_enable_gdm2_loopback(dev);
if (err)
return err;
}
@@ -0,0 +1,37 @@
From b38cae85d1c45ff189d7ecb6ac36f41cdc3d84d0 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Tue, 2 Jun 2026 11:21:04 +0200
Subject: [PATCH] net: airoha: Fix use-after-free in metadata dst teardown
airoha_metadata_dst_free() runs metadata_dst_free() which frees the
metadata_dst with kfree() immediately, bypassing the RCU grace period.
In the RX path, skb_dst_set_noref() sets a non-refcounted pointer from
the skb to the metadata_dst. This function requires RCU read-side
protection and the dst must remain valid until all RCU readers complete.
Since metadata_dst_free() calls kfree() directly, an use-after-free can
occur if any skb still holds a noref pointer to the dst when the driver
tears it down.
Replace metadata_dst_free() with dst_release() which properly goes
through the refcount path: when the refcount drops to zero, it schedules
the actual free via call_rcu_hurry(), ensuring all RCU readers have
completed before the memory is freed.
Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260602-airoha-mtk-metadata-uaf-fix-v1-1-3aaa99d83351@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2972,7 +2972,7 @@ static void airoha_metadata_dst_free(str
if (!port->dsa_meta[i])
continue;
- metadata_dst_free(port->dsa_meta[i]);
+ dst_release(&port->dsa_meta[i]->dst);
}
}
@@ -0,0 +1,40 @@
From 144969cd80c5227c2b19fe5f644d6eebd61be586 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 3 Jun 2026 12:30:01 +0200
Subject: [PATCH] net: airoha: Report extack error to the user if
airoha_tc_htb_modify_queue() fails
Report an extack error message in airoha_tc_htb_modify_queue routine if
airoha_qdma_set_tx_rate_limit() fails.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Link: https://patch.msgid.link/20260603-airoha_tc_htb_modify_queue-err-message-v1-1-33ec3ab997d9@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2605,13 +2605,19 @@ static int airoha_tc_htb_modify_queue(st
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
+ int err;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
return -EINVAL;
}
- return airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+ err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+ if (err)
+ NL_SET_ERR_MSG_MOD(opt->extack,
+ "failed configuring htb offload");
+
+ return err;
}
static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
@@ -0,0 +1,41 @@
From f9f25118faa4dd2b6e3d14a03d123bbdbd59925d Mon Sep 17 00:00:00 2001
From: ZhaoJinming <zhaojinming@uniontech.com>
Date: Thu, 4 Jun 2026 15:03:52 +0800
Subject: [PATCH] net: airoha: Add NULL check for of_reserved_mem_lookup() in
airoha_qdma_init_hfwd_queues()
of_reserved_mem_lookup() may return NULL if the reserved memory region
referenced by the "memory-region" phandle is not found in the reserved
memory table (e.g. due to a misconfigured DTS or a removed
memory-region node). The current code dereferences the returned
pointer without checking for NULL, leading to a kernel NULL pointer
dereference at the following lines:
dma_addr = rmem->base; // line 1156
num_desc = div_u64(rmem->size, buf_size); // line 1160
Add a NULL check after of_reserved_mem_lookup() and return -ENODEV if
the lookup fails, which is consistent with the existing error handling
for of_parse_phandle() failure in the same code block.
Fixes: 3a1ce9e3d01b ("net: airoha: Add the capability to allocate hwfd buffers via reserved-memory")
Cc: stable@vger.kernel.org
Signed-off-by: ZhaoJinming <zhaojinming@uniontech.com>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1159,6 +1159,9 @@ static int airoha_qdma_init_hfwd_queues(
rmem = of_reserved_mem_lookup(np);
of_node_put(np);
+ if (!rmem)
+ return -ENODEV;
+
dma_addr = rmem->base;
/* Compute the number of hw descriptors according to the
* reserved memory size and the payload buffer size
@@ -0,0 +1,125 @@
From 4408f520680990042a1e6bfdf3cef217a1abf152 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 3 Jun 2026 08:00:16 +0200
Subject: [PATCH 1/5] net: airoha: Remove private net_device pointer in
airoha_gdm_dev struct
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>
Link: https://patch.msgid.link/20260603-airoha-eth-multi-serdes-v9-2-5d476bc2f426@kernel.org
Signed-off-by: Jakub Kicinski <kuba@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(-)
--- 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
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
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
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;
@@ -1870,7 +1872,7 @@ static int airoha_dev_init(struct net_de
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = &eth->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) {
@@ -3047,7 +3049,6 @@ static int airoha_alloc_gdm_device(struc
}
dev = netdev_priv(netdev);
- dev->dev = netdev;
dev->port = port;
port->dev = dev;
dev->eth = eth;
@@ -3110,7 +3111,7 @@ static int airoha_register_gdm_devices(s
if (!port)
continue;
- err = register_netdev(port->dev->dev);
+ err = register_netdev(netdev_from_priv(port->dev));
if (err)
return err;
}
@@ -3225,8 +3226,12 @@ error_napi_stop:
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);
@@ -3254,7 +3259,7 @@ static void airoha_remove(struct platfor
dev = port->dev;
if (dev)
- unregister_netdev(dev->dev);
+ unregister_netdev(netdev_from_priv(dev));
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
--- 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);
--- 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_
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;
@@ -1,11 +1,8 @@
From 8eb0a71bfbe92b6fbc668c5d9ebdcbf6523a89ad Mon Sep 17 00:00:00 2001
Message-ID: <8eb0a71bfbe92b6fbc668c5d9ebdcbf6523a89ad.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From a9c2ca61fec7706574c009d8f1898ca392368f89 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 1 Nov 2025 11:27:48 +0100
Subject: [PATCH 06/13] net: airoha: Support multiple net_devices for a single
FE GDM port
Date: Wed, 3 Jun 2026 08:00:17 +0200
Subject: [PATCH 2/5] net: airoha: Support multiple net_devices for a single FE
GDM port
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
@@ -48,15 +45,17 @@ 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>
Link: https://patch.msgid.link/20260603-airoha-eth-multi-serdes-v9-3-5d476bc2f426@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 274 +++++++++++++++++------
drivers/net/ethernet/airoha/airoha_eth.h | 10 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 13 +-
3 files changed, 228 insertions(+), 69 deletions(-)
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(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -116,7 +116,7 @@ static int airoha_set_vip_for_gdm_port(s
@@ -106,7 +106,7 @@ static int airoha_set_vip_for_gdm_port(s
struct airoha_eth *eth = dev->eth;
u32 vip_port;
@@ -65,7 +64,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (enable) {
airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
@@ -646,30 +646,26 @@ static int airoha_qdma_fill_rx_queue(str
@@ -566,24 +566,26 @@ static int airoha_qdma_fill_rx_queue(str
return nframes;
}
@@ -80,14 +79,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
- switch (sport) {
- case 0x18:
- port = 3; /* GDM4 */
- break;
- case 0x16:
- port = 2; /* GDM3 */
- break;
- case 0x10 ... 0x14:
- port = 0; /* GDM1 */
- port = 0;
- break;
- case 0x2 ... 0x4:
- port = sport - 1;
@@ -97,11 +90,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- }
+ if (eth->soc->ops.get_dev_from_sport(desc, &p, &d))
+ return ERR_PTR(-ENODEV);
+
+ if (p >= ARRAY_SIZE(eth->ports))
+ 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);
@@ -112,8 +105,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
}
static struct sk_buff *airoha_qdma_lro_rx_skb(struct airoha_queue *q,
@@ -841,9 +837,9 @@ static int airoha_qdma_rx_process(struct
static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
@@ -598,9 +600,9 @@ static int airoha_qdma_rx_process(struct
struct airoha_queue_entry *e = &q->entry[q->tail];
struct airoha_qdma_desc *desc = &q->desc[q->tail];
u32 hash, reason, msg1, desc_ctrl;
@@ -125,7 +118,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct page *page;
desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
@@ -864,15 +860,11 @@ static int airoha_qdma_rx_process(struct
@@ -621,15 +623,11 @@ static int airoha_qdma_rx_process(struct
if (!len || data_len < len)
goto free_frag;
@@ -142,9 +135,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- netdev = netdev_from_priv(port->dev);
+ netdev = netdev_from_priv(dev);
if (!q->skb) { /* first buffer */
q->skb = airoha_qdma_build_rx_skb(q, desc, e, netdev);
if (!q->skb)
@@ -893,6 +885,8 @@ static int airoha_qdma_rx_process(struct
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -659,6 +657,8 @@ static int airoha_qdma_rx_process(struct
continue;
if (netdev_uses_dsa(netdev)) {
@@ -153,7 +146,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
/* 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
@@ -1095,26 +1089,29 @@ static void airoha_qdma_wake_netdev_txqs
@@ -852,26 +852,29 @@ static void airoha_qdma_wake_netdev_txqs
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -178,7 +171,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ if (!dev)
+ continue;
- netdev = netdev_from_priv(port->dev);
- 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)
@@ -195,58 +188,21 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
}
q->txq_stopped = false;
@@ -1961,22 +1958,32 @@ static void airoha_update_hw_stats(struc
static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
{
- struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
int i;
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
- struct airoha_gdm_port *p = eth->ports[i];
- struct net_device *netdev;
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
- if (!p)
+ if (!port)
continue;
- netdev = netdev_from_priv(p->dev);
- if (netdev->reg_state != NETREG_REGISTERED)
- continue;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *iter_dev = port->devs[j];
+ struct net_device *netdev;
+
+ if (!iter_dev || iter_dev == dev)
+ continue;
+
+ if (iter_dev->qdma != dev->qdma)
+ continue;
+
+ netdev = netdev_from_priv(iter_dev);
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
- netdev_update_features(netdev);
+ netdev_update_features(netdev);
+ }
}
}
@@ -2037,7 +2044,8 @@ static int airoha_dev_open(struct net_de
@@ -1745,11 +1748,9 @@ static int airoha_dev_open(struct net_de
GLOBAL_CFG_RX_DMA_EN_MASK);
qdma->users++;
atomic_inc(&qdma->users);
- if (port->id == AIROHA_GDM2_IDX && airoha_ppe_is_enabled(eth, 1)) {
- 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(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
+ airoha_ppe_is_enabled(qdma->eth, 1))
pse_port = FE_PSE_PORT_PPE2;
}
@@ -2143,7 +2151,7 @@ static int airoha_set_gdm2_loopback(stru
- }
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
pse_port);
@@ -1837,7 +1838,7 @@ static int airoha_enable_gdm2_loopback(s
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));
@@ -255,7 +211,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (src_port < 0)
return src_port;
@@ -2160,7 +2168,7 @@ static int airoha_set_gdm2_loopback(stru
@@ -1854,7 +1855,7 @@ static int airoha_enable_gdm2_loopback(s
airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
@@ -264,7 +220,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
__field_prep(mask, AIROHA_GDM2_IDX));
@@ -2174,7 +2182,7 @@ static int airoha_dev_init(struct net_de
@@ -1868,7 +1869,7 @@ static int airoha_dev_init(struct net_de
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
@@ -273,7 +229,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
@@ -2197,8 +2205,8 @@ static int airoha_dev_init(struct net_de
@@ -1891,8 +1892,8 @@ static int airoha_dev_init(struct net_de
break;
}
@@ -284,7 +240,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
return 0;
}
@@ -2396,7 +2404,8 @@ static netdev_tx_t airoha_dev_xmit(struc
@@ -2058,7 +2059,8 @@ static netdev_tx_t airoha_dev_xmit(struc
}
fport = airoha_get_fe_port(dev);
@@ -294,7 +250,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
q = &qdma->q_tx[qid];
@@ -3339,12 +3348,15 @@ bool airoha_is_valid_gdm_dev(struct airo
@@ -2994,12 +2996,15 @@ bool airoha_is_valid_gdm_dev(struct airo
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -312,7 +268,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
return false;
@@ -3468,10 +3480,11 @@ static int airoha_setup_phylink(struct n
@@ -3007,10 +3012,11 @@ bool airoha_is_valid_gdm_dev(struct airo
static int airoha_alloc_gdm_device(struct airoha_eth *eth,
struct airoha_gdm_port *port,
@@ -326,15 +282,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int err;
netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
@@ -3489,7 +3502,6 @@ static int airoha_alloc_gdm_device(struc
netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
netdev->features |= AIROHA_HW_FEATURES;
netdev->vlan_features = AIROHA_HW_FEATURES;
@@ -3030,7 +3036,6 @@ static int airoha_alloc_gdm_device(struc
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 */
@@ -3507,10 +3519,24 @@ static int airoha_alloc_gdm_device(struc
@@ -3048,10 +3053,24 @@ static int airoha_alloc_gdm_device(struc
netdev->dev_addr);
}
@@ -358,9 +314,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ dev->nbq = nbq;
+ port->devs[index] = dev;
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
@@ -3528,7 +3554,8 @@ static int airoha_alloc_gdm_port(struct
return 0;
}
@@ -3061,7 +3080,8 @@ static int airoha_alloc_gdm_port(struct
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
struct airoha_gdm_port *port;
@@ -370,7 +326,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 id;
if (!id_ptr) {
@@ -3556,15 +3583,51 @@ static int airoha_alloc_gdm_port(struct
@@ -3089,15 +3109,51 @@ static int airoha_alloc_gdm_port(struct
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
port->id = id;
@@ -425,7 +381,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
static int airoha_register_gdm_devices(struct airoha_eth *eth)
@@ -3573,14 +3636,22 @@ static int airoha_register_gdm_devices(s
@@ -3106,14 +3162,22 @@ static int airoha_register_gdm_devices(s
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -452,7 +408,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
set_bit(DEV_STATE_REGISTERED, &eth->state);
@@ -3687,15 +3758,19 @@ error_napi_stop:
@@ -3220,17 +3284,22 @@ error_napi_stop:
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -473,18 +429,13 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ continue;
+ netdev = netdev_from_priv(dev);
if (netdev->reg_state == NETREG_REGISTERED) {
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port))
@@ -3703,6 +3778,7 @@ error_napi_stop:
#endif
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
}
+ of_node_put(netdev->dev.of_node);
}
airoha_metadata_dst_free(port);
}
@@ -3724,19 +3800,27 @@ static void airoha_remove(struct platfor
@@ -3252,14 +3321,22 @@ static void airoha_remove(struct platfor
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
@@ -494,6 +445,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
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;
@@ -501,16 +455,6 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ if (!dev)
+ continue;
+
#if defined(CONFIG_PCS_AIROHA)
- if (airhoa_is_phy_external(port))
- phylink_destroy(dev->phylink);
+ if (airhoa_is_phy_external(port))
+ phylink_destroy(dev->phylink);
#endif
- dev = port->dev;
- if (dev)
- unregister_netdev(netdev_from_priv(dev));
+ netdev = netdev_from_priv(dev);
+ unregister_netdev(netdev);
+ of_node_put(netdev->dev.of_node);
@@ -518,7 +462,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3798,6 +3882,39 @@ static u32 airoha_en7581_get_vip_port(st
@@ -3322,6 +3399,39 @@ static u32 airoha_en7581_get_vip_port(st
return 0;
}
@@ -556,9 +500,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+}
+
static const char * const an7583_xsi_rsts_names[] = {
"xsi-mac",
"hsi0-mac",
"hsi1-mac",
@@ -3846,6 +3963,36 @@ static u32 airoha_an7583_get_vip_port(st
@@ -3371,6 +3481,36 @@ static u32 airoha_an7583_get_vip_port(st
return 0;
}
@@ -595,7 +539,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static const struct airoha_eth_soc_data en7581_soc_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
@@ -3854,6 +4001,7 @@ static const struct airoha_eth_soc_data
@@ -3379,6 +3519,7 @@ static const struct airoha_eth_soc_data
.ops = {
.get_sport = airoha_en7581_get_sport,
.get_vip_port = airoha_en7581_get_vip_port,
@@ -603,7 +547,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
},
};
@@ -3865,6 +4013,7 @@ static const struct airoha_eth_soc_data
@@ -3390,6 +3531,7 @@ static const struct airoha_eth_soc_data
.ops = {
.get_sport = airoha_an7583_get_sport,
.get_vip_port = airoha_an7583_get_vip_port,
@@ -621,7 +565,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#define AIROHA_MAX_NUM_QDMA 2
#define AIROHA_MAX_NUM_IRQ_BANKS 4
#define AIROHA_MAX_DSA_PORTS 7
@@ -563,12 +564,13 @@ struct airoha_gdm_dev {
@@ -546,12 +547,13 @@ struct airoha_gdm_dev {
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
@@ -637,7 +581,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_hw_stats stats;
@@ -604,6 +606,8 @@ struct airoha_eth_soc_data {
@@ -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);
@@ -648,7 +592,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -168,9 +168,7 @@ static void airoha_ppe_hw_init(struct ai
@@ -167,9 +167,7 @@ static void airoha_ppe_hw_init(struct ai
airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
PPE_FLOW_CFG_IP6_6RD_MASK);
@@ -659,7 +603,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
FP0_EGRESS_MTU_MASK |
FP1_EGRESS_MTU_MASK,
@@ -178,11 +176,27 @@ static void airoha_ppe_hw_init(struct ai
@@ -177,11 +175,27 @@ static void airoha_ppe_hw_init(struct ai
AIROHA_MAX_MTU) |
FIELD_PREP(FP1_EGRESS_MTU_MASK,
AIROHA_MAX_MTU));
@@ -1,10 +1,7 @@
From de856a5b802cf030c8e242e98df3bc88446a4ea1 Mon Sep 17 00:00:00 2001
Message-ID: <de856a5b802cf030c8e242e98df3bc88446a4ea1.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From 99b9d095d71e5cb84121dacce2ac217aad9b174f Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 20 Mar 2026 11:09:40 +0100
Subject: [PATCH 07/13] net: airoha: Do not stop GDM port if it is shared
Date: Wed, 3 Jun 2026 08:00:18 +0200
Subject: [PATCH 3/5] net: airoha: Do not stop GDM port if it is shared
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
@@ -16,6 +13,8 @@ GDM port.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260603-airoha-eth-multi-serdes-v9-4-5d476bc2f426@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 59 +++++++++++++++++++-----
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
@@ -23,27 +22,25 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1992,10 +1992,10 @@ static int airoha_dev_open(struct net_de
@@ -1723,8 +1723,8 @@ static int airoha_dev_open(struct net_de
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;
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
- u32 pse_port = FE_PSE_PORT_PPE1;
/* HW LRO is configured on the QDMA and it is shared between
* all the devices using it. Refuse to open a second device on
@@ -2034,10 +2034,20 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
@@ -1738,10 +1738,20 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
- 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(eth, REG_GDM_LEN_CFG(port->id),
+ 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
@@ -51,7 +48,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ * multiple net_devices with different MTUs to share the same
+ * GDM port.
+ */
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
+ 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));
@@ -60,7 +57,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
@@ -2060,6 +2070,30 @@ static int airoha_dev_open(struct net_de
@@ -1757,6 +1767,30 @@ static int airoha_dev_open(struct net_de
return 0;
}
@@ -91,21 +88,22 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -2073,8 +2107,11 @@ static int airoha_dev_stop(struct net_de
@@ -1769,8 +1803,12 @@ static int airoha_dev_stop(struct net_de
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
- 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(eth, port);
+ airoha_set_port_mtu(dev->eth, port);
+ else
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+ airoha_set_gdm_port_fwd_cfg(qdma->eth,
+ REG_GDM_FWD_CFG(port->id),
+ FE_PSE_PORT_DROP);
if (!--qdma->users) {
if (atomic_dec_and_test(&qdma->users)) {
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
@@ -2238,13 +2275,10 @@ static int airoha_dev_change_mtu(struct
@@ -1925,13 +1963,10 @@ static int airoha_dev_change_mtu(struct
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
@@ -123,7 +121,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -571,6 +571,7 @@ struct airoha_gdm_dev {
@@ -554,6 +554,7 @@ struct airoha_gdm_dev {
struct airoha_gdm_port {
struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
int id;
@@ -1,10 +1,7 @@
From c4f3077948eda05a6b9d1a11304d82c3e0300151 Mon Sep 17 00:00:00 2001
Message-ID: <c4f3077948eda05a6b9d1a11304d82c3e0300151.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From 7758cb462ff72b10d71e72bca8cf65179a1264e8 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 3 Apr 2026 12:07:27 +0200
Subject: [PATCH 08/13] net: airoha: Introduce WAN device flag
Date: Wed, 3 Jun 2026 08:00:19 +0200
Subject: [PATCH 4/5] net: airoha: Introduce WAN device flag
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
@@ -21,15 +18,17 @@ officially supported since PCS/external phy is not merged mainline yet
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260603-airoha-eth-multi-serdes-v9-5-5d476bc2f426@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 72 +++++++++++++++++++-----
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, 65 insertions(+), 22 deletions(-)
3 files changed, 66 insertions(+), 22 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2214,36 +2214,81 @@ static int airoha_set_gdm2_loopback(stru
@@ -1902,36 +1902,81 @@ static int airoha_enable_gdm2_loopback(s
return 0;
}
@@ -86,7 +85,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- if (!eth->ports[1]) {
- int err;
-
- err = airoha_set_gdm2_loopback(dev);
- err = airoha_enable_gdm2_loopback(dev);
- if (err)
- return err;
- }
@@ -119,7 +118,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
+ int err;
+
+ err = airoha_set_gdm2_loopback(dev);
+ err = airoha_enable_gdm2_loopback(dev);
+ if (err)
+ return err;
+ }
@@ -128,7 +127,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -550,6 +550,10 @@ struct airoha_qdma {
@@ -538,6 +538,10 @@ struct airoha_qdma {
DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
@@ -139,7 +138,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_gdm_dev {
struct airoha_gdm_port *port;
struct airoha_qdma *qdma;
@@ -565,6 +569,7 @@ struct airoha_gdm_dev {
@@ -548,6 +552,7 @@ struct airoha_gdm_dev {
u64 cpu_tx_packets;
u64 fwd_tx_packets;
@@ -147,7 +146,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int nbq;
};
@@ -671,13 +676,7 @@ static inline u16 airoha_qdma_get_txq(st
@@ -654,13 +659,7 @@ static inline u16 airoha_qdma_get_txq(st
static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
@@ -164,7 +163,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static inline bool airoha_is_7581(struct airoha_eth *eth)
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -354,7 +354,7 @@ static int airoha_ppe_foe_entry_prepare(
@@ -355,7 +355,7 @@ static int airoha_ppe_foe_entry_prepare(
return -EINVAL;
port = dev->port;
@@ -1,38 +1,42 @@
From 144f0e6e55896625e3411aad02399a5ebb48d8f9 Mon Sep 17 00:00:00 2001
Message-ID: <144f0e6e55896625e3411aad02399a5ebb48d8f9.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From ef2aee987174b51573645b4bdacedf610e89ce1a Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 13 Apr 2026 17:38:51 +0200
Subject: [PATCH 09/13] net: airoha: Support multiple LAN/WAN interfaces for hw
Date: Wed, 3 Jun 2026 08:00:20 +0200
Subject: [PATCH 5/5] 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, used to determine whether received traffic is destined for
this host or should be forwarded to another device.
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 a common upper MAC address, which is programmed into the
REG_FE_{LAN,WAN}_MAC_H register. The lower bytes of 'local' addresses can
be expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX
registers.
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.
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>
Link: https://patch.msgid.link/20260603-airoha-eth-multi-serdes-v9-6-5d476bc2f426@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 77 ++++++++++++++++++++----
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, 68 insertions(+), 15 deletions(-)
3 files changed, 71 insertions(+), 12 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -81,20 +81,76 @@ static bool airhoa_is_phy_external(struc
@@ -71,20 +71,76 @@ static void airoha_qdma_irq_disable(stru
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
#endif
-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)
@@ -105,15 +109,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
- 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, addr);
- 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,
@@ -2141,13 +2197,18 @@ static int airoha_dev_stop(struct net_de
@@ -1829,13 +1885,18 @@ static int airoha_dev_stop(struct net_de
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -136,7 +140,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -712,7 +712,7 @@ void airoha_ppe_check_skb(struct airoha_
@@ -683,7 +683,7 @@ void airoha_ppe_check_skb(struct airoha_
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);
@@ -147,7 +151,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
u32 hash);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1497,12 +1497,10 @@ void airoha_ppe_check_skb(struct airoha_
@@ -1487,12 +1487,10 @@ void airoha_ppe_check_skb(struct airoha_
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
@@ -1,29 +1,31 @@
From 9652322e0b47eacfef497828f6476a2a3169fd13 Mon Sep 17 00:00:00 2001
Message-ID: <9652322e0b47eacfef497828f6476a2a3169fd13.1779351672.git.lorenzo@kernel.org>
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Sat, 17 Jan 2026 14:46:12 +0100
Subject: [PATCH] net: airoha: Better handle MIB for GDM with multiple port
attached
In the context of a GDM that can have multiple port attached (GDM3/4)
the HW counter (MIB) are global for the GDM port. This cause duplicated
stats reported to the kernel for the related interface.
From 8f4695fb67b259b2cae0be1eef55859bfc559058 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Thu, 11 Jun 2026 12:43:00 +0200
Subject: [PATCH] net: airoha: better handle MIBs for GDM ports with multiple
devs attached
In the context of a GDM port that can have multiple net_devices attached
(GDM3 and GDM4), the HW counters (MIBs) are global for the GDM port.
This cause duplicated stats reported to the kernel for the related
net_device.
The SoC supports a split MIB feature where each counter is tracked based
on the relevant HW channel (NBQ) to account for this scenario and
provide a way to select the related counter on accessing the MIB
registers.
Enable this feature for GDM3 and GDM4 and configure the relevant HW
channel before updating the HW stats to report correct HW counter to the
kernel for the related interface.
Move the stats struct from port to dev since HW counter are now specific
to the network device instead of the GDM port. Refactor
airoha_update_hw_stats() to take airoha_eth and airoha_gdm_port
parameters since the function operates on the entire port.
Also move the stats struct from port to dev since HW counter are
now specific to the network interface instead of the GDM port.
Co-developed-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Brown Huang <Brown.huang@airoha.com>
Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260611-airoha-eth-multi-serdes-stats-v1-1-42442ae42064@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 191 +++++++++++++----------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +-
@@ -31,9 +33,9 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -640,6 +640,14 @@ static int airoha_fe_init(struct airoha_
airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX),
GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
@@ -556,6 +556,14 @@ static int airoha_fe_init(struct airoha_
airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM3_IDX), GDM_PAD_EN_MASK);
airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX), GDM_PAD_EN_MASK);
+ /* Enable split for MIB counters for GDM3 and GDM4 */
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX),
@@ -46,7 +48,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
airoha_fe_crsn_qsel_init(eth);
airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK);
@@ -1872,149 +1880,169 @@ static void airoha_qdma_stop_napi(struct
@@ -1629,149 +1637,169 @@ static void airoha_qdma_stop_napi(struct
}
}
@@ -59,14 +61,14 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
- spin_lock(&port->stats.lock);
- u64_stats_update_begin(&port->stats.syncp);
+ u64_stats_update_begin(&dev->stats.syncp);
+
+ /* Read relevant MIB for GDM with multiple port attached */
+ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
+ airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
+ FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
+ FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
+
+ u64_stats_update_begin(&dev->stats.syncp);
/* TX */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
@@ -245,7 +247,9 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+
+ u64_stats_update_end(&dev->stats.syncp);
+}
+
- /* reset mib counters */
- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
@@ -257,9 +261,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+ if (port->devs[i])
+ airoha_dev_get_hw_stats(port->devs[i]);
+ }
- /* reset mib counters */
- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
+
+ /* Reset MIB counters */
+ airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
@@ -269,8 +271,8 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+ spin_unlock(&port->stats_lock);
}
static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
@@ -2418,23 +2446,22 @@ static void airoha_dev_get_stats64(struc
static int airoha_dev_open(struct net_device *netdev)
@@ -2046,23 +2074,22 @@ static void airoha_dev_get_stats64(struc
struct rtnl_link_stats64 *storage)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -306,7 +308,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
@@ -2723,20 +2750,19 @@ static void airoha_ethtool_get_mac_stats
@@ -2313,20 +2340,19 @@ static void airoha_ethtool_get_mac_stats
struct ethtool_eth_mac_stats *stats)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -336,7 +338,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
@@ -2756,8 +2782,7 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2346,8 +2372,7 @@ airoha_ethtool_get_rmon_stats(struct net
const struct ethtool_rmon_hist_range **ranges)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -346,7 +348,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
unsigned int start;
BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
@@ -2770,7 +2795,7 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2360,7 +2385,7 @@ airoha_ethtool_get_rmon_stats(struct net
do {
int i;
@@ -355,7 +357,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
stats->fragments = hw_stats->rx_fragment;
stats->jabbers = hw_stats->rx_jabber;
for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
@@ -2778,7 +2803,7 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2368,7 +2393,7 @@ airoha_ethtool_get_rmon_stats(struct net
stats->hist[i] = hw_stats->rx_len[i];
stats->hist_tx[i] = hw_stats->tx_len[i];
}
@@ -364,7 +366,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
@@ -3849,6 +3874,7 @@ static int airoha_alloc_gdm_device(struc
@@ -3208,6 +3233,7 @@ static int airoha_alloc_gdm_device(struc
netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
@@ -372,7 +374,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
dev->port = port;
dev->eth = eth;
dev->nbq = nbq;
@@ -3896,9 +3922,8 @@ static int airoha_alloc_gdm_port(struct
@@ -3247,9 +3273,8 @@ static int airoha_alloc_gdm_port(struct
if (!port)
return -ENOMEM;
@@ -385,7 +387,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
err = airoha_metadata_dst_alloc(port);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -228,8 +228,6 @@ struct airoha_tx_irq_queue {
@@ -215,8 +215,6 @@ struct airoha_tx_irq_queue {
};
struct airoha_hw_stats {
@@ -394,7 +396,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
struct u64_stats_sync syncp;
/* get_stats64 */
@@ -573,6 +571,8 @@ struct airoha_gdm_dev {
@@ -554,6 +552,8 @@ struct airoha_gdm_dev {
u32 flags;
int nbq;
@@ -403,7 +405,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
};
struct airoha_gdm_port {
@@ -580,7 +580,8 @@ struct airoha_gdm_port {
@@ -561,7 +561,8 @@ struct airoha_gdm_port {
int id;
int users;
@@ -1,5 +1,4 @@
From a459b560e58be327689e9bd8c0a6be9a4f163366 Mon Sep 17 00:00:00 2001
Message-ID: <a459b560e58be327689e9bd8c0a6be9a4f163366.1781641894.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Thu, 11 Jun 2026 23:55:51 +0200
Subject: [PATCH] net: airoha: use int instead of atomic_t for qdma users
@@ -18,18 +17,18 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1761,7 +1761,7 @@ static int airoha_dev_open(struct net_de
@@ -1840,7 +1840,7 @@ static int airoha_dev_open(struct net_de
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
GLOBAL_CFG_TX_DMA_EN_MASK |
GLOBAL_CFG_RX_DMA_EN_MASK);
- atomic_inc(&qdma->users);
+ qdma->users++;
if (port->id == AIROHA_GDM2_IDX &&
airoha_ppe_is_enabled(qdma->eth, 1)) {
@@ -1788,7 +1788,7 @@ static int airoha_dev_stop(struct net_de
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
if (!airoha_is_lan_gdm_dev(dev) &&
airoha_ppe_is_enabled(qdma->eth, 1))
@@ -1894,7 +1894,7 @@ static int airoha_dev_stop(struct net_de
REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
- if (atomic_dec_and_test(&qdma->users)) {
+ if (!--qdma->users) {
@@ -38,7 +37,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
GLOBAL_CFG_RX_DMA_EN_MASK);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -525,7 +525,7 @@ struct airoha_qdma {
@@ -524,7 +524,7 @@ struct airoha_qdma {
struct airoha_eth *eth;
void __iomem *regs;
@@ -0,0 +1,50 @@
From aeb62be4d3b18b76551bf7806e30d72e5c3bbade Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 8 Jun 2026 23:24:15 +0200
Subject: [PATCH] net: airoha: move get_sport() callback at the beginning of
airoha_enable_gdm2_loopback()
Move the get_sport() callback invocation at the beginning of
airoha_enable_gdm2_loopback() routine in order to avoid leaving the
hardware in a partially configured state if get_sport() fails.
Previously, get_sport() was called after GDM2 forwarding, loopback,
channel, length, VIP and IFC registers had already been programmed.
A failure at that point would return an error leaving GDM2 with
loopback enabled but WAN port, PPE CPU port and flow control mappings
not configured.
Performing the get_sport() lookup before any register write guarantees
the routine either completes the full configuration sequence or exits
with no side effects on the hardware.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260608-airoha_enable_gdm2_loopback-minor-change-v1-1-1787a0f42b31@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1936,6 +1936,10 @@ static int airoha_enable_gdm2_loopback(s
u32 val, pse_port, chan;
int i, src_port;
+ src_port = eth->soc->ops.get_sport(port, dev->nbq);
+ if (src_port < 0)
+ return src_port;
+
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
FE_PSE_PORT_DROP);
airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
@@ -1965,10 +1969,6 @@ static int airoha_enable_gdm2_loopback(s
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, dev->nbq);
- if (src_port < 0)
- return src_port;
-
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, src_port));
@@ -1,7 +1,6 @@
From 3d67e4ba79bd4b40f520d26af098f133b5f551d4 Mon Sep 17 00:00:00 2001
Message-ID: <3d67e4ba79bd4b40f520d26af098f133b5f551d4.1781097105.git.lorenzo@kernel.org>
From 9912dfa2d46aef4cd708246aa6b39b49213f5e62 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 6 Jun 2026 08:53:03 +0200
Date: Wed, 10 Jun 2026 15:25:13 +0200
Subject: [PATCH] net: airoha: simplify WAN device check in airoha_dev_init()
airoha_register_gdm_devices() iterates eth->ports[] in order, so GDM2's
@@ -13,13 +12,16 @@ Remove the redundant check and rely solely on airoha_get_wan_gdm_dev()
which handles both the GDM2-present and GDM2-absent cases.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Link: https://patch.msgid.link/20260610-airoha-eth-simplify-dev-init-v2-1-8f244e69b0d4@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2407,18 +2407,10 @@ static int airoha_dev_init(struct net_de
@@ -2035,18 +2035,10 @@ static int airoha_dev_init(struct net_de
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -45,6 +45,6 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
- for (i = 0; i < AIROHA_NUM_TX_RING; i++)
+ for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++)
airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
@@ -24,11 +24,11 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2797,7 +2797,7 @@ static void airoha_tc_remove_htb_queue(s
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = dev->qdma;
netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
- airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0);
+ airoha_qdma_set_tx_rate_limit(dev, queue, 0, 0);
clear_bit(queue, port->qos_sq_bmap);
}
netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
- airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
+ airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);
clear_bit(queue, qdma->qos_channel_map);
clear_bit(queue, dev->qos_sq_bmap);
@@ -13,7 +13,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1487,6 +1487,10 @@ static int airoha_hw_init(struct platfor
@@ -1568,6 +1568,10 @@ static int airoha_hw_init(struct platfor
if (err)
return err;
@@ -1,31 +0,0 @@
From ad29054f9b0e96e30a5d0bb6967d1204b8ea8bd1 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Fri, 17 Jan 2025 10:12:02 +0100
Subject: [PATCH 3/9] net: airoha: add reference for SPORT GDM4 in
qdma_get_gdm_port
Add SPORT reference in get gdm port as the on receive the SPORT 0x18 is
assigned for the GDM4 port.
While at it also add comments to better identify GDM1 ports.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -574,8 +574,11 @@ static int airoha_qdma_get_gdm_port(stru
sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
switch (sport) {
+ case 0x18:
+ port = 3; /* GDM4 */
+ break;
case 0x10 ... 0x14:
- port = 0;
+ port = 0; /* GDM1 */
break;
case 0x2 ... 0x4:
port = sport - 1;
@@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -498,8 +498,10 @@ static int airoha_fe_init(struct airoha_
@@ -553,8 +553,10 @@ static int airoha_fe_init(struct airoha_
FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) |
FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22));
@@ -26,15 +26,15 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX),
+ GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
airoha_fe_crsn_qsel_init(eth);
@@ -1719,7 +1721,8 @@ static int airoha_dev_open(struct net_de
/* Enable split for MIB counters for GDM3 and GDM4 */
airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX),
@@ -1819,7 +1821,8 @@ static int airoha_dev_open(struct net_de
if (err)
return err;
- if (netdev_uses_dsa(dev))
- if (netdev_uses_dsa(netdev))
+ /* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
+ if (netdev_uses_dsa(dev) || port->id > 2)
+ if (netdev_uses_dsa(netdev) || port->id > 2)
airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
else
@@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3196,7 +3196,6 @@ static void airoha_remove(struct platfor
@@ -3511,7 +3511,6 @@ static void airoha_remove(struct platfor
}
static const char * const en7581_xsi_rsts_names[] = {
@@ -23,7 +23,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
"hsi0-mac",
"hsi1-mac",
"hsi-mac",
@@ -3250,7 +3249,6 @@ static u32 airoha_en7581_get_vip_port(st
@@ -3598,7 +3597,6 @@ static int airoha_en7581_get_dev_from_sp
}
static const char * const an7583_xsi_rsts_names[] = {
@@ -1,261 +0,0 @@
From bdcad9ab6b0f071e8492d88064a58323d7155aa7 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Fri, 17 Jan 2025 13:23:13 +0100
Subject: [PATCH] net: airoha: add phylink support for GDM2/4
Add phylink support for GDM2/4 port that require configuration of the
PCS to make the external PHY or attached SFP cage work.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 133 ++++++++++++++++++++++
drivers/net/ethernet/airoha/airoha_eth.h | 4 +
drivers/net/ethernet/airoha/airoha_regs.h | 12 ++
3 files changed, 149 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -8,6 +8,7 @@
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/tcp.h>
+#include <linux/pcs/pcs-airoha.h>
#include <linux/u64_stats_sync.h>
#include <net/dst_metadata.h>
#include <net/page_pool/helpers.h>
@@ -71,6 +72,11 @@ static void airoha_qdma_irq_disable(stru
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
+static bool airhoa_is_phy_external(struct airoha_gdm_port *port)
+{
+ return port->id != 1;
+}
+
static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
{
struct airoha_eth *eth = port->qdma->eth;
@@ -1716,6 +1722,17 @@ static int airoha_dev_open(struct net_de
struct airoha_qdma *qdma = port->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
+ if (airhoa_is_phy_external(port)) {
+ err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
+ if (err) {
+ netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
+ err);
+ return err;
+ }
+
+ phylink_start(port->phylink);
+ }
+
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
if (err)
@@ -1777,6 +1794,11 @@ static int airoha_dev_stop(struct net_de
}
}
+ if (airhoa_is_phy_external(port)) {
+ phylink_stop(port->phylink);
+ phylink_disconnect_phy(port->phylink);
+ }
+
return 0;
}
@@ -2916,6 +2938,20 @@ static const struct ethtool_ops airoha_e
.get_link = ethtool_op_get_link,
};
+static struct phylink_pcs *airoha_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct airoha_gdm_port *port = container_of(config, struct airoha_gdm_port,
+ phylink_config);
+
+ return port->pcs;
+}
+
+static void airoha_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+}
+
static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
{
int i;
@@ -2960,6 +2987,99 @@ bool airoha_is_valid_gdm_port(struct air
return false;
}
+static void airoha_mac_link_up(struct phylink_config *config, struct phy_device *phy,
+ unsigned int mode, phy_interface_t interface,
+ int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+ struct airoha_gdm_port *port = container_of(config, struct airoha_gdm_port,
+ phylink_config);
+ struct airoha_qdma *qdma = port->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ u32 frag_size_tx, frag_size_rx;
+
+ switch (speed) {
+ case SPEED_10000:
+ case SPEED_5000:
+ frag_size_tx = 8;
+ frag_size_rx = 8;
+ break;
+ case SPEED_2500:
+ frag_size_tx = 2;
+ frag_size_rx = 1;
+ break;
+ default:
+ frag_size_tx = 1;
+ frag_size_rx = 0;
+ }
+
+ /* Configure TX/RX frag based on speed */
+ if (port->id == 4) {
+ airoha_fe_rmw(eth, REG_GDMA4_TMBI_FRAG, GDMA4_SGMII0_TX_FRAG_SIZE,
+ FIELD_PREP(GDMA4_SGMII0_TX_FRAG_SIZE, frag_size_tx));
+
+ airoha_fe_rmw(eth, REG_GDMA4_RMBI_FRAG, GDMA4_SGMII0_RX_FRAG_SIZE,
+ FIELD_PREP(GDMA4_SGMII0_RX_FRAG_SIZE, frag_size_rx));
+ }
+}
+
+static void airoha_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+}
+
+static const struct phylink_mac_ops airoha_phylink_ops = {
+ .mac_select_pcs = airoha_phylink_mac_select_pcs,
+ .mac_config = airoha_mac_config,
+ .mac_link_up = airoha_mac_link_up,
+ .mac_link_down = airoha_mac_link_down,
+};
+
+static int airoha_setup_phylink(struct net_device *dev)
+{
+ struct airoha_gdm_port *port = netdev_priv(dev);
+ struct device_node *np = dev->dev.of_node;
+ phy_interface_t phy_mode;
+ struct phylink *phylink;
+ int err;
+
+ err = of_get_phy_mode(np, &phy_mode);
+ if (err) {
+ dev_err(&dev->dev, "incorrect phy-mode\n");
+ return err;
+ }
+
+ port->phylink_config.dev = &dev->dev;
+ port->phylink_config.type = PHYLINK_NETDEV;
+ port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD |
+ MAC_5000FD | MAC_10000FD;
+
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_USXGMII,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER,
+ port->phylink_config.supported_interfaces);
+
+ port->pcs = airoha_pcs_create(&dev->dev);
+ if (IS_ERR(port->pcs))
+ return PTR_ERR(port->pcs);
+
+ phylink = phylink_create(&port->phylink_config,
+ of_fwnode_handle(np),
+ phy_mode, &airoha_phylink_ops);
+ if (IS_ERR(phylink))
+ return PTR_ERR(phylink);
+
+ port->phylink = phylink;
+
+ return 0;
+}
+
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
{
@@ -3033,6 +3173,12 @@ static int airoha_alloc_gdm_port(struct
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
+ if (airhoa_is_phy_external(port)) {
+ err = airoha_setup_phylink(dev);
+ if (err)
+ return err;
+ }
+
return airoha_metadata_dst_alloc(port);
}
@@ -3160,8 +3306,12 @@ error_napi_stop:
if (!port)
continue;
if (port->dev->reg_state == NETREG_REGISTERED)
unregister_netdev(port->dev);
+ if (airhoa_is_phy_external(port)) {
+ phylink_destroy(port->phylink);
+ airoha_pcs_destroy(port->pcs);
+ }
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3186,8 +3335,12 @@ static void airoha_remove(struct platfor
if (!port)
continue;
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
+ if (airhoa_is_phy_external(port)) {
+ phylink_destroy(port->phylink);
+ airoha_pcs_destroy(port->pcs);
+ }
}
airoha_hw_cleanup(eth);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -542,6 +542,10 @@ struct airoha_gdm_port {
int id;
int nbq;
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
+ struct phylink_pcs *pcs;
+
struct airoha_hw_stats stats;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -358,6 +358,18 @@
#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5)
#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0)
+#define REG_GDMA4_TMBI_FRAG 0x2028
+#define GDMA4_SGMII1_TX_WEIGHT GENMASK(31, 26)
+#define GDMA4_SGMII1_TX_FRAG_SIZE GENMASK(25, 16)
+#define GDMA4_SGMII0_TX_WEIGHT GENMASK(15, 10)
+#define GDMA4_SGMII0_TX_FRAG_SIZE GENMASK(9, 0)
+
+#define REG_GDMA4_RMBI_FRAG 0x202c
+#define GDMA4_SGMII1_RX_WEIGHT GENMASK(31, 26)
+#define GDMA4_SGMII1_RX_FRAG_SIZE GENMASK(25, 16)
+#define GDMA4_SGMII0_RX_WEIGHT GENMASK(15, 10)
+#define GDMA4_SGMII0_RX_FRAG_SIZE GENMASK(9, 0)
+
#define REG_MC_VLAN_EN 0x2100
#define MC_VLAN_EN_MASK BIT(0)
@@ -0,0 +1,361 @@
From beb56181a370b003b12f657239b510435cae2c9d Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Fri, 17 Jan 2025 13:23:13 +0100
Subject: [PATCH] net: airoha: add phylink support
Add phylink support for each GDM port. For GDM1 add the internal interface
mode as the only supported mode. For GDM2/3/4 add the required
configuration of the PCS to make the external PHY or attached SFP cage
work.
These needs to be defined in the GDM port node using the pcs-handle
property.
Update and provide a .get/set_link_ksettings function that use phylink
for ethtool OPs now that we fully support phylink.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/Kconfig | 1 +
drivers/net/ethernet/airoha/airoha_eth.c | 193 +++++++++++++++++++++-
drivers/net/ethernet/airoha/airoha_eth.h | 7 +-
drivers/net/ethernet/airoha/airoha_regs.h | 12 ++
4 files changed, 206 insertions(+), 7 deletions(-)
--- a/drivers/net/ethernet/airoha/Kconfig
+++ b/drivers/net/ethernet/airoha/Kconfig
@@ -20,6 +20,7 @@ config NET_AIROHA
depends on NET_DSA || !NET_DSA
select NET_AIROHA_NPU
select PAGE_POOL
+ select PHYLINK
help
This driver supports the gigabit ethernet MACs in the
Airoha SoC family.
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -8,6 +8,7 @@
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/tcp.h>
+#include <linux/pcs/pcs.h>
#include <linux/u64_stats_sync.h>
#include <net/dst_metadata.h>
#include <net/page_pool/helpers.h>
@@ -1794,7 +1795,7 @@ static void airoha_update_hw_stats(struc
struct airoha_gdm_port *port = dev->port;
int i;
- spin_lock(&port->stats_lock);
+ spin_lock(&port->lock);
for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
if (port->devs[i])
@@ -1805,7 +1806,7 @@ static void airoha_update_hw_stats(struc
airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
- spin_unlock(&port->stats_lock);
+ spin_unlock(&port->lock);
}
static int airoha_dev_open(struct net_device *netdev)
@@ -1816,6 +1817,14 @@ static int airoha_dev_open(struct net_de
u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
+ err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
+ if (err) {
+ netdev_err(netdev, "could not attach PHY: %d\n", err);
+ return err;
+ }
+
+ phylink_start(dev->phylink);
+
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
if (err)
@@ -1914,6 +1923,9 @@ static int airoha_dev_stop(struct net_de
}
}
+ phylink_stop(dev->phylink);
+ phylink_disconnect_phy(dev->phylink);
+
return 0;
}
@@ -2395,6 +2407,24 @@ airoha_ethtool_get_rmon_stats(struct net
} while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
+static int
+airoha_ethtool_get_link_ksettings(struct net_device *netdev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+
+ return phylink_ethtool_ksettings_get(dev->phylink, cmd);
+}
+
+static int
+airoha_ethtool_set_link_ksettings(struct net_device *netdev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+
+ return phylink_ethtool_ksettings_set(dev->phylink, cmd);
+}
+
static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
int channel, enum tx_sched_mode mode,
const u16 *weights, u8 n_weights)
@@ -3119,7 +3149,8 @@ static const struct ethtool_ops airoha_e
.get_drvinfo = airoha_ethtool_get_drvinfo,
.get_eth_mac_stats = airoha_ethtool_get_mac_stats,
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .get_link_ksettings = airoha_ethtool_get_link_ksettings,
+ .set_link_ksettings = airoha_ethtool_set_link_ksettings,
.get_link = ethtool_op_get_link,
};
@@ -3175,6 +3206,159 @@ bool airoha_is_valid_gdm_dev(struct airo
return false;
}
+/* Nothing to do in MAC, everything is handled in PCS */
+static void airoha_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+}
+
+static void airoha_mac_link_up(struct phylink_config *config, struct phy_device *phy,
+ unsigned int mode, phy_interface_t interface,
+ int speed, int duplex, bool tx_pause, bool rx_pause)
+{
+ struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
+ phylink_config);
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ u32 frag_size_tx, frag_size_rx;
+ u32 mask, val;
+
+ /* TX/RX frag is configured only for GDM4 */
+ if (port->id != AIROHA_GDM4_IDX)
+ return;
+
+ switch (speed) {
+ case SPEED_10000:
+ case SPEED_5000:
+ frag_size_tx = 8;
+ frag_size_rx = 8;
+ break;
+ case SPEED_2500:
+ frag_size_tx = 2;
+ frag_size_rx = 1;
+ break;
+ default:
+ frag_size_tx = 1;
+ frag_size_rx = 0;
+ }
+
+ spin_lock(&port->lock);
+
+ /* Configure TX/RX frag based on speed */
+ if (dev->nbq == 1) {
+ mask = GDM4_SGMII1_TX_FRAG_SIZE_MASK;
+ val = FIELD_PREP(GDM4_SGMII1_TX_FRAG_SIZE_MASK,
+ frag_size_tx);
+ } else {
+ mask = GDM4_SGMII0_TX_FRAG_SIZE_MASK;
+ val = FIELD_PREP(GDM4_SGMII0_TX_FRAG_SIZE_MASK,
+ frag_size_tx);
+ }
+ airoha_fe_rmw(eth, REG_FE_GDM4_TMBI_FRAG, mask, val);
+
+ if (dev->nbq == 1) {
+ mask = GDM4_SGMII1_RX_FRAG_SIZE_MASK;
+ val = FIELD_PREP(GDM4_SGMII1_RX_FRAG_SIZE_MASK,
+ frag_size_rx);
+ } else {
+ mask = GDM4_SGMII0_RX_FRAG_SIZE_MASK;
+ val = FIELD_PREP(GDM4_SGMII0_RX_FRAG_SIZE_MASK,
+ frag_size_rx);
+ }
+ airoha_fe_rmw(eth, REG_FE_GDM4_RMBI_FRAG, mask, val);
+
+ spin_unlock(&port->lock);
+}
+
+/* Nothing to do in MAC, everything is handled in PCS */
+static void airoha_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+}
+
+static const struct phylink_mac_ops airoha_phylink_ops = {
+ .mac_config = airoha_mac_config,
+ .mac_link_up = airoha_mac_link_up,
+ .mac_link_down = airoha_mac_link_down,
+};
+
+static int airoha_fill_available_pcs(struct phylink_config *config,
+ struct phylink_pcs **available_pcs,
+ unsigned int num_possible_pcs)
+{
+ struct device *dev = config->dev;
+
+ return fwnode_phylink_pcs_parse(dev_fwnode(dev), available_pcs,
+ &num_possible_pcs);
+}
+
+static int airoha_setup_phylink(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct device_node *np = netdev->dev.of_node;
+ struct airoha_gdm_port *port = dev->port;
+ struct phylink_config *config;
+ phy_interface_t phy_mode;
+ struct phylink *phylink;
+ int err;
+
+ err = of_get_phy_mode(np, &phy_mode);
+ if (err) {
+ dev_err(&netdev->dev, "incorrect phy-mode\n");
+ return err;
+ }
+
+ config = &dev->phylink_config;
+ config->dev = &netdev->dev;
+ config->type = PHYLINK_NETDEV;
+
+ /*
+ * GDM1 only supports internal for Embedded Switch
+ * and doesn't require a PCS.
+ */
+ if (port->id == AIROHA_GDM1_IDX) {
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10000FD;
+
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ config->supported_interfaces);
+ } else {
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000 |
+ MAC_2500FD | MAC_5000FD | MAC_10000FD;
+
+ err = fwnode_phylink_pcs_parse(dev_fwnode(config->dev), NULL,
+ &dev->phylink_config.num_available_pcs);
+ if (err)
+ return err;
+
+ config->fill_available_pcs = airoha_fill_available_pcs;
+
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_USXGMII,
+ config->supported_interfaces);
+
+ phy_interface_copy(config->pcs_interfaces,
+ config->supported_interfaces);
+ }
+
+ phylink = phylink_create(config, of_fwnode_handle(np),
+ phy_mode, &airoha_phylink_ops);
+ if (IS_ERR(phylink))
+ return PTR_ERR(phylink);
+
+ dev->phylink = phylink;
+
+ return 0;
+}
+
static int airoha_alloc_gdm_device(struct airoha_eth *eth,
struct airoha_gdm_port *port,
int nbq, struct device_node *np)
@@ -3238,7 +3422,7 @@ static int airoha_alloc_gdm_device(struc
dev->nbq = nbq;
port->devs[index] = dev;
- return 0;
+ return airoha_setup_phylink(netdev);
}
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
@@ -3273,7 +3457,7 @@ static int airoha_alloc_gdm_port(struct
return -ENOMEM;
port->id = id;
- spin_lock_init(&port->stats_lock);
+ spin_lock_init(&port->lock);
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
@@ -3464,6 +3648,8 @@ error_napi_stop:
netdev = netdev_from_priv(dev);
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
+ if (dev->phylink)
+ phylink_destroy(dev->phylink);
of_node_put(netdev->dev.of_node);
}
airoha_metadata_dst_free(port);
@@ -3500,6 +3686,7 @@ static void airoha_remove(struct platfor
netdev = netdev_from_priv(dev);
unregister_netdev(netdev);
+ phylink_destroy(dev->phylink);
of_node_put(netdev->dev.of_node);
}
airoha_metadata_dst_free(port);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -554,6 +554,9 @@ struct airoha_gdm_dev {
int nbq;
struct airoha_hw_stats stats;
+
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
};
struct airoha_gdm_port {
@@ -561,8 +564,8 @@ struct airoha_gdm_port {
int id;
int users;
- /* protect concurrent hw_stats accesses */
- spinlock_t stats_lock;
+ /* protect concurrent hw_stats and frag register accesses */
+ spinlock_t lock;
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -358,6 +358,18 @@
#define IP_FRAGMENT_PORT_MASK GENMASK(8, 5)
#define IP_FRAGMENT_NBQ_MASK GENMASK(4, 0)
+#define REG_FE_GDM4_TMBI_FRAG 0x2028
+#define GDM4_SGMII1_TX_WEIGHT_MASK GENMASK(31, 26)
+#define GDM4_SGMII1_TX_FRAG_SIZE_MASK GENMASK(25, 16)
+#define GDM4_SGMII0_TX_WEIGHT_MASK GENMASK(15, 10)
+#define GDM4_SGMII0_TX_FRAG_SIZE_MASK GENMASK(9, 0)
+
+#define REG_FE_GDM4_RMBI_FRAG 0x202c
+#define GDM4_SGMII1_RX_WEIGHT_MASK GENMASK(31, 26)
+#define GDM4_SGMII1_RX_FRAG_SIZE_MASK GENMASK(25, 16)
+#define GDM4_SGMII0_RX_WEIGHT_MASK GENMASK(15, 10)
+#define GDM4_SGMII0_RX_FRAG_SIZE_MASK GENMASK(9, 0)
+
#define REG_MC_VLAN_EN 0x2100
#define MC_VLAN_EN_MASK BIT(0)
@@ -1,26 +0,0 @@
From 6548e580509397a622b7c504a79de93414771459 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Wed, 25 Jun 2025 00:04:36 +0200
Subject: [PATCH 6/6] net: ethernet: airoha: define sport value for GDM3
On Airoha AN7583, the Serdes Ethernet goes through the GDM3 port.
To correctly receive packet for QDMA, add the sport value to identify
packet from GDM3 port.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -585,6 +585,9 @@ static int airoha_qdma_get_gdm_port(stru
case 0x18:
port = 3; /* GDM4 */
break;
+ case 0x16:
+ port = 2; /* GDM3 */
+ break;
case 0x10 ... 0x14:
port = 0; /* GDM1 */
break;
@@ -1,145 +0,0 @@
From 843e2892f2d9353bf039e0dfb5442a600e75009e Mon Sep 17 00:00:00 2001
From: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
Date: Thu, 9 Oct 2025 23:46:08 +0300
Subject: [PATCH] net: airoha: disable external phy code if PCS_AIROHA is not
enabled
External phy code breaks building for EN7523, so disable it if
PCS_AIROHA is not selected.
Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu>
---
drivers/net/ethernet/airoha/airoha_eth.c | 16 ++++++++++++++++
drivers/net/ethernet/airoha/airoha_eth.h | 2 ++
2 files changed, 18 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -72,10 +72,12 @@ static void airoha_qdma_irq_disable(stru
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
+#if defined(CONFIG_PCS_AIROHA)
static bool airhoa_is_phy_external(struct airoha_gdm_port *port)
{
return port->id != 1;
}
+#endif
static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
{
@@ -1725,6 +1727,7 @@ static int airoha_dev_open(struct net_de
struct airoha_qdma *qdma = port->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
if (err) {
@@ -1735,6 +1738,7 @@ static int airoha_dev_open(struct net_de
phylink_start(port->phylink);
}
+#endif
netif_tx_start_all_queues(dev);
err = airoha_set_vip_for_gdm_port(port, true);
@@ -1797,10 +1801,12 @@ static int airoha_dev_stop(struct net_de
}
}
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_stop(port->phylink);
phylink_disconnect_phy(port->phylink);
}
+#endif
return 0;
}
@@ -2939,6 +2945,7 @@ static const struct ethtool_ops airoha_e
.get_link = ethtool_op_get_link,
};
+#if defined(CONFIG_PCS_AIROHA)
static struct phylink_pcs *airoha_phylink_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
@@ -2952,6 +2959,7 @@ static void airoha_mac_config(struct phy
const struct phylink_link_state *state)
{
}
+#endif
static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
{
@@ -2997,6 +3005,7 @@ bool airoha_is_valid_gdm_port(struct air
return false;
}
+#if defined(CONFIG_PCS_AIROHA)
static void airoha_mac_link_up(struct phylink_config *config, struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex, bool tx_pause, bool rx_pause)
@@ -3102,6 +3109,7 @@ static int airoha_setup_phylink(struct n
return 0;
}
+#endif
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
@@ -3176,11 +3184,13 @@ static int airoha_alloc_gdm_port(struct
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
err = airoha_setup_phylink(dev);
if (err)
return err;
}
+#endif
return airoha_metadata_dst_alloc(port);
}
@@ -3310,10 +3320,12 @@ error_napi_stop:
if (port->dev->reg_state == NETREG_REGISTERED)
unregister_netdev(port->dev);
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_destroy(port->phylink);
airoha_pcs_destroy(port->pcs);
}
+#endif
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3328,10 +3342,12 @@ static void airoha_remove(struct platfor
unregister_netdev(port->dev);
airoha_metadata_dst_free(port);
+#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_destroy(port->phylink);
airoha_pcs_destroy(port->pcs);
}
+#endif
}
airoha_hw_cleanup(eth);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -542,9 +542,11 @@ struct airoha_gdm_port {
int id;
int nbq;
+#if defined(CONFIG_PCS_AIROHA)
struct phylink *phylink;
struct phylink_config phylink_config;
struct phylink_pcs *pcs;
+#endif
struct airoha_hw_stats stats;
@@ -24,7 +24,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#include <uapi/linux/ppp_defs.h>
#include "airoha_regs.h"
@@ -439,6 +441,73 @@ static void airoha_fe_crsn_qsel_init(str
@@ -487,6 +489,73 @@ static void airoha_fe_crsn_qsel_init(str
CDM_CRSN_QSEL_Q1));
}
@@ -70,9 +70,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ LRO_RXQ_EN_MASK);
+}
+
+static void airoha_dev_lro_enable(struct airoha_gdm_port *port)
+static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
+{
+ struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - &eth->qdma[0];
+ int i, lro_queue_index = 0;
@@ -98,7 +98,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static int airoha_fe_init(struct airoha_eth *eth)
{
airoha_fe_maccr_init(eth);
@@ -558,6 +627,7 @@ static int airoha_qdma_fill_rx_queue(str
@@ -614,6 +683,7 @@ static int airoha_qdma_fill_rx_queue(str
e->dma_addr = page_pool_get_dma_addr(page) + offset;
e->dma_len = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
@@ -106,8 +106,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len);
WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr));
@@ -603,12 +673,169 @@ static int airoha_qdma_get_gdm_port(stru
return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
@@ -655,12 +725,169 @@ airoha_qdma_get_gdm_dev(struct airoha_et
return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
}
+static struct sk_buff *airoha_qdma_lro_rx_skb(struct airoha_queue *q,
@@ -279,27 +279,27 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
int done = 0;
while (done < budget) {
@@ -643,18 +870,9 @@ static int airoha_qdma_rx_process(struct
@@ -696,18 +923,9 @@ static int airoha_qdma_rx_process(struct
port = eth->ports[p];
netdev = netdev_from_priv(dev);
if (!q->skb) { /* first buffer */
- q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
- q->buf_size);
+ q->skb = airoha_qdma_build_rx_skb(q, desc, e, port->dev);
+ q->skb = airoha_qdma_build_rx_skb(q, desc, e, netdev);
if (!q->skb)
goto free_frag;
-
- skb_reserve(q->skb, AIROHA_RX_HEADROOM);
- __skb_put(q->skb, len);
- skb_mark_for_recycle(q->skb);
- q->skb->dev = port->dev;
- q->skb->protocol = eth_type_trans(q->skb, port->dev);
- q->skb->dev = netdev;
- q->skb->protocol = eth_type_trans(q->skb, netdev);
- q->skb->ip_summed = CHECKSUM_UNNECESSARY;
- skb_record_rx_queue(q->skb, qid);
} else { /* scattered frame */
struct skb_shared_info *shinfo = skb_shinfo(q->skb);
int nr_frags = shinfo->nr_frags;
@@ -743,12 +961,10 @@ static int airoha_qdma_rx_napi_poll(stru
@@ -798,12 +1016,10 @@ static int airoha_qdma_rx_napi_poll(stru
static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
struct airoha_qdma *qdma, int ndesc)
{
@@ -313,7 +313,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
.nid = NUMA_NO_NODE,
.dev = qdma->eth->dev,
.napi = &q->napi,
@@ -756,9 +972,10 @@ static int airoha_qdma_init_rx_queue(str
@@ -811,9 +1027,10 @@ static int airoha_qdma_init_rx_queue(str
struct airoha_eth *eth = qdma->eth;
int qid = q - &qdma->q_rx[0], thr;
dma_addr_t dma_addr;
@@ -325,7 +325,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
GFP_KERNEL);
@@ -770,6 +987,9 @@ static int airoha_qdma_init_rx_queue(str
@@ -825,6 +1042,9 @@ static int airoha_qdma_init_rx_queue(str
if (!q->desc)
return -ENOMEM;
@@ -335,7 +335,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(q->page_pool)) {
int err = PTR_ERR(q->page_pool);
@@ -778,6 +998,7 @@ static int airoha_qdma_init_rx_queue(str
@@ -833,6 +1053,7 @@ static int airoha_qdma_init_rx_queue(str
return err;
}
@@ -343,7 +343,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
q->ndesc = ndesc;
netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
@@ -791,7 +1012,12 @@ static int airoha_qdma_init_rx_queue(str
@@ -846,7 +1067,12 @@ static int airoha_qdma_init_rx_queue(str
FIELD_PREP(RX_RING_THR_MASK, thr));
airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
@@ -357,7 +357,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
airoha_qdma_fill_rx_queue(q);
@@ -813,6 +1039,7 @@ static void airoha_qdma_cleanup_rx_queue
@@ -868,6 +1094,7 @@ static void airoha_qdma_cleanup_rx_queue
page_pool_get_dma_dir(q->page_pool));
page_pool_put_full_page(q->page_pool, page, false);
/* Reset DMA descriptor */
@@ -365,56 +365,67 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
WRITE_ONCE(desc->ctrl, 0);
WRITE_ONCE(desc->addr, 0);
WRITE_ONCE(desc->data, 0);
@@ -1720,13 +1947,45 @@ static void airoha_update_hw_stats(struc
spin_unlock(&port->stats.lock);
@@ -1809,6 +2036,37 @@ static void airoha_update_hw_stats(struc
spin_unlock(&port->lock);
}
+static void airoha_update_netdev_features(struct airoha_gdm_port *port)
+static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
+{
+ struct airoha_eth *eth = port->eth;
+ struct airoha_eth *eth = dev->eth;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *p = eth->ports[i];
+ struct net_device *netdev;
+ struct airoha_gdm_port *port = dev->port;
+ int j;
+
+ if (!p)
+ if (!port)
+ continue;
+
+ netdev = p->dev;
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *iter_dev = port->devs[j];
+ struct net_device *netdev;
+
+ netdev_update_features(netdev);
+ if (!iter_dev || iter_dev == dev)
+ continue;
+
+ if (iter_dev->qdma != dev->qdma)
+ continue;
+
+ netdev = netdev_from_priv(iter_dev);
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
+
+ netdev_update_features(netdev);
+ }
+ }
+}
+
static int airoha_dev_open(struct net_device *dev)
static int airoha_dev_open(struct net_device *netdev)
{
int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
@@ -1816,6 +2074,18 @@ static int airoha_dev_open(struct net_de
struct airoha_gdm_port *port = dev->port;
u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - &eth->qdma[0];
u32 pse_port = FE_PSE_PORT_PPE1;
+
+ /* HW LRO is configured on the QDMA and it is shared between
+ * all the devices using it. Refuse to open a second device on
+ * the same QDMA if LRO is enabled on any device sharing it.
+ */
+ if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
+ netdev_warn(dev, "required to disable HW GRO on QDMA%d\n",
+ netdev_warn(netdev, "required to disable HW GRO on QDMA%d\n",
+ qdma_id);
+ return -EBUSY;
+ }
+
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
@@ -1747,13 +2006,13 @@ static int airoha_dev_open(struct net_de
err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
if (err) {
@@ -1832,13 +2102,13 @@ static int airoha_dev_open(struct net_de
/* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
if (netdev_uses_dsa(dev) || port->id > 2)
if (netdev_uses_dsa(netdev) || port->id > 2)
- airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+ airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
@@ -423,68 +434,77 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ airoha_fe_rmw(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));
@@ -1763,14 +2022,18 @@ static int airoha_dev_open(struct net_de
GLOBAL_CFG_RX_DMA_EN_MASK);
- cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ cur_len = airoha_fe_get(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
@@ -1846,7 +2116,7 @@ static int airoha_dev_open(struct net_de
* multiple net_devices with different MTUs to share the same
* GDM port.
*/
- airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+ airoha_fe_rmw(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));
@@ -1859,11 +2129,16 @@ static int airoha_dev_open(struct net_de
qdma->users++;
- if (port->id == AIROHA_GDM2_IDX &&
- airoha_ppe_is_enabled(qdma->eth, 1)) {
+ if (port->id == AIROHA_GDM2_IDX && airoha_ppe_is_enabled(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
if (!airoha_is_lan_gdm_dev(dev) &&
- airoha_ppe_is_enabled(qdma->eth, 1))
+ airoha_ppe_is_enabled(eth, 1))
pse_port = FE_PSE_PORT_PPE2;
}
- airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
pse_port);
+ if (dev->features & NETIF_F_GRO_HW)
+ airoha_dev_lro_enable(port);
+ if (netdev->features & NETIF_F_GRO_HW)
+ airoha_dev_lro_enable(dev);
+
+ airoha_update_netdev_features(port);
+ airoha_update_netdev_features(dev);
+
return 0;
}
@@ -1778,6 +2041,7 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
@@ -1896,6 +2171,7 @@ static int airoha_dev_stop(struct net_de
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
int i;
netif_tx_disable(dev);
@@ -1785,7 +2049,7 @@ static int airoha_dev_stop(struct net_de
for (i = 0; i < dev->num_tx_queues; i++)
netdev_tx_reset_subqueue(dev, i);
netif_tx_disable(netdev);
@@ -1904,9 +2180,9 @@ static int airoha_dev_stop(struct net_de
netdev_tx_reset_subqueue(netdev, i);
- airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
if (--port->users)
- airoha_set_port_mtu(dev->eth, port);
+ airoha_set_port_mtu(eth, port);
else
- airoha_set_gdm_port_fwd_cfg(qdma->eth,
+ airoha_set_gdm_port_fwd_cfg(eth,
REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
if (!--qdma->users) {
@@ -1801,6 +2065,8 @@ static int airoha_dev_stop(struct net_de
@@ -1923,6 +2199,8 @@ static int airoha_dev_stop(struct net_de
}
}
+ airoha_update_netdev_features(port);
+ airoha_update_netdev_features(dev);
+
#if defined(CONFIG_PCS_AIROHA)
if (airhoa_is_phy_external(port)) {
phylink_stop(port->phylink);
@@ -2034,6 +2300,38 @@ int airoha_get_fe_port(struct airoha_gdm
phylink_stop(dev->phylink);
phylink_disconnect_phy(dev->phylink);
@@ -2195,6 +2473,38 @@ int airoha_get_fe_port(struct airoha_gdm
}
}
+static netdev_features_t airoha_dev_fix_features(struct net_device *dev,
+static netdev_features_t airoha_dev_fix_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_qdma *qdma = port->qdma;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+
+ if (qdma->users > 1)
+ features &= ~NETIF_F_GRO_HW;
@@ -492,12 +512,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ return features;
+}
+
+static int airoha_dev_set_features(struct net_device *dev,
+static int airoha_dev_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ netdev_features_t diff = dev->features ^ features;
+ struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_qdma *qdma = port->qdma;
+ netdev_features_t diff = netdev->features ^ features;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - &eth->qdma[0];
+
@@ -505,7 +525,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ return 0;
+
+ if (features & NETIF_F_GRO_HW)
+ airoha_dev_lro_enable(port);
+ airoha_dev_lro_enable(dev);
+ else
+ airoha_fe_lro_disable(eth, qdma_id);
+
@@ -513,9 +533,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+}
+
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *dev)
struct net_device *netdev)
{
@@ -2933,6 +3231,8 @@ static const struct net_device_ops airoh
@@ -3139,6 +3449,8 @@ static const struct net_device_ops airoh
.ndo_stop = airoha_dev_stop,
.ndo_change_mtu = airoha_dev_change_mtu,
.ndo_select_queue = airoha_dev_select_queue,
@@ -524,25 +544,24 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
.ndo_start_xmit = airoha_dev_xmit,
.ndo_get_stats64 = airoha_dev_get_stats64,
.ndo_set_mac_address = airoha_dev_set_macaddr,
@@ -3150,12 +3450,9 @@ static int airoha_alloc_gdm_port(struct
dev->ethtool_ops = &airoha_ethtool_ops;
dev->max_mtu = AIROHA_MAX_MTU;
dev->watchdog_timeo = 5 * HZ;
- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
- NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
- NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HW_TC;
- dev->features |= dev->hw_features;
- dev->vlan_features = dev->hw_features;
+ dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
+ dev->features |= AIROHA_HW_FEATURES;
+ dev->vlan_features = AIROHA_HW_FEATURES;
dev->dev.of_node = np;
SET_NETDEV_DEV(dev, eth->dev);
@@ -3380,11 +3692,9 @@ static int airoha_alloc_gdm_device(struc
netdev->ethtool_ops = &airoha_ethtool_ops;
netdev->max_mtu = AIROHA_MAX_MTU;
netdev->watchdog_timeo = 5 * HZ;
- netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 |
- NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HW_TC;
- netdev->features |= netdev->hw_features;
- netdev->vlan_features = netdev->hw_features;
+ netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
+ netdev->features |= AIROHA_HW_FEATURES;
+ netdev->vlan_features = AIROHA_HW_FEATURES;
SET_NETDEV_DEV(netdev, eth->dev);
/* reserve hw queues for HTB offloading */
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -43,6 +43,18 @@
@@ -44,6 +44,18 @@
(_n) == 15 ? 128 : \
(_n) == 0 ? 1024 : 16)
@@ -561,7 +580,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#define PSE_RSV_PAGES 128
#define PSE_QUEUE_RSV_PAGES 64
@@ -666,6 +678,18 @@ static inline bool airoha_is_7583(struct
@@ -676,6 +688,18 @@ static inline bool airoha_is_7583(struct
return eth->soc->version == 0x7583;
}
@@ -577,9 +596,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
+}
+
int airoha_get_fe_port(struct airoha_gdm_port *port);
bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
struct airoha_gdm_port *port);
int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -122,6 +122,20 @@
@@ -1,37 +0,0 @@
From 917f959d54efd09719bd5047aa4b9543615e131e Mon Sep 17 00:00:00 2001
Message-ID: <917f959d54efd09719bd5047aa4b9543615e131e.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Fri, 19 Dec 2025 23:12:32 +0100
Subject: [PATCH 10/13] net: airoha: Rename airoha_set_gdm2_loopback in
airoha_enable_gdm2_loopback
This is a preliminary patch in order to allow the user to select if the
configured device will be used as hw lan or wan.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2213,7 +2213,7 @@ static int airoha_dev_set_macaddr(struct
return 0;
}
-static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
+static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
@@ -2346,7 +2346,7 @@ static int airoha_dev_init(struct net_de
(port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
int err;
- err = airoha_set_gdm2_loopback(dev);
+ err = airoha_enable_gdm2_loopback(dev);
if (err)
return err;
}
@@ -1,668 +0,0 @@
From e928621a0bbd010b75624c77105ce31f2b8d2faf Mon Sep 17 00:00:00 2001
Message-ID: <e928621a0bbd010b75624c77105ce31f2b8d2faf.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 13 Dec 2025 09:45:09 +0100
Subject: [PATCH 12/13] net: airoha: Add ethtool priv_flags callbacks
Introduce ethtool priv_flags callbacks in order to allow the user to
select if the configured device will be used as hw lan or wan and enable
or disable gdm2 loopback (used for hw QoS).
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 173 ++++++++++++++++++++++
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
2 files changed, 174 insertions(+)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -540,7 +540,7 @@ static bool airoha_fe_lro_is_enabled(str
static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
{
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
int i, lro_queue_index = 0;
@@ -1158,7 +1158,7 @@ static void airoha_qdma_wake_netdev_txqs
if (!dev)
continue;
- if (dev->qdma != qdma)
+ if (rcu_access_pointer(dev->qdma) != qdma)
continue;
netdev = netdev_from_priv(dev);
@@ -2031,7 +2031,8 @@ static void airoha_update_netdev_feature
if (!iter_dev || iter_dev == dev)
continue;
- if (iter_dev->qdma != dev->qdma)
+ if (rcu_access_pointer(iter_dev->qdma) !=
+ rcu_access_pointer(dev->qdma))
continue;
netdev = netdev_from_priv(iter_dev);
@@ -2043,20 +2044,57 @@ static void airoha_update_netdev_feature
}
}
+static void airoha_qdma_start(struct airoha_qdma *qdma)
+{
+ airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
+ GLOBAL_CFG_TX_DMA_EN_MASK |
+ GLOBAL_CFG_RX_DMA_EN_MASK);
+ qdma->users++;
+}
+
+static void airoha_qdma_stop(struct airoha_qdma *qdma)
+{
+ u32 status;
+
+ if (--qdma->users)
+ return;
+
+ airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
+ GLOBAL_CFG_TX_DMA_EN_MASK |
+ GLOBAL_CFG_RX_DMA_EN_MASK);
+
+ if (read_poll_timeout(airoha_qdma_rr, status,
+ !(status & (GLOBAL_CFG_TX_DMA_BUSY_MASK |
+ GLOBAL_CFG_RX_DMA_BUSY_MASK)),
+ USEC_PER_MSEC, 50 * USEC_PER_MSEC, true,
+ qdma, REG_QDMA_GLOBAL_CFG))
+ dev_warn(qdma->eth->dev, "QDMA DMA engine busy timeout\n");
+
+ for (int i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
+ if (!qdma->q_tx[i].ndesc)
+ continue;
+
+ airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
+ }
+}
+
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;
- struct airoha_eth *eth = qdma->eth;
- int qdma_id = qdma - &eth->qdma[0];
+ struct airoha_eth *eth = dev->eth;
+ struct airoha_qdma *qdma;
+ int qdma_id;
/* HW LRO is configured on the QDMA and it is shared between
* all the devices using it. Refuse to open a second device on
* the same QDMA if LRO is enabled on any device sharing it.
*/
+ qdma = airoha_qdma_deref(dev);
+ qdma_id = qdma - &eth->qdma[0];
+
if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
netdev_warn(netdev, "required to disable HW GRO on QDMA%d\n",
qdma_id);
@@ -2105,10 +2143,7 @@ static int airoha_dev_open(struct net_de
}
port->users++;
- airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
- GLOBAL_CFG_TX_DMA_EN_MASK |
- GLOBAL_CFG_RX_DMA_EN_MASK);
- qdma->users++;
+ airoha_qdma_start(qdma);
if (!airoha_is_lan_gdm_dev(dev) &&
airoha_ppe_is_enabled(eth, 1)) {
@@ -2154,8 +2189,7 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = dev->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_qdma *qdma;
int i;
netif_tx_disable(netdev);
@@ -2163,25 +2197,14 @@ static int airoha_dev_stop(struct net_de
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
+ qdma = airoha_qdma_deref(dev);
if (--port->users)
- airoha_set_port_mtu(eth, port);
+ airoha_set_port_mtu(dev->eth, port);
else
- airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+ airoha_set_gdm_port_fwd_cfg(qdma->eth,
+ REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
-
- if (!--qdma->users) {
- airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
- GLOBAL_CFG_TX_DMA_EN_MASK |
- GLOBAL_CFG_RX_DMA_EN_MASK);
-
- for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
- if (!qdma->q_tx[i].ndesc)
- continue;
-
- airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
- }
- }
-
+ airoha_qdma_stop(qdma);
airoha_update_netdev_features(dev);
#if defined(CONFIG_PCS_AIROHA)
@@ -2275,6 +2298,53 @@ static int airoha_enable_gdm2_loopback(s
return 0;
}
+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ int i, src_port;
+ u32 pse_port;
+
+ src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
+ if (src_port < 0)
+ return src_port;
+
+ airoha_fe_clear(eth,
+ REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
+ SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
+
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ GDM_STRIP_CRC_MASK);
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ FE_PSE_PORT_DROP);
+ airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
+ LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
+ pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+ : FE_PSE_PORT_PPE1;
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ pse_port);
+
+ airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
+ FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
+
+ for (i = 0; i < eth->soc->num_ppe; i++)
+ airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
+ DFT_CPORT_MASK(AIROHA_GDM2_IDX));
+
+ /* Enable VIP and IFC for GDM2 */
+ airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+ airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
+
+ if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
+
+ airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
+ FC_MAP6_DEF_VALUE & mask);
+ }
+
+ return 0;
+}
+
static struct airoha_gdm_dev *
airoha_get_wan_gdm_dev(struct airoha_eth *eth)
{
@@ -2301,15 +2371,30 @@ airoha_get_wan_gdm_dev(struct airoha_eth
static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
{
struct net_device *netdev = netdev_from_priv(dev);
+ struct airoha_qdma *cur_qdma, *qdma;
struct airoha_eth *eth = dev->eth;
int ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
- netdev->irq = dev->qdma->irq_banks[0].irq;
+ qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
+ cur_qdma = airoha_qdma_deref(dev);
+ if (netif_running(netdev))
+ airoha_qdma_start(qdma);
+
+ rcu_assign_pointer(dev->qdma, qdma);
+ netdev->irq = qdma->irq_banks[0].irq;
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));
+
+ if (!cur_qdma)
+ return;
+
+ synchronize_rcu();
+ netif_tx_wake_all_queues(netdev);
+
+ if (netif_running(netdev))
+ airoha_qdma_stop(cur_qdma);
}
static int airoha_dev_init(struct net_device *netdev)
@@ -2473,8 +2558,9 @@ static netdev_features_t airoha_dev_fix_
netdev_features_t features)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
+ qdma = airoha_qdma_deref(dev);
if (qdma->users > 1)
features &= ~NETIF_F_GRO_HW;
@@ -2486,9 +2572,8 @@ static int airoha_dev_set_features(struc
{
netdev_features_t diff = netdev->features ^ features;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
- struct airoha_eth *eth = qdma->eth;
- int qdma_id = qdma - &eth->qdma[0];
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+ int qdma_id = qdma - &dev->eth->qdma[0];
if (!(diff & NETIF_F_GRO_HW))
return 0;
@@ -2496,7 +2581,7 @@ static int airoha_dev_set_features(struc
if (features & NETIF_F_GRO_HW)
airoha_dev_lro_enable(dev);
else
- airoha_fe_lro_disable(eth, qdma_id);
+ airoha_fe_lro_disable(dev->eth, qdma_id);
return 0;
}
@@ -2505,9 +2590,9 @@ static netdev_tx_t airoha_dev_xmit(struc
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
+ struct airoha_qdma *qdma;
struct netdev_queue *txq;
struct airoha_queue *q;
LIST_HEAD(tx_list);
@@ -2516,6 +2601,8 @@ static netdev_tx_t airoha_dev_xmit(struc
u16 index;
u8 fport;
+ rcu_read_lock();
+ qdma = rcu_dereference(dev->qdma);
qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
tag = airoha_get_dsa_tag(skb, netdev);
@@ -2562,6 +2649,8 @@ static netdev_tx_t airoha_dev_xmit(struc
netif_tx_stop_queue(txq);
q->txq_stopped = true;
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
+
return NETDEV_TX_BUSY;
}
@@ -2624,6 +2713,7 @@ static netdev_tx_t airoha_dev_xmit(struc
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
return NETDEV_TX_OK;
@@ -2639,6 +2729,7 @@ error_unmap:
error:
dev_kfree_skb_any(skb);
netdev->stats.tx_dropped++;
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
@@ -2720,17 +2811,19 @@ static int airoha_qdma_set_chan_tx_sched
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma;
int i;
+ qdma = airoha_qdma_deref(dev);
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
- airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
+ airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
for (i = 0; i < n_weights; i++) {
u32 status;
int err;
- airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
+ airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG,
TWRR_RW_CMD_MASK |
FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2738,12 +2831,12 @@ static int airoha_qdma_set_chan_tx_sched
err = read_poll_timeout(airoha_qdma_rr, status,
status & TWRR_RW_CMD_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC,
- true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
+ true, qdma, REG_TXWRR_WEIGHT_CFG);
if (err)
return err;
}
- airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+ airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
@@ -2807,13 +2900,15 @@ static int airoha_qdma_get_tx_ets_stats(
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ u64 cpu_tx_packets, fwd_tx_packets, tx_packets;
+ struct airoha_qdma *qdma;
- u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(qdma,
- REG_CNTR_VAL((channel << 1) + 1));
- u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
- (fwd_tx_packets - dev->fwd_tx_packets);
+ qdma = airoha_qdma_deref(dev);
+ cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+ fwd_tx_packets = airoha_qdma_rr(qdma,
+ REG_CNTR_VAL((channel << 1) + 1));
+ tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
+ (fwd_tx_packets - dev->fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
dev->cpu_tx_packets = cpu_tx_packets;
@@ -3073,16 +3168,18 @@ static int airoha_qdma_set_tx_rate_limit
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma;
int i, err;
+ qdma = airoha_qdma_deref(dev);
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
- err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
+ err = airoha_qdma_set_trtcm_config(qdma, channel,
REG_EGRESS_TRTCM_CFG, i,
!!rate, TRTCM_METER_MODE);
if (err)
return err;
- err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
+ err = airoha_qdma_set_trtcm_token_bucket(qdma, channel,
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
@@ -3118,11 +3215,12 @@ static int airoha_tc_htb_alloc_leaf_queu
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
int err, num_tx_queues = netdev->real_num_tx_queues;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
/* Here we need to check the requested QDMA channel is not already
* in use by another net_device running on the same QDMA block.
*/
+ qdma = airoha_qdma_deref(dev);
if (test_and_set_bit(channel, qdma->qos_channel_map)) {
NL_SET_ERR_MSG_MOD(opt->extack,
"qdma qos channel already in use");
@@ -3156,7 +3254,7 @@ static int airoha_qdma_set_rx_meter(stru
u32 rate, u32 bucket_size,
enum trtcm_unit_type unit_type)
{
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -3330,11 +3428,12 @@ static int airoha_dev_setup_tc_block(str
static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
+ qdma = airoha_qdma_deref(dev);
clear_bit(queue, qdma->qos_channel_map);
clear_bit(queue, dev->qos_sq_bmap);
}
@@ -3355,6 +3454,95 @@ static int airoha_tc_htb_delete_leaf_que
return 0;
}
+static void airoha_disable_qos_for_gdm34(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ int err;
+
+ if (port->id != AIROHA_GDM3_IDX &&
+ port->id != AIROHA_GDM4_IDX)
+ return;
+
+ err = airoha_disable_gdm2_loopback(dev);
+ if (err)
+ netdev_warn(netdev,
+ "failed disabling GDM2 loopback: %d\n", err);
+
+ dev->flags &= ~AIROHA_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+
+ airoha_set_macaddr(dev, netdev->dev_addr);
+ if (netif_running(netdev))
+ airoha_set_gdm_port_fwd_cfg(dev->eth,
+ REG_GDM_FWD_CFG(port->id),
+ FE_PSE_PORT_PPE1);
+}
+
+static int airoha_enable_qos_for_gdm34(struct net_device *netdev,
+ struct netlink_ext_ack *extack)
+{
+ struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ int err = -EBUSY;
+
+ if (port->id != AIROHA_GDM3_IDX &&
+ port->id != AIROHA_GDM4_IDX) {
+ /* HW QoS is always supported by GDM1 and GDM2 */
+ return 0;
+ }
+
+ if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */
+ return 0;
+
+ mutex_lock(&flow_offload_mutex);
+
+ wan_dev = airoha_get_wan_gdm_dev(eth);
+ if (wan_dev) {
+ if ((wan_dev->flags & AIROHA_PRIV_F_QOS) ||
+ wan_dev->port->id == AIROHA_GDM2_IDX) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "QoS configured for WAN device");
+ goto error_unlock;
+ }
+ airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
+ }
+
+ dev->flags |= AIROHA_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+ err = airoha_enable_gdm2_loopback(dev);
+ if (err)
+ goto error_disable_wan;
+
+ err = airoha_set_macaddr(dev, netdev->dev_addr);
+ if (err)
+ goto error_disable_loopback;
+
+ if (netif_running(netdev)) {
+ u32 pse_port;
+
+ pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+ : FE_PSE_PORT_PPE1;
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+ pse_port);
+ }
+
+ mutex_unlock(&flow_offload_mutex);
+
+ return 0;
+
+error_disable_loopback:
+ airoha_disable_gdm2_loopback(dev);
+error_disable_wan:
+ dev->flags &= ~AIROHA_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+error_unlock:
+ mutex_unlock(&flow_offload_mutex);
+
+ return err;
+}
+
static int airoha_tc_htb_destroy(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -3363,6 +3551,8 @@ static int airoha_tc_htb_destroy(struct
for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
airoha_tc_remove_htb_queue(netdev, q);
+ dev->flags &= ~AIROHA_PRIV_F_QOS;
+
return 0;
}
@@ -3382,24 +3572,33 @@ static int airoha_tc_get_htb_get_leaf_qu
return 0;
}
-static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
+static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
switch (opt->command) {
- case TC_HTB_CREATE:
+ case TC_HTB_CREATE: {
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ int err;
+
+ err = airoha_enable_qos_for_gdm34(netdev, opt->extack);
+ if (err)
+ return err;
+
+ dev->flags |= AIROHA_PRIV_F_QOS;
break;
+ }
case TC_HTB_DESTROY:
- return airoha_tc_htb_destroy(dev);
+ return airoha_tc_htb_destroy(netdev);
case TC_HTB_NODE_MODIFY:
- return airoha_tc_htb_modify_queue(dev, opt);
+ return airoha_tc_htb_modify_queue(netdev, opt);
case TC_HTB_LEAF_ALLOC_QUEUE:
- return airoha_tc_htb_alloc_leaf_queue(dev, opt);
+ return airoha_tc_htb_alloc_leaf_queue(netdev, opt);
case TC_HTB_LEAF_DEL:
case TC_HTB_LEAF_DEL_LAST:
case TC_HTB_LEAF_DEL_LAST_FORCE:
- return airoha_tc_htb_delete_leaf_queue(dev, opt);
+ return airoha_tc_htb_delete_leaf_queue(netdev, opt);
case TC_HTB_LEAF_QUERY_QUEUE:
- return airoha_tc_get_htb_get_leaf_queue(dev, opt);
+ return airoha_tc_get_htb_get_leaf_queue(netdev, opt);
default:
return -EOPNOTSUPP;
}
@@ -3509,8 +3708,8 @@ static void airoha_mac_link_up(struct ph
{
struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
phylink_config);
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
u32 frag_size_tx, frag_size_rx;
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -402,6 +402,7 @@
#define REG_SRC_PORT_FC_MAP6 0x2298
#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
+#define FC_MAP6_DEF_VALUE 0x1b1a1918
#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -552,11 +552,12 @@ struct airoha_qdma {
enum airoha_priv_flags {
AIROHA_PRIV_F_WAN = BIT(0),
+ AIROHA_PRIV_F_QOS = BIT(1),
};
struct airoha_gdm_dev {
+ struct airoha_qdma __rcu *qdma;
struct airoha_gdm_port *port;
- struct airoha_qdma *qdma;
struct airoha_eth *eth;
#if defined(CONFIG_PCS_AIROHA)
@@ -705,6 +706,16 @@ int airoha_get_fe_port(struct airoha_gdm
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
+extern struct mutex flow_offload_mutex;
+
+static inline struct airoha_qdma *
+airoha_qdma_deref(struct airoha_gdm_dev *dev)
+{
+ return rcu_dereference_protected(dev->qdma,
+ lockdep_rtnl_is_held() ||
+ lockdep_is_held(&flow_offload_mutex));
+}
+
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -16,7 +16,10 @@
#include "airoha_regs.h"
#include "airoha_eth.h"
-static DEFINE_MUTEX(flow_offload_mutex);
+/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port
+ * configuration.
+ */
+DEFINE_MUTEX(flow_offload_mutex);
static DEFINE_SPINLOCK(ppe_lock);
static const struct rhashtable_params airoha_flow_table_params = {
@@ -87,8 +90,8 @@ static u32 airoha_ppe_get_timestamp(stru
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
{
- struct airoha_qdma *qdma = dev->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+ struct airoha_eth *eth = dev->eth;
u8 qdma_id = qdma - &eth->qdma[0];
u32 fe_cpu_port;
@@ -1,279 +0,0 @@
From 99e204d255c9b47a188dd79762b9406d9c5fed9b Mon Sep 17 00:00:00 2001
Message-ID: <99e204d255c9b47a188dd79762b9406d9c5fed9b.1779348625.git.lorenzo@kernel.org>
In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 20 May 2026 17:16:39 +0200
Subject: [PATCH 13/13] net: airoha: Rework MTU configuration
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 85 +++++++++++------------
drivers/net/ethernet/airoha/airoha_eth.h | 1 +
drivers/net/ethernet/airoha/airoha_ppe.c | 8 +--
drivers/net/ethernet/airoha/airoha_regs.h | 9 ++-
4 files changed, 49 insertions(+), 54 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -188,10 +188,15 @@ static void airoha_fe_maccr_init(struct
{
int p;
- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++)
+ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK |
GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK);
+ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
+ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+ FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_RX_SIZE));
+ }
airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK,
FIELD_PREP(CDM_VLAN_MASK, 0x8100));
@@ -2078,15 +2083,25 @@ static void airoha_qdma_stop(struct airo
}
}
+static void airoha_dev_set_mtu(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+
+ airoha_ppe_set_mtu(dev);
+ if (!airoha_is_lan_gdm_dev(dev))
+ airoha_fe_rmw(dev->eth, REG_WAN_MTU0,
+ WAN_MTU0_MASK,
+ FIELD_PREP(WAN_MTU0_MASK, netdev->mtu));
+}
+
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_eth *eth = dev->eth;
struct airoha_qdma *qdma;
- int qdma_id;
+ u32 pse_port = FE_PSE_PORT_PPE1;
+ int err, qdma_id;
/* HW LRO is configured on the QDMA and it is shared between
* all the devices using it. Refuse to open a second device on
@@ -2128,19 +2143,7 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
- cur_len = airoha_fe_get(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(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));
- }
+ airoha_dev_set_mtu(netdev);
port->users++;
airoha_qdma_start(qdma);
@@ -2161,30 +2164,6 @@ static int airoha_dev_open(struct net_de
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);
@@ -2199,7 +2178,7 @@ static int airoha_dev_stop(struct net_de
qdma = airoha_qdma_deref(dev);
if (--port->users)
- airoha_set_port_mtu(dev->eth, port);
+ airoha_ppe_set_mtu(dev);
else
airoha_set_gdm_port_fwd_cfg(qdma->eth,
REG_GDM_FWD_CFG(port->id),
@@ -2258,10 +2237,6 @@ static int airoha_enable_gdm2_loopback(s
FIELD_PREP(LPBK_CHAN_MASK, chan) |
LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK |
LBK_CHAN_MODE_MASK | LPBK_EN_MASK);
- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX),
- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
- FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
/* Forward the traffic to the proper GDM port */
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
@@ -2469,7 +2444,7 @@ static int airoha_dev_change_mtu(struct
WRITE_ONCE(netdev->mtu, mtu);
if (port->users)
- airoha_set_port_mtu(dev->eth, port);
+ airoha_dev_set_mtu(netdev);
return 0;
}
@@ -3471,6 +3446,7 @@ static void airoha_disable_qos_for_gdm34
dev->flags &= ~AIROHA_PRIV_F_WAN;
airoha_dev_set_qdma(dev);
+ airoha_dev_set_mtu(netdev);
airoha_set_macaddr(dev, netdev->dev_addr);
if (netif_running(netdev))
@@ -3519,6 +3495,7 @@ static int airoha_enable_qos_for_gdm34(s
if (err)
goto error_disable_loopback;
+ airoha_dev_set_mtu(netdev);
if (netif_running(netdev)) {
u32 pse_port;
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -23,6 +23,7 @@
#define AIROHA_MAX_DSA_PORTS 7
#define AIROHA_MAX_NUM_RSTS 3
#define AIROHA_MAX_MTU 9220
+#define AIROHA_MAX_RX_SIZE 16128
#define AIROHA_MAX_PACKET_SIZE 2048
#define AIROHA_NUM_QOS_CHANNELS 4
#define AIROHA_NUM_QOS_QUEUES 8
@@ -716,6 +717,7 @@ airoha_qdma_deref(struct airoha_gdm_dev
lockdep_is_held(&flow_offload_mutex));
}
+void airoha_ppe_set_mtu(struct airoha_gdm_dev *dev);
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -101,6 +101,39 @@ void airoha_ppe_set_cpu_port(struct airo
__field_prep(DFT_CPORT_MASK(fport), fe_cpu_port));
}
+void airoha_ppe_set_mtu(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ struct airoha_qdma *qdma;
+ int i, ppe_id, index;
+ u32 mtu = 0;
+
+ qdma = airoha_qdma_deref(dev);
+ for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
+ struct airoha_gdm_dev *d = port->devs[i];
+ struct net_device *netdev;
+ struct airoha_qdma *q;
+
+ if (!d)
+ continue;
+
+ q = airoha_qdma_deref(d);
+ if (q != qdma)
+ continue;
+
+ netdev = netdev_from_priv(d);
+ if (netif_running(netdev))
+ mtu = max_t(u32, mtu, netdev->mtu);
+ }
+
+ ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+ index = port->id == AIROHA_GDM4_IDX ? 7 : port->id;
+ airoha_fe_rmw(eth, REG_PPE_MTU(ppe_id, index),
+ FP_EGRESS_MTU_MASK(index),
+ __field_prep(FP_EGRESS_MTU_MASK(index), mtu));
+}
+
static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
{
u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries;
@@ -119,8 +152,6 @@ static void airoha_ppe_hw_init(struct ai
PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries);
for (i = 0; i < eth->soc->num_ppe; i++) {
- int p;
-
airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
ppe->foe_dma + sram_tb_size);
@@ -170,15 +201,6 @@ static void airoha_ppe_hw_init(struct ai
airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
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++)
- airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
- FP0_EGRESS_MTU_MASK |
- FP1_EGRESS_MTU_MASK,
- FIELD_PREP(FP0_EGRESS_MTU_MASK,
- AIROHA_MAX_MTU) |
- FIELD_PREP(FP1_EGRESS_MTU_MASK,
- AIROHA_MAX_MTU));
}
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
@@ -200,6 +222,7 @@ static void airoha_ppe_hw_init(struct ai
airoha_ppe_is_enabled(eth, 1);
fport = airoha_get_fe_port(dev);
airoha_ppe_set_cpu_port(dev, ppe_id, fport);
+ airoha_ppe_set_mtu(dev);
}
}
}
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -341,9 +341,8 @@
#define PPE_SRAM_TABLE_EN_MASK BIT(0)
#define REG_PPE_MTU_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304)
-#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + ((_n) << 2))
-#define FP1_EGRESS_MTU_MASK GENMASK(29, 16)
-#define FP0_EGRESS_MTU_MASK GENMASK(13, 0)
+#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + (((_n) / 2) << 2))
+#define FP_EGRESS_MTU_MASK(_n) GENMASK(13 + (((_n) % 2) << 4), ((_n) % 2) << 4)
#define REG_PPE_RAM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c)
#define PPE_SRAM_CTRL_ACK_MASK BIT(31)
@@ -404,6 +403,10 @@
#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
#define FC_MAP6_DEF_VALUE 0x1b1a1918
+#define REG_WAN_MTU0 0x2300
+#define WAN_MTU1_MASK GENMASK(29, 16)
+#define WAN_MTU0_MASK GENMASK(13, 0)
+
#define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4
/* QDMA */
@@ -1,40 +0,0 @@
From 1e596dac503da44a9fcd3e2654a4963db3e2ee6a Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Wed, 28 Jan 2026 02:38:24 +0100
Subject: [PATCH] net: airoha: fix wrong airoha_get_fe_port()
It seems the SDK where the airoha_get_fe_port() logic was taken was
actually wrong and the AN7583 SoC doesn't have such difference. Instead
it does follow the same port order of AN7581 SoC.
Drop the switch case and apply the same condition on both AN7581 and
AN7583 SoC.
Fixes: e4e5ce823bdd ("net: airoha: Add AN7583 SoC support")
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2543,17 +2543,9 @@ static u32 airoha_get_dsa_tag(struct sk_
int airoha_get_fe_port(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = dev->eth;
- switch (eth->soc->version) {
- case 0x7583:
- return port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
- : port->id;
- case 0x7581:
- default:
- return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4
- : port->id;
- }
+ return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4
+ : port->id;
}
static netdev_features_t airoha_dev_fix_features(struct net_device *netdev,
@@ -1,36 +0,0 @@
From 18aa76cd820c412358dc36d115b8e02d24a78f39 Mon Sep 17 00:00:00 2001
Message-ID: <18aa76cd820c412358dc36d115b8e02d24a78f39.1780942778.git.lorenzo@kernel.org>
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Mon, 8 Jun 2026 20:10:35 +0200
Subject: [PATCH] net: airoha: move get_sport() callback at the beginning of
airoha_enable_gdm2_loopback()
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2250,6 +2250,10 @@ static int airoha_enable_gdm2_loopback(s
u32 val, pse_port, chan;
int i, src_port;
+ src_port = eth->soc->ops.get_sport(port, dev->nbq);
+ if (src_port < 0)
+ return src_port;
+
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
FE_PSE_PORT_DROP);
airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
@@ -2275,10 +2279,6 @@ static int airoha_enable_gdm2_loopback(s
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, dev->nbq);
- if (src_port < 0)
- return src_port;
-
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, src_port));