- FC_ID_OF_SRC_PORT24_MASK,
- FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2));
+ if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
-+ u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
++ u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
+
+ airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
+ __field_prep(mask, AIROHA_GDM2_IDX));
+ airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX);
+
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
- u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
@@ -1873,7 +1876,8 @@ static int airoha_dev_init(struct net_de
}
-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.
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 +-
--- 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 - ð->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;
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;
/* 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)
{
+ 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 - ð->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);
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;
}
+ 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);
+ 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;
}
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;
}
port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
- port->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, dev->dev_addr);
-+ 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;
}
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));
}
}
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 - ð->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,
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));
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);
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;
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) {
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);
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
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));
}
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));
}
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);
}
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;
}
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;
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;
}
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)
{
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
}
}
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;
}
{
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
}
}
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);
- }
-
--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;
+ return false;
}
- #endif
+static int airoha_alloc_gdm_device(struct airoha_eth *eth,
+ struct airoha_gdm_port *port,
+ 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);
+
+ }
+
+ 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;
+}
+
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;
}
- 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)
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];
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];
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;
-
- struct airoha_hw_stats stats;
+ int id;
+ int nbq;
-@@ -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);
u8 fport);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
-@@ -299,12 +299,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);
struct airoha_foe_mac_info_common *l2;
u8 smac_id = 0xf;
-@@ -320,10 +320,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);
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) |
FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT,
FE_PSE_PORT_CDM4);
-@@ -333,12 +334,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))
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.
- */
-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
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 +-
--- 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)
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);
}
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));
}
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 - ð->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;
+ 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
}
}
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 - ð->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);
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);
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);
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++)
+ airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
- u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
-@@ -2173,9 +2179,9 @@ static int airoha_dev_init(struct net_de
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
+@@ -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 = ð->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 = ð->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;
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++)
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;
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;
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
}
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 - ð->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);
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
}
}
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);
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;
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;
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);
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,
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;
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);
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;
}
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);
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:
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);
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 {
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);
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);
}
struct airoha_eth *eth = qdma->eth;
u8 qdma_id = qdma - ð->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;
}
}
}
-@@ -1485,11 +1485,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;
-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
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 +++-
--- 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)
{
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 = ð->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->qdma = ð->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);
}
* phy module.
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
-@@ -367,7 +367,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.
*/
-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
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 ++-
--- 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;
}
- 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");
- 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,
+ * 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;
}
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");
}
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);
}
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);
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);
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);
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:
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];
};
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;
-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
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 +++----
--- 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);
}
--- 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 */
};
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;
--- /dev/null
+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;
+ }
--- /dev/null
+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);
+ }
+ }
+
--- /dev/null
+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,
--- /dev/null
+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
--- /dev/null
+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 = ð->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;
-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
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;
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;
}
- 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;
- }
+ 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);
+ 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;
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;
- 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)) {
/* 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];
+ 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)
}
}
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));
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)) {
-- u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
+- u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
__field_prep(mask, AIROHA_GDM2_IDX));
-@@ -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;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
dev->qdma = ð->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;
}
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);
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];
}
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,
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);
}
+ 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;
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;
}
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];
}
set_bit(DEV_STATE_REGISTERED, ð->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];
+ 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];
if (!port)
continue;
+- dev = port->dev;
+- if (dev)
+- unregister_netdev(netdev_from_priv(dev));
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *dev = port->devs[j];
+ struct net_device *netdev;
+ if (!dev)
+ continue;
+
- #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);
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;
}
+}
+
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;
}
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,
},
};
-@@ -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,
#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;
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);
--- 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);
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));
-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
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 +
--- 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 - ð->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
+ * 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));
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;
}
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;
}
--- 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;
-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
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;
}
- if (!eth->ports[1]) {
- int err;
-
-- err = airoha_set_gdm2_loopback(dev);
+- err = airoha_enable_gdm2_loopback(dev);
- if (err)
- return err;
- }
+ (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;
+ }
}
--- 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);
};
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;
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)
{
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
-@@ -356,7 +356,7 @@ static int airoha_ppe_foe_entry_prepare(
+@@ -355,7 +355,7 @@ static int airoha_ppe_foe_entry_prepare(
return -EINVAL;
port = dev->port;
-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)
- 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);
}
--- 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);
u32 hash);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
-@@ -1499,12 +1499,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);
}
-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 +-
--- 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),
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
}
}
- 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));
+
+ 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;
+ 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);
+ 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);
}
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);
}
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);
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;
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];
}
}
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);
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;
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 {
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;
};
struct airoha_gdm_port {
-@@ -580,7 +580,8 @@ struct airoha_gdm_port {
+@@ -561,7 +561,8 @@ struct airoha_gdm_port {
int id;
int users;
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
--- 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) {
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;
--- /dev/null
+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));
-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
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:
--- 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;
+++ /dev/null
-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;
--- 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));
+ 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
--- 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[] = {
"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[] = {
+++ /dev/null
-From ee93671d30d7741a39026c2aaaa6a7729929c347 Mon Sep 17 00:00:00 2001
-From: Christian Marangi <ansuelsmth@gmail.com>
-Date: Fri, 17 Jan 2025 13:23:13 +0100
-Subject: [PATCH 2/2] net: airoha: add phylink support for GDM2/3/4
-
-Add phylink support for GDM2/3/4 port that require 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.
-
-Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
----
- drivers/net/ethernet/airoha/Kconfig | 1 +
- drivers/net/ethernet/airoha/airoha_eth.c | 146 +++++++++++++++++++++-
- drivers/net/ethernet/airoha/airoha_eth.h | 3 +
- drivers/net/ethernet/airoha/airoha_regs.h | 12 ++
- 4 files changed, 161 insertions(+), 1 deletion(-)
-
---- 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>
-@@ -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,11 @@ static const struct ethtool_ops airoha_e
- .get_link = ethtool_op_get_link,
- };
-
-+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,119 @@ 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;
-+
-+ if (port->id != 4)
-+ 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;
-+ }
-+
-+ /* Configure TX/RX frag based on speed */
-+ airoha_fe_rmw(eth, REG_GDMA4_TMBI_FRAG,
-+ GDMA4_SGMII0_TX_FRAG_SIZE_MASK,
-+ FIELD_PREP(GDMA4_SGMII0_TX_FRAG_SIZE_MASK,
-+ frag_size_tx));
-+
-+ airoha_fe_rmw(eth, REG_GDMA4_RMBI_FRAG,
-+ GDMA4_SGMII0_RX_FRAG_SIZE_MASK,
-+ FIELD_PREP(GDMA4_SGMII0_RX_FRAG_SIZE_MASK,
-+ 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_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_available_pcs)
-+{
-+ struct device *dev = config->dev;
-+
-+ return fwnode_phylink_pcs_parse(dev_fwnode(dev), available_pcs,
-+ &num_available_pcs);
-+}
-+
-+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;
-+
-+ err = fwnode_phylink_pcs_parse(dev_fwnode(&dev->dev), NULL,
-+ &port->phylink_config.num_available_pcs);
-+ if (err)
-+ return err;
-+
-+ port->phylink_config.fill_available_pcs = airoha_fill_available_pcs;
-+
-+ __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_10GBASER,
-+ port->phylink_config.supported_interfaces);
-+ __set_bit(PHY_INTERFACE_MODE_USXGMII,
-+ port->phylink_config.supported_interfaces);
-+
-+ phy_interface_copy(port->phylink_config.pcs_interfaces,
-+ port->phylink_config.supported_interfaces);
-+
-+ 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 err;
-+}
-+
- 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,11 @@ error_napi_stop:
- if (!port)
- continue;
-
-- if (port->dev->reg_state == NETREG_REGISTERED)
-+ if (port->dev->reg_state == NETREG_REGISTERED) {
-+ if (airhoa_is_phy_external(port))
-+ phylink_destroy(port->phylink);
- unregister_netdev(port->dev);
-+ }
- airoha_metadata_dst_free(port);
- }
- airoha_hw_cleanup(eth);
-@@ -3186,6 +3335,8 @@ static void airoha_remove(struct platfor
- if (!port)
- continue;
-
-+ if (airhoa_is_phy_external(port))
-+ phylink_destroy(port->phylink);
- unregister_netdev(port->dev);
- airoha_metadata_dst_free(port);
- }
---- a/drivers/net/ethernet/airoha/airoha_eth.h
-+++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -542,6 +542,9 @@ struct airoha_gdm_port {
- int id;
- int nbq;
-
-+ struct phylink *phylink;
-+ struct phylink_config phylink_config;
-+
- 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_MASK GENMASK(31, 26)
-+#define GDMA4_SGMII1_TX_FRAG_SIZE_MASK GENMASK(25, 16)
-+#define GDMA4_SGMII0_TX_WEIGHT_MASK GENMASK(15, 10)
-+#define GDMA4_SGMII0_TX_FRAG_SIZE_MASK GENMASK(9, 0)
-+
-+#define REG_GDMA4_RMBI_FRAG 0x202c
-+#define GDMA4_SGMII1_RX_WEIGHT_MASK GENMASK(31, 26)
-+#define GDMA4_SGMII1_RX_FRAG_SIZE_MASK GENMASK(25, 16)
-+#define GDMA4_SGMII0_RX_WEIGHT_MASK GENMASK(15, 10)
-+#define GDMA4_SGMII0_RX_FRAG_SIZE_MASK GENMASK(9, 0)
-+
- #define REG_MC_VLAN_EN 0x2100
- #define MC_VLAN_EN_MASK BIT(0)
-
--- /dev/null
+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)
+
+++ /dev/null
-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;
+++ /dev/null
-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;
- }
-@@ -2990,6 +2996,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 err;
- }
-+#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,8 +3320,10 @@ error_napi_stop:
- continue;
-
- if (port->dev->reg_state == NETREG_REGISTERED) {
-+#if defined(CONFIG_PCS_AIROHA)
- if (airhoa_is_phy_external(port))
- phylink_destroy(port->phylink);
-+#endif
- unregister_netdev(port->dev);
- }
- airoha_metadata_dst_free(port);
-@@ -3338,8 +3350,10 @@ static void airoha_remove(struct platfor
- if (!port)
- continue;
-
-+#if defined(CONFIG_PCS_AIROHA)
- if (airhoa_is_phy_external(port))
- phylink_destroy(port->phylink);
-+#endif
- unregister_netdev(port->dev);
- airoha_metadata_dst_free(port);
- }
---- a/drivers/net/ethernet/airoha/airoha_eth.h
-+++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -542,8 +542,10 @@ struct airoha_gdm_port {
- int id;
- int nbq;
-
-+#if defined(CONFIG_PCS_AIROHA)
- struct phylink *phylink;
- struct phylink_config phylink_config;
-+#endif
-
- struct airoha_hw_stats stats;
-
#include "airoha_regs.h"
#include "airoha_eth.h"
-@@ -300,7 +301,7 @@ static int airoha_ppe_foe_entry_prepare(
+@@ -314,7 +315,7 @@ static int airoha_ppe_foe_entry_prepare(
struct airoha_foe_entry *hwe,
- struct net_device *dev, int type,
+ struct net_device *netdev, int type,
struct airoha_flow_data *data,
- int l4proto)
+ int l4proto, u8 dsfield)
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
- int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev);
-@@ -333,7 +334,7 @@ static int airoha_ppe_foe_entry_prepare(
- info.wcid);
+ int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev);
+@@ -349,7 +350,7 @@ static int airoha_ppe_foe_entry_prepare(
} else {
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port;
- u8 pse_port, channel;
+ u8 pse_port, channel, priority;
- if (!airoha_is_valid_gdm_port(eth, port))
+ if (!airoha_is_valid_gdm_dev(eth, dev))
return -EINVAL;
-@@ -352,9 +353,13 @@ static int airoha_ppe_foe_entry_prepare(
+@@ -369,9 +370,13 @@ static int airoha_ppe_foe_entry_prepare(
*/
channel = dsa_port >= 0 ? dsa_port : port->id;
channel = channel % AIROHA_NUM_QOS_CHANNELS;
AIROHA_FOE_IB2_PSE_QOS;
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
-@@ -1046,9 +1051,9 @@ static int airoha_ppe_flow_offload_repla
+@@ -1063,9 +1068,9 @@ static int airoha_ppe_flow_offload_repla
struct net_device *odev = NULL;
struct flow_action_entry *act;
struct airoha_foe_entry hwe;
if (rhashtable_lookup(ð->flow_table, &f->cookie,
airoha_flow_table_params))
-@@ -1078,6 +1083,13 @@ static int airoha_ppe_flow_offload_repla
+@@ -1095,6 +1100,13 @@ static int airoha_ppe_flow_offload_repla
return -EOPNOTSUPP;
}
switch (addr_type) {
case 0:
offload_type = PPE_PKT_TYPE_BRIDGE;
-@@ -1143,7 +1155,7 @@ static int airoha_ppe_flow_offload_repla
+@@ -1160,7 +1172,7 @@ static int airoha_ppe_flow_offload_repla
return -EINVAL;
err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type,
#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));
}
+ 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 - ð->qdma[0];
+ int i, lro_queue_index = 0;
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));
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,
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)
{
.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;
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;
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;
}
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));
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 */
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;
++
++ 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);
++ }
+ }
+}
+
- 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 - ð->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);
+ 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;
+ 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 - ð->qdma[0];
+
+ 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);
+
+}
+
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,
.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)
#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;
}
+ 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 @@
+++ /dev/null
-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;
- }
--- 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
+@@ -533,7 +533,7 @@ static bool airoha_fe_lro_is_enabled(str
static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
{
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - ð->qdma[0];
int i, lro_queue_index = 0;
-@@ -1158,7 +1158,7 @@ static void airoha_qdma_wake_netdev_txqs
+@@ -1159,7 +1159,7 @@ static void airoha_qdma_wake_netdev_txqs
if (!dev)
continue;
continue;
netdev = netdev_from_priv(dev);
-@@ -2031,7 +2031,8 @@ static void airoha_update_netdev_feature
+@@ -2055,7 +2055,8 @@ static void airoha_update_netdev_feature
if (!iter_dev || iter_dev == dev)
continue;
continue;
netdev = netdev_from_priv(iter_dev);
-@@ -2043,20 +2044,57 @@ static void airoha_update_netdev_feature
+@@ -2067,20 +2068,57 @@ static void airoha_update_netdev_feature
}
}
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
+@@ -2123,10 +2161,7 @@ static int airoha_dev_open(struct net_de
}
port->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
+ airoha_ppe_is_enabled(eth, 1))
+@@ -2170,8 +2205,7 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
int i;
netif_tx_disable(netdev);
-@@ -2163,25 +2197,14 @@ static int airoha_dev_stop(struct net_de
+@@ -2179,26 +2213,14 @@ 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_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(eth,
+ airoha_set_gdm_port_fwd_cfg(qdma->eth,
-+ REG_GDM_FWD_CFG(port->id),
+ REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
-
- if (!--qdma->users) {
+ 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
+ phylink_stop(dev->phylink);
+@@ -2288,6 +2310,53 @@ static int airoha_enable_gdm2_loopback(s
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
+@@ -2314,15 +2383,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);
}
static int airoha_dev_init(struct net_device *netdev)
-@@ -2473,8 +2558,9 @@ static netdev_features_t airoha_dev_fix_
+@@ -2477,8 +2561,9 @@ static netdev_features_t airoha_dev_fix_
netdev_features_t features)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
if (qdma->users > 1)
features &= ~NETIF_F_GRO_HW;
-@@ -2486,9 +2572,8 @@ static int airoha_dev_set_features(struc
+@@ -2490,9 +2575,8 @@ static int airoha_dev_set_features(struc
{
netdev_features_t diff = netdev->features ^ features;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
if (!(diff & NETIF_F_GRO_HW))
return 0;
-@@ -2496,7 +2581,7 @@ static int airoha_dev_set_features(struc
+@@ -2500,7 +2584,7 @@ static int airoha_dev_set_features(struc
if (features & NETIF_F_GRO_HW)
airoha_dev_lro_enable(dev);
else
return 0;
}
-@@ -2505,9 +2590,9 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2509,9 +2593,9 @@ static netdev_tx_t airoha_dev_xmit(struc
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct netdev_queue *txq;
struct airoha_queue *q;
LIST_HEAD(tx_list);
-@@ -2516,6 +2601,8 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2520,6 +2604,8 @@ static netdev_tx_t airoha_dev_xmit(struc
u16 index;
u8 fport;
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
+@@ -2566,6 +2652,8 @@ static netdev_tx_t airoha_dev_xmit(struc
netif_tx_stop_queue(txq);
q->txq_stopped = true;
spin_unlock_bh(&q->lock);
return NETDEV_TX_BUSY;
}
-@@ -2624,6 +2713,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2628,6 +2716,7 @@ static netdev_tx_t airoha_dev_xmit(struc
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
spin_unlock_bh(&q->lock);
return NETDEV_TX_OK;
-@@ -2639,6 +2729,7 @@ error_unmap:
+@@ -2643,6 +2732,7 @@ error_unmap:
error:
dev_kfree_skb_any(skb);
netdev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
-@@ -2720,17 +2811,19 @@ static int airoha_qdma_set_chan_tx_sched
+@@ -2740,17 +2830,19 @@ static int airoha_qdma_set_chan_tx_sched
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
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
+@@ -2758,12 +2850,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,
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(
+@@ -2827,13 +2919,15 @@ static int airoha_qdma_get_tx_ets_stats(
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
_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
+@@ -3093,16 +3187,18 @@ static int airoha_qdma_set_tx_rate_limit
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
-@@ -3118,11 +3215,12 @@ static int airoha_tc_htb_alloc_leaf_queu
+@@ -3138,11 +3234,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);
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
+@@ -3176,7 +3273,7 @@ static int airoha_qdma_set_rx_meter(stru
u32 rate, u32 bucket_size,
enum trtcm_unit_type unit_type)
{
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
-@@ -3330,11 +3428,12 @@ static int airoha_dev_setup_tc_block(str
+@@ -3350,11 +3447,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);
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
+@@ -3375,6 +3473,95 @@ static int airoha_tc_htb_delete_leaf_que
return 0;
}
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
+@@ -3383,6 +3570,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);
return 0;
}
-@@ -3382,24 +3572,33 @@ static int airoha_tc_get_htb_get_leaf_qu
+@@ -3402,24 +3591,33 @@ static int airoha_tc_get_htb_get_leaf_qu
return 0;
}
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 @@
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -552,11 +552,12 @@ struct airoha_qdma {
+@@ -550,11 +550,12 @@ struct airoha_qdma {
enum airoha_priv_flags {
AIROHA_PRIV_F_WAN = BIT(0),
- 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
+ DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+@@ -704,6 +705,16 @@ int airoha_get_fe_port(struct airoha_gdm
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
--- 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
+@@ -181,10 +181,15 @@ static void airoha_fe_maccr_init(struct
{
int p;
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
+@@ -2102,15 +2107,25 @@ static void airoha_qdma_stop(struct airo
}
}
/* 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
+@@ -2146,19 +2161,7 @@ static int airoha_dev_open(struct net_de
airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
port->users++;
airoha_qdma_start(qdma);
-@@ -2161,30 +2164,6 @@ static int airoha_dev_open(struct net_de
+@@ -2177,30 +2180,6 @@ static int airoha_dev_open(struct net_de
return 0;
}
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
+@@ -2215,7 +2194,7 @@ static int airoha_dev_stop(struct net_de
qdma = airoha_qdma_deref(dev);
if (--port->users)
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
+@@ -2274,10 +2253,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);
/* 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
+@@ -2472,7 +2447,7 @@ static int airoha_dev_change_mtu(struct
WRITE_ONCE(netdev->mtu, mtu);
if (port->users)
return 0;
}
-@@ -3471,6 +3446,7 @@ static void airoha_disable_qos_for_gdm34
+@@ -3490,6 +3465,7 @@ static void airoha_disable_qos_for_gdm34
dev->flags &= ~AIROHA_PRIV_F_WAN;
airoha_dev_set_qdma(dev);
airoha_set_macaddr(dev, netdev->dev_addr);
if (netif_running(netdev))
-@@ -3519,6 +3495,7 @@ static int airoha_enable_qos_for_gdm34(s
+@@ -3538,6 +3514,7 @@ static int airoha_enable_qos_for_gdm34(s
if (err)
goto error_disable_loopback;
#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
+@@ -715,6 +716,7 @@ airoha_qdma_deref(struct airoha_gdm_dev
lockdep_is_held(&flow_offload_mutex));
}
--- 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_
+@@ -2519,17 +2519,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;
+++ /dev/null
-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));