* [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port
@ 2026-05-27 10:21 Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu, Lorenzo Bianconi
EN7581 or AN7583 SoCs support connecting multiple external SerDes (e.g.
Ethernet or USB SerDes) to GDM3 or GDM4 ports via a hw arbiter that
manages the traffic in a TDM manner. As a result multiple net_devices can
connect to the same GDM{3,4} port and there is a theoretical "1:n"
relation between GDM ports and net_devices.
┌─────────────────────────────────┐
│ │ ┌──────┐
│ P1 GDM1 ├────►MT7530│
│ │ └──────┘
│ │ ETH0 (DSA conduit)
│ │
│ PSE/FE │
│ │
│ │
│ │ ┌─────┐
│ P0 CDM1 ├────►QDMA0│
│ P4 P9 GDM4 │ └─────┘
└──┬─────────────────────────┬────┘
│ │
┌──▼──┐ ┌────▼────┐
│ PPE │ │ ARB │
└─────┘ └─┬─────┬─┘
│ │
┌──▼──┐┌─▼───┐
│ ETH ││ USB │
└─────┘└─────┘
ETH1 ETH2
This is a preliminary series to introduce support for multiple net_devices
connected to the same Frame Engine (FE) GDM port (GDM3 or GDM4) via an
external hw arbiter.
---
Lorenzo Bianconi (6):
net: airoha: Introduce airoha_gdm_dev struct
net: airoha: Move airoha_qdma pointer in airoha_gdm_dev struct
net: airoha: Rely on airoha_gdm_dev pointer in airoha_is_lan_gdm_port()
net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
net: airoha: Move {cpu,fwd}_tx_packets in airoha_gdm_dev struct
net: airoha: Rename airoha_set_gdm2_loopback in airoha_enable_gdm2_loopback
drivers/net/ethernet/airoha/airoha_eth.c | 445 ++++++++++++++++++-------------
drivers/net/ethernet/airoha/airoha_eth.h | 35 ++-
drivers/net/ethernet/airoha/airoha_ppe.c | 34 +--
3 files changed, 297 insertions(+), 217 deletions(-)
---
base-commit: aa064a614efcfa4c300609d1f01134e99a12ad10
change-id: 20260527-airoha-eth-multi-serdes-preliminary-51204fb642fc
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
@ 2026-05-27 10:21 ` Lorenzo Bianconi
2026-05-28 12:38 ` Lorenzo Bianconi
2026-05-28 13:42 ` Alexander Lobakin
2026-05-27 10:21 ` [PATCH net-next 2/6] net: airoha: Move airoha_qdma pointer in " Lorenzo Bianconi
` (4 subsequent siblings)
5 siblings, 2 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu, Lorenzo Bianconi
EN7581 and AN7583 SoCs support connecting multiple external SerDes to GDM3
or GDM4 ports via a hw arbiter that manages the traffic in a TDM manner.
As a result multiple net_devices can connect to the same GDM{3,4} port
and there is a theoretical "1:n" relation between GDM port and
net_devices.
Introduce airoha_gdm_dev struct to collect net_device related info (e.g.
net_device and external phy pointer). Please note this is just a
preliminary patch and we are still supporting a single net_device for
each GDM port. Subsequent patches will add support for multiple net_devices
connected to the same GDM port.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 312 ++++++++++++++++++-------------
drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 17 +-
3 files changed, 206 insertions(+), 136 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 6418fe0c9f80..b2684af2db76 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -600,6 +600,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
struct airoha_qdma_desc *desc = &q->desc[q->tail];
u32 hash, reason, msg1, desc_ctrl;
struct airoha_gdm_port *port;
+ struct net_device *netdev;
int data_len, len, p;
struct page *page;
@@ -626,6 +627,10 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
goto free_frag;
port = eth->ports[p];
+ if (!port->dev)
+ goto free_frag;
+
+ netdev = port->dev->dev;
if (!q->skb) { /* first buffer */
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
q->buf_size);
@@ -635,8 +640,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
skb_reserve(q->skb, AIROHA_RX_HEADROOM);
__skb_put(q->skb, len);
skb_mark_for_recycle(q->skb);
- q->skb->dev = port->dev;
- q->skb->protocol = eth_type_trans(q->skb, port->dev);
+ q->skb->dev = netdev;
+ q->skb->protocol = eth_type_trans(q->skb, netdev);
q->skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(q->skb, qid);
} else { /* scattered frame */
@@ -654,7 +659,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
continue;
- if (netdev_uses_dsa(port->dev)) {
+ if (netdev_uses_dsa(netdev)) {
/* PPE module requires untagged packets to work
* properly and it provides DSA port index via the
* DMA descriptor. Report DSA tag to the DSA stack
@@ -848,6 +853,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct airoha_gdm_dev *dev;
int j;
if (!port)
@@ -856,11 +862,12 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
if (port->qdma != qdma)
continue;
- for (j = 0; j < port->dev->num_tx_queues; j++) {
+ dev = port->dev;
+ for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
- netif_wake_subqueue(port->dev, j);
+ netif_wake_subqueue(dev->dev, j);
}
}
q->txq_stopped = false;
@@ -1700,19 +1707,20 @@ static void airoha_update_hw_stats(struct airoha_gdm_port *port)
spin_unlock(&port->stats.lock);
}
-static int airoha_dev_open(struct net_device *dev)
+static int airoha_dev_open(struct net_device *netdev)
{
- int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
- netif_tx_start_all_queues(dev);
+ netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(port, true);
if (err)
return err;
- if (netdev_uses_dsa(dev))
+ if (netdev_uses_dsa(netdev))
airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
else
@@ -1740,16 +1748,17 @@ static int airoha_dev_open(struct net_device *dev)
return 0;
}
-static int airoha_dev_stop(struct net_device *dev)
+static int airoha_dev_stop(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
int i;
- netif_tx_disable(dev);
+ netif_tx_disable(netdev);
airoha_set_vip_for_gdm_port(port, false);
- for (i = 0; i < dev->num_tx_queues; i++)
- netdev_tx_reset_subqueue(dev, i);
+ for (i = 0; i < netdev->num_tx_queues; i++)
+ netdev_tx_reset_subqueue(netdev, i);
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
@@ -1770,16 +1779,17 @@ static int airoha_dev_stop(struct net_device *dev)
return 0;
}
-static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
+static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int err;
- err = eth_mac_addr(dev, p);
+ err = eth_mac_addr(netdev, p);
if (err)
return err;
- airoha_set_macaddr(port, dev->dev_addr);
+ airoha_set_macaddr(port, netdev->dev_addr);
return 0;
}
@@ -1845,16 +1855,17 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
return 0;
}
-static int airoha_dev_init(struct net_device *dev)
+static int airoha_dev_init(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
- struct airoha_eth *eth = port->eth;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
- port->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, dev->dev_addr);
+ dev->dev->irq = port->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(port, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -1879,10 +1890,11 @@ static int airoha_dev_init(struct net_device *dev)
return 0;
}
-static void airoha_dev_get_stats64(struct net_device *dev,
+static void airoha_dev_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *storage)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(port);
@@ -1901,36 +1913,39 @@ static void airoha_dev_get_stats64(struct net_device *dev,
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
-static int airoha_dev_change_mtu(struct net_device *dev, int mtu)
+static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = port->qdma->eth;
u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK,
FIELD_PREP(GDM_LONG_LEN_MASK, len));
- WRITE_ONCE(dev->mtu, mtu);
+ WRITE_ONCE(netdev->mtu, mtu);
return 0;
}
-static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
+static u16 airoha_dev_select_queue(struct net_device *netdev,
+ struct sk_buff *skb,
struct net_device *sb_dev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int queue, channel;
/* For dsa device select QoS channel according to the dsa user port
* index, rely on port id otherwise. Select QoS queue based on the
* skb priority.
*/
- channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
+ channel = netdev_uses_dsa(netdev) ? skb_get_queue_mapping(skb) : port->id;
channel = channel % AIROHA_NUM_QOS_CHANNELS;
queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
- return queue < dev->num_tx_queues ? queue : 0;
+ return queue < netdev->num_tx_queues ? queue : 0;
}
static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
@@ -1994,9 +2009,10 @@ int airoha_get_fe_port(struct airoha_gdm_port *port)
}
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
- struct net_device *dev)
+ struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = port->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
@@ -2009,7 +2025,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
u8 fport;
qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
- tag = airoha_get_dsa_tag(skb, dev);
+ tag = airoha_get_dsa_tag(skb, netdev);
msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
qid / AIROHA_NUM_QOS_QUEUES) |
@@ -2045,7 +2061,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
spin_lock_bh(&q->lock);
- txq = skb_get_tx_queue(dev, skb);
+ txq = skb_get_tx_queue(netdev, skb);
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
if (q->queued + nr_frags >= q->ndesc) {
@@ -2069,9 +2085,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
dma_addr_t addr;
u32 val;
- addr = dma_map_single(dev->dev.parent, data, len,
+ addr = dma_map_single(netdev->dev.parent, data, len,
DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
+ if (unlikely(dma_mapping_error(netdev->dev.parent, addr)))
goto error_unmap;
list_move_tail(&e->list, &tx_list);
@@ -2120,7 +2136,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
error_unmap:
list_for_each_entry(e, &tx_list, list) {
- dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len,
+ dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
e->dma_addr = 0;
}
@@ -2129,25 +2145,27 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
spin_unlock_bh(&q->lock);
error:
dev_kfree_skb_any(skb);
- dev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
-static void airoha_ethtool_get_drvinfo(struct net_device *dev,
+static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = port->qdma->eth;
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
}
-static void airoha_ethtool_get_mac_stats(struct net_device *dev,
+static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
struct ethtool_eth_mac_stats *stats)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(port);
@@ -2175,11 +2193,12 @@ static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
};
static void
-airoha_ethtool_get_rmon_stats(struct net_device *dev,
+airoha_ethtool_get_rmon_stats(struct net_device *netdev,
struct ethtool_rmon_stats *stats,
const struct ethtool_rmon_hist_range **ranges)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_hw_stats *hw_stats = &port->stats;
unsigned int start;
@@ -2204,11 +2223,12 @@ airoha_ethtool_get_rmon_stats(struct net_device *dev,
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
-static int airoha_qdma_set_chan_tx_sched(struct net_device *dev,
+static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
int channel, enum tx_sched_mode mode,
const u16 *weights, u8 n_weights)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int i;
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
@@ -2293,10 +2313,12 @@ static int airoha_qdma_set_tx_ets_sched(struct net_device *dev, int channel,
ARRAY_SIZE(w));
}
-static int airoha_qdma_get_tx_ets_stats(struct net_device *dev, int channel,
+static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct tc_ets_qopt_offload *opt)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+
u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
REG_CNTR_VAL(channel << 1));
u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
@@ -2558,11 +2580,12 @@ static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma,
mode, val);
}
-static int airoha_qdma_set_tx_rate_limit(struct net_device *dev,
+static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
int channel, u32 rate,
u32 bucket_size)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int i, err;
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
@@ -2582,20 +2605,22 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *dev,
return 0;
}
-static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev,
+static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
- int err, num_tx_queues = dev->real_num_tx_queues;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ int err, num_tx_queues = netdev->real_num_tx_queues;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
return -EINVAL;
}
- err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
+ err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
+ opt->quantum);
if (err) {
NL_SET_ERR_MSG_MOD(opt->extack,
"failed configuring htb offload");
@@ -2605,9 +2630,10 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev,
if (opt->command == TC_HTB_NODE_MODIFY)
return 0;
- err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1);
+ err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
if (err) {
- airoha_qdma_set_tx_rate_limit(dev, channel, 0, opt->quantum);
+ airoha_qdma_set_tx_rate_limit(netdev, channel, 0,
+ opt->quantum);
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
return err;
@@ -2697,11 +2723,12 @@ static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f)
return 0;
}
-static int airoha_dev_tc_matchall(struct net_device *dev,
+static int airoha_dev_tc_matchall(struct net_device *netdev,
struct tc_cls_matchall_offload *f)
{
enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -2736,18 +2763,19 @@ static int airoha_dev_tc_matchall(struct net_device *dev,
static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
- struct net_device *dev = cb_priv;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct net_device *netdev = cb_priv;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = port->qdma->eth;
- if (!tc_can_offload(dev))
+ if (!tc_can_offload(netdev))
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_CLSFLOWER:
return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data);
case TC_SETUP_CLSMATCHALL:
- return airoha_dev_tc_matchall(dev, type_data);
+ return airoha_dev_tc_matchall(netdev, type_data);
default:
return -EOPNOTSUPP;
}
@@ -2794,47 +2822,51 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
}
}
-static void airoha_tc_remove_htb_queue(struct net_device *dev, int queue)
+static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
- netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
- airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0);
+ netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
+ airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
clear_bit(queue, port->qos_sq_bmap);
}
-static int airoha_tc_htb_delete_leaf_queue(struct net_device *dev,
+static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
if (!test_bit(channel, port->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
- airoha_tc_remove_htb_queue(dev, channel);
+ airoha_tc_remove_htb_queue(netdev, channel);
return 0;
}
-static int airoha_tc_htb_destroy(struct net_device *dev)
+static int airoha_tc_htb_destroy(struct net_device *netdev)
{
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
int q;
for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
- airoha_tc_remove_htb_queue(dev, q);
+ airoha_tc_remove_htb_queue(netdev, q);
return 0;
}
-static int airoha_tc_get_htb_get_leaf_queue(struct net_device *dev,
+static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
if (!test_bit(channel, port->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
@@ -2870,8 +2902,8 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
return 0;
}
-static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
- void *type_data)
+static int airoha_dev_tc_setup(struct net_device *dev,
+ enum tc_setup_type type, void *type_data)
{
switch (type) {
case TC_SETUP_QDISC_ETS:
@@ -2937,25 +2969,81 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
}
}
-bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
- struct airoha_gdm_port *port)
+bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
+ struct airoha_gdm_dev *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
- if (eth->ports[i] == port)
+ struct airoha_gdm_port *port = eth->ports[i];
+
+ if (!port)
+ continue;
+
+ if (port->dev == dev)
return true;
}
return false;
}
+static int airoha_alloc_gdm_device(struct airoha_eth *eth,
+ struct airoha_gdm_port *port,
+ struct device_node *np)
+{
+ struct airoha_gdm_dev *dev;
+ struct net_device *netdev;
+ int err;
+
+ netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
+ AIROHA_NUM_NETDEV_TX_RINGS,
+ AIROHA_NUM_RX_RING);
+ if (!netdev) {
+ dev_err(eth->dev, "alloc_etherdev failed\n");
+ return -ENOMEM;
+ }
+
+ netdev->netdev_ops = &airoha_netdev_ops;
+ netdev->ethtool_ops = &airoha_ethtool_ops;
+ netdev->max_mtu = AIROHA_MAX_MTU;
+ netdev->watchdog_timeo = 5 * HZ;
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 |
+ NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
+ NETIF_F_HW_TC;
+ netdev->features |= netdev->hw_features;
+ netdev->vlan_features = netdev->hw_features;
+ netdev->dev.of_node = np;
+ SET_NETDEV_DEV(netdev, eth->dev);
+
+ /* reserve hw queues for HTB offloading */
+ err = netif_set_real_num_tx_queues(netdev, AIROHA_NUM_TX_RING);
+ if (err)
+ return err;
+
+ err = of_get_ethdev_address(np, netdev);
+ if (err) {
+ if (err == -EPROBE_DEFER)
+ return err;
+
+ eth_hw_addr_random(netdev);
+ dev_info(eth->dev, "generated random MAC address %pM\n",
+ netdev->dev_addr);
+ }
+
+ dev = netdev_priv(netdev);
+ dev->dev = netdev;
+ dev->port = port;
+ port->dev = dev;
+ dev->eth = eth;
+
+ return 0;
+}
+
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
struct device_node *np)
{
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
struct airoha_gdm_port *port;
- struct net_device *dev;
int err, p;
u32 id;
@@ -2977,53 +3065,22 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
return -EINVAL;
}
- dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
- AIROHA_NUM_NETDEV_TX_RINGS,
- AIROHA_NUM_RX_RING);
- if (!dev) {
- dev_err(eth->dev, "alloc_etherdev failed\n");
+ port = devm_kzalloc(eth->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
return -ENOMEM;
- }
-
- dev->netdev_ops = &airoha_netdev_ops;
- dev->ethtool_ops = &airoha_ethtool_ops;
- dev->max_mtu = AIROHA_MAX_MTU;
- dev->watchdog_timeo = 5 * HZ;
- dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
- NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
- NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HW_TC;
- dev->features |= dev->hw_features;
- dev->vlan_features = dev->hw_features;
- dev->dev.of_node = np;
- SET_NETDEV_DEV(dev, eth->dev);
- /* reserve hw queues for HTB offloading */
- err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
- if (err)
- return err;
-
- err = of_get_ethdev_address(np, dev);
- if (err) {
- if (err == -EPROBE_DEFER)
- return err;
-
- eth_hw_addr_random(dev);
- dev_info(eth->dev, "generated random MAC address %pM\n",
- dev->dev_addr);
- }
-
- port = netdev_priv(dev);
u64_stats_init(&port->stats.syncp);
spin_lock_init(&port->stats.lock);
- port->eth = eth;
- port->dev = dev;
port->id = id;
/* XXX: Read nbq from DTS */
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
eth->ports[p] = port;
- return airoha_metadata_dst_alloc(port);
+ err = airoha_metadata_dst_alloc(port);
+ if (err)
+ return err;
+
+ return airoha_alloc_gdm_device(eth, port, np);
}
static int airoha_register_gdm_devices(struct airoha_eth *eth)
@@ -3037,7 +3094,7 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth)
if (!port)
continue;
- err = register_netdev(port->dev);
+ err = register_netdev(port->dev->dev);
if (err)
return err;
}
@@ -3146,12 +3203,14 @@ static int airoha_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct airoha_gdm_dev *dev;
if (!port)
continue;
- if (port->dev->reg_state == NETREG_REGISTERED)
- unregister_netdev(port->dev);
+ dev = port->dev;
+ if (dev && dev->dev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(dev->dev);
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
@@ -3172,11 +3231,14 @@ static void airoha_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
struct airoha_gdm_port *port = eth->ports[i];
+ struct airoha_gdm_dev *dev;
if (!port)
continue;
- unregister_netdev(port->dev);
+ dev = port->dev;
+ if (dev)
+ unregister_netdev(dev->dev);
airoha_metadata_dst_free(port);
}
airoha_hw_cleanup(eth);
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index d3781103abb5..c78cabbec753 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -535,10 +535,15 @@ struct airoha_qdma {
struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
};
+struct airoha_gdm_dev {
+ struct airoha_gdm_port *port;
+ struct net_device *dev;
+ struct airoha_eth *eth;
+};
+
struct airoha_gdm_port {
struct airoha_qdma *qdma;
- struct airoha_eth *eth;
- struct net_device *dev;
+ struct airoha_gdm_dev *dev;
int id;
int nbq;
@@ -662,8 +667,8 @@ static inline bool airoha_is_7583(struct airoha_eth *eth)
}
int airoha_get_fe_port(struct airoha_gdm_port *port);
-bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
- struct airoha_gdm_port *port);
+bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
+ struct airoha_gdm_dev *dev);
void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id,
u8 fport);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 26da519236bf..af7af4097b98 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -298,12 +298,12 @@ static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br,
static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
struct airoha_foe_entry *hwe,
- struct net_device *dev, int type,
+ struct net_device *netdev, int type,
struct airoha_flow_data *data,
int l4proto)
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
- int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev);
+ int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev);
struct airoha_foe_mac_info_common *l2;
u8 smac_id = 0xf;
@@ -319,10 +319,11 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
hwe->ib1 = val;
val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f);
- if (dev) {
+ if (netdev) {
struct airoha_wdma_info info = {};
- if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) {
+ if (!airoha_ppe_get_wdma_info(netdev, data->eth.h_dest,
+ &info)) {
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) |
FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT,
FE_PSE_PORT_CDM4);
@@ -332,12 +333,14 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID,
info.wcid);
} else {
- struct airoha_gdm_port *port = netdev_priv(dev);
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port;
u8 pse_port, channel;
- if (!airoha_is_valid_gdm_port(eth, port))
+ if (!airoha_is_valid_gdm_dev(eth, dev))
return -EINVAL;
+ port = dev->port;
if (dsa_port >= 0 || eth->ports[1])
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
: port->id;
@@ -1473,7 +1476,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
{
struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev;
+ struct net_device *dev = port->dev->dev;
const u8 *addr = dev->dev_addr;
u32 val;
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 2/6] net: airoha: Move airoha_qdma pointer in airoha_gdm_dev struct
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-27 10:21 ` Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 3/6] net: airoha: Rely on airoha_gdm_dev pointer in airoha_is_lan_gdm_port() Lorenzo Bianconi
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu, Lorenzo Bianconi
Move airoha_qdma pointer from airoha_gdm_port struct to airoha_gdm_dev
one since the QDMA block used depends on the particular net_device
WAN/LAN configuration and in the current codebase net_device pointer is
associated to airoha_gdm_dev struct.
This is a preliminary patch to support multiple net_devices connected
to the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 105 +++++++++++++++----------------
drivers/net/ethernet/airoha/airoha_eth.h | 9 ++-
drivers/net/ethernet/airoha/airoha_ppe.c | 17 ++---
3 files changed, 64 insertions(+), 67 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index b2684af2db76..0f37d26e4d52 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -71,9 +71,10 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
+static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, reg;
reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
@@ -85,7 +86,7 @@ static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
- airoha_ppe_init_upd_mem(port);
+ airoha_ppe_init_upd_mem(dev);
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -101,10 +102,10 @@ static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
FIELD_PREP(GDM_UCFQ_MASK, val));
}
-static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port,
- bool enable)
+static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 vip_port;
vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
@@ -859,10 +860,13 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
if (!port)
continue;
- if (port->qdma != qdma)
+ dev = port->dev;
+ if (!dev)
+ continue;
+
+ if (dev->qdma != qdma)
continue;
- dev = port->dev;
for (j = 0; j < dev->dev->num_tx_queues; j++) {
if (airoha_qdma_get_txq(qdma, j) != qid)
continue;
@@ -1563,9 +1567,10 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
}
}
-static void airoha_update_hw_stats(struct airoha_gdm_port *port)
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
spin_lock(&port->stats.lock);
@@ -1712,11 +1717,11 @@ static int airoha_dev_open(struct net_device *netdev)
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
netif_tx_start_all_queues(netdev);
- err = airoha_set_vip_for_gdm_port(port, true);
+ err = airoha_set_vip_for_gdm_port(dev, true);
if (err)
return err;
@@ -1752,11 +1757,11 @@ static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
int i;
netif_tx_disable(netdev);
- airoha_set_vip_for_gdm_port(port, false);
+ airoha_set_vip_for_gdm_port(dev, false);
for (i = 0; i < netdev->num_tx_queues; i++)
netdev_tx_reset_subqueue(netdev, i);
@@ -1782,21 +1787,21 @@ static int airoha_dev_stop(struct net_device *netdev)
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int err;
err = eth_mac_addr(netdev, p);
if (err)
return err;
- airoha_set_macaddr(port, netdev->dev_addr);
+ airoha_set_macaddr(dev, netdev->dev_addr);
return 0;
}
-static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
+static int airoha_set_gdm2_loopback(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
u32 val, pse_port, chan;
int i, src_port;
@@ -1843,7 +1848,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
__field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX);
+ airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
@@ -1863,9 +1868,9 @@ static int airoha_dev_init(struct net_device *netdev)
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
- dev->dev->irq = port->qdma->irq_banks[0].irq;
- airoha_set_macaddr(port, netdev->dev_addr);
+ dev->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->dev->irq = dev->qdma->irq_banks[0].irq;
+ airoha_set_macaddr(dev, netdev->dev_addr);
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -1874,7 +1879,7 @@ static int airoha_dev_init(struct net_device *netdev)
if (!eth->ports[1]) {
int err;
- err = airoha_set_gdm2_loopback(port);
+ err = airoha_set_gdm2_loopback(dev);
if (err)
return err;
}
@@ -1884,8 +1889,7 @@ static int airoha_dev_init(struct net_device *netdev)
}
for (i = 0; i < eth->soc->num_ppe; i++)
- airoha_ppe_set_cpu_port(port, i,
- airoha_get_fe_port(port));
+ airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
return 0;
}
@@ -1897,7 +1901,7 @@ static void airoha_dev_get_stats64(struct net_device *netdev,
struct airoha_gdm_port *port = dev->port;
unsigned int start;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
storage->rx_packets = port->stats.rx_ok_pkts;
@@ -1917,8 +1921,8 @@ static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
+ struct airoha_eth *eth = dev->eth;
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
GDM_LONG_LEN_MASK,
@@ -1992,10 +1996,10 @@ static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
#endif
}
-int airoha_get_fe_port(struct airoha_gdm_port *port)
+int airoha_get_fe_port(struct airoha_gdm_dev *dev)
{
- struct airoha_qdma *qdma = port->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
switch (eth->soc->version) {
case 0x7583:
@@ -2012,8 +2016,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
struct netdev_queue *txq;
@@ -2051,7 +2054,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
}
}
- fport = airoha_get_fe_port(port);
+ fport = airoha_get_fe_port(dev);
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@@ -2154,8 +2157,7 @@ static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_eth *eth = dev->eth;
strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
@@ -2168,7 +2170,7 @@ static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
struct airoha_gdm_port *port = dev->port;
unsigned int start;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
@@ -2208,7 +2210,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
ARRAY_SIZE(hw_stats->rx_len) + 1);
*ranges = airoha_ethtool_rmon_ranges;
- airoha_update_hw_stats(port);
+ airoha_update_hw_stats(dev);
do {
int i;
@@ -2228,18 +2230,17 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int i;
for (i = 0; i < AIROHA_NUM_TX_RING; i++)
- airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
+ airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
for (i = 0; i < n_weights; i++) {
u32 status;
int err;
- airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG,
+ airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
TWRR_RW_CMD_MASK |
FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2247,13 +2248,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
err = read_poll_timeout(airoha_qdma_rr, status,
status & TWRR_RW_CMD_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC,
- true, port->qdma,
- REG_TXWRR_WEIGHT_CFG);
+ true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
if (err)
return err;
}
- airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+ airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
@@ -2319,9 +2319,9 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
+ u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma,
REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
+ u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma,
REG_CNTR_VAL((channel << 1) + 1));
u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
(fwd_tx_packets - port->fwd_tx_packets);
@@ -2585,17 +2585,16 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int i, err;
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
- err = airoha_qdma_set_trtcm_config(port->qdma, channel,
+ err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
REG_EGRESS_TRTCM_CFG, i,
!!rate, TRTCM_METER_MODE);
if (err)
return err;
- err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel,
+ err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
@@ -2645,11 +2644,11 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
return 0;
}
-static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port,
+static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
u32 rate, u32 bucket_size,
enum trtcm_unit_type unit_type)
{
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -2728,7 +2727,6 @@ static int airoha_dev_tc_matchall(struct net_device *netdev,
{
enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
u32 rate = 0, bucket_size = 0;
switch (f->command) {
@@ -2753,7 +2751,7 @@ static int airoha_dev_tc_matchall(struct net_device *netdev,
fallthrough;
}
case TC_CLSMATCHALL_DESTROY:
- return airoha_qdma_set_rx_meter(port, rate, bucket_size,
+ return airoha_qdma_set_rx_meter(dev, rate, bucket_size,
unit_type);
default:
return -EOPNOTSUPP;
@@ -2765,8 +2763,7 @@ static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
{
struct net_device *netdev = cb_priv;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_eth *eth = port->qdma->eth;
+ struct airoha_eth *eth = dev->eth;
if (!tc_can_offload(netdev))
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index c78cabbec753..f1eea492217c 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -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;
};
struct airoha_gdm_port {
- struct airoha_qdma *qdma;
struct airoha_gdm_dev *dev;
int id;
int nbq;
@@ -666,19 +666,18 @@ static inline bool airoha_is_7583(struct airoha_eth *eth)
return eth->soc->version == 0x7583;
}
-int airoha_get_fe_port(struct airoha_gdm_port *port);
+int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id,
- u8 fport);
+void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan);
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port);
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index af7af4097b98..22f5f1bae730 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -84,9 +84,9 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
AIROHA_FOE_IB1_BIND_TIMESTAMP);
}
-void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport)
+void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
{
- struct airoha_qdma *qdma = port->qdma;
+ struct airoha_qdma *qdma = dev->qdma;
struct airoha_eth *eth = qdma->eth;
u8 qdma_id = qdma - ð->qdma[0];
u32 fe_cpu_port;
@@ -180,8 +180,8 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
if (!port)
continue;
- airoha_ppe_set_cpu_port(port, i,
- airoha_get_fe_port(port));
+ airoha_ppe_set_cpu_port(port->dev, i,
+ airoha_get_fe_port(port->dev));
}
}
}
@@ -1473,11 +1473,12 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
{
- struct airoha_eth *eth = port->qdma->eth;
- struct net_device *dev = port->dev->dev;
- const u8 *addr = dev->dev_addr;
+ struct airoha_gdm_port *port = dev->port;
+ struct net_device *netdev = dev->dev;
+ struct airoha_eth *eth = dev->eth;
+ const u8 *addr = netdev->dev_addr;
u32 val;
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 3/6] net: airoha: Rely on airoha_gdm_dev pointer in airoha_is_lan_gdm_port()
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 2/6] net: airoha: Move airoha_qdma pointer in " Lorenzo Bianconi
@ 2026-05-27 10:21 ` Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu, Lorenzo Bianconi
Rename airoha_is_lan_gdm_port in airoha_is_lan_gdm_dev. Moreover, rely
on airoha_gdm_dev pointer in airoha_is_lan_gdm_dev() instead of
airoha_gdm_port one.
This is a preliminary patch to support multiple net_devices connected to
the same GDM{3,4} port via an external hw arbiter.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 6 ++----
drivers/net/ethernet/airoha/airoha_eth.h | 4 +++-
drivers/net/ethernet/airoha/airoha_ppe.c | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 0f37d26e4d52..c5d25f7640ac 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -73,12 +73,10 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
- struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, reg;
- reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
- : REG_FE_WAN_MAC_H;
+ reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
@@ -1868,7 +1866,7 @@ static int airoha_dev_init(struct net_device *netdev)
int i;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
+ dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
dev->dev->irq = dev->qdma->irq_banks[0].irq;
airoha_set_macaddr(dev, netdev->dev_addr);
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f1eea492217c..f6f59d25abd9 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -647,8 +647,10 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
return qid % ARRAY_SIZE(qdma->q_tx);
}
-static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port)
+static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
+ struct airoha_gdm_port *port = dev->port;
+
/* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
* GDM{2,3,4} can be used as wan port connected to an external
* phy module.
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 22f5f1bae730..047141b2d6d8 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -362,7 +362,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
*/
- if (airoha_is_lan_gdm_port(port))
+ if (airoha_is_lan_gdm_dev(dev))
val |= AIROHA_FOE_IB2_FAST_PATH;
if (dsa_port >= 0)
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ,
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (2 preceding siblings ...)
2026-05-27 10:21 ` [PATCH net-next 3/6] net: airoha: Rely on airoha_gdm_dev pointer in airoha_is_lan_gdm_port() Lorenzo Bianconi
@ 2026-05-27 10:21 ` Lorenzo Bianconi
2026-05-28 12:49 ` Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 5/6] net: airoha: Move {cpu,fwd}_tx_packets " Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 6/6] net: airoha: Rename airoha_set_gdm2_loopback in airoha_enable_gdm2_loopback Lorenzo Bianconi
5 siblings, 1 reply; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu, Lorenzo Bianconi
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, qos_sq_bmap field can be overwritten with the
configuration obtained from a net_device connected to a different QDMA
block. In order to fix the issue move qos_sq_bmap field from
airoha_gdm_port struct to airoha_gdm_dev one.
Add qos_channel_map bitmap in airoha_qdma struct to track if a shared
QDMA channel is already in use by another net_device.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 58 ++++++++++++++++++++------------
drivers/net/ethernet/airoha/airoha_eth.h | 6 ++--
2 files changed, 40 insertions(+), 24 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index c5d25f7640ac..4f77a0c22162 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2602,30 +2602,40 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
return 0;
}
-static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
- struct tc_htb_qopt_offload *opt)
+static int airoha_tc_htb_modify_queue(struct net_device *dev,
+ struct tc_htb_qopt_offload *opt)
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
- int err, num_tx_queues = netdev->real_num_tx_queues;
- struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
return -EINVAL;
}
- err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
- opt->quantum);
- if (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,
+ struct tc_htb_qopt_offload *opt)
+{
+ u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
+ int err, num_tx_queues = netdev->real_num_tx_queues;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+
+ /* Here we need to check the requested QDMA channel is not already
+ * in use by another net_device running on the same QDMA block.
+ */
+ if (test_and_set_bit(channel, qdma->qos_channel_map)) {
NL_SET_ERR_MSG_MOD(opt->extack,
- "failed configuring htb offload");
- return err;
+ "qdma qos channel already in use");
+ return -EBUSY;
}
- if (opt->command == TC_HTB_NODE_MODIFY)
- return 0;
+ err = airoha_tc_htb_modify_queue(netdev, opt);
+ if (err)
+ goto error;
err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
if (err) {
@@ -2633,13 +2643,17 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
opt->quantum);
NL_SET_ERR_MSG_MOD(opt->extack,
"failed setting real_num_tx_queues");
- return err;
+ goto error;
}
- set_bit(channel, port->qos_sq_bmap);
+ set_bit(channel, dev->qos_sq_bmap);
opt->qid = AIROHA_NUM_TX_RING + channel;
return 0;
+error:
+ clear_bit(channel, qdma->qos_channel_map);
+
+ return err;
}
static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
@@ -2820,11 +2834,13 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
- clear_bit(queue, port->qos_sq_bmap);
+
+ clear_bit(queue, qdma->qos_channel_map);
+ clear_bit(queue, dev->qos_sq_bmap);
}
static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
@@ -2832,9 +2848,8 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- if (!test_bit(channel, port->qos_sq_bmap)) {
+ if (!test_bit(channel, dev->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
@@ -2847,10 +2862,9 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
static int airoha_tc_htb_destroy(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
int q;
- for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
+ for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
airoha_tc_remove_htb_queue(netdev, q);
return 0;
@@ -2861,9 +2875,8 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
{
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- if (!test_bit(channel, port->qos_sq_bmap)) {
+ if (!test_bit(channel, dev->qos_sq_bmap)) {
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
return -EINVAL;
}
@@ -2882,6 +2895,7 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
case TC_HTB_DESTROY:
return airoha_tc_htb_destroy(dev);
case TC_HTB_NODE_MODIFY:
+ return airoha_tc_htb_modify_queue(dev, opt);
case TC_HTB_LEAF_ALLOC_QUEUE:
return airoha_tc_htb_alloc_leaf_queue(dev, opt);
case TC_HTB_LEAF_DEL:
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f6f59d25abd9..a308a770116b 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -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];
+
+ DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
struct airoha_gdm_dev {
@@ -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 {
@@ -549,8 +553,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
- DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
-
/* qos stats counters */
u64 cpu_tx_packets;
u64 fwd_tx_packets;
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 5/6] net: airoha: Move {cpu,fwd}_tx_packets in airoha_gdm_dev struct
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (3 preceding siblings ...)
2026-05-27 10:21 ` [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-27 10:21 ` Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 6/6] net: airoha: Rename airoha_set_gdm2_loopback in airoha_enable_gdm2_loopback Lorenzo Bianconi
5 siblings, 0 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu, Lorenzo Bianconi
Since now multiple net_devices connected to different QDMA blocks can
share the same GDM port, cpu_tx_packets and fwd_tx_packets fields can
be overwritten with the value from a different QDMA block. In order to
fix the issue move cpu_tx_packets and fwd_tx_packets fields from
airoha_gdm_port struct to airoha_gdm_dev one.
Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 16 +++++++---------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +++----
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 4f77a0c22162..72c6f554508c 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2315,19 +2315,17 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma = dev->qdma;
- u64 cpu_tx_packets = airoha_qdma_rr(dev->qdma,
- REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(dev->qdma,
+ u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+ u64 fwd_tx_packets = airoha_qdma_rr(qdma,
REG_CNTR_VAL((channel << 1) + 1));
- u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
- (fwd_tx_packets - port->fwd_tx_packets);
+ u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
+ (fwd_tx_packets - dev->fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
-
- port->cpu_tx_packets = cpu_tx_packets;
- port->fwd_tx_packets = fwd_tx_packets;
+ dev->cpu_tx_packets = cpu_tx_packets;
+ dev->fwd_tx_packets = fwd_tx_packets;
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index a308a770116b..fbb50dc73af8 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -544,6 +544,9 @@ struct airoha_gdm_dev {
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+ /* qos stats counters */
+ u64 cpu_tx_packets;
+ u64 fwd_tx_packets;
};
struct airoha_gdm_port {
@@ -553,10 +556,6 @@ struct airoha_gdm_port {
struct airoha_hw_stats stats;
- /* qos stats counters */
- u64 cpu_tx_packets;
- u64 fwd_tx_packets;
-
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net-next 6/6] net: airoha: Rename airoha_set_gdm2_loopback in airoha_enable_gdm2_loopback
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
` (4 preceding siblings ...)
2026-05-27 10:21 ` [PATCH net-next 5/6] net: airoha: Move {cpu,fwd}_tx_packets " Lorenzo Bianconi
@ 2026-05-27 10:21 ` Lorenzo Bianconi
5 siblings, 0 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-27 10:21 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Lorenzo Bianconi
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>
---
drivers/net/ethernet/airoha/airoha_eth.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 72c6f554508c..6574901ebd19 100644
--- 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 net_device *netdev, void *p)
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_device *netdev)
if (!eth->ports[1]) {
int err;
- err = airoha_set_gdm2_loopback(dev);
+ err = airoha_enable_gdm2_loopback(dev);
if (err)
return err;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct
2026-05-27 10:21 ` [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-28 12:38 ` Lorenzo Bianconi
2026-05-28 13:42 ` Alexander Lobakin
1 sibling, 0 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-28 12:38 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu
[-- Attachment #1: Type: text/plain, Size: 30674 bytes --]
> EN7581 and AN7583 SoCs support connecting multiple external SerDes to GDM3
> or GDM4 ports via a hw arbiter that manages the traffic in a TDM manner.
> As a result multiple net_devices can connect to the same GDM{3,4} port
> and there is a theoretical "1:n" relation between GDM port and
> net_devices.
> Introduce airoha_gdm_dev struct to collect net_device related info (e.g.
> net_device and external phy pointer). Please note this is just a
> preliminary patch and we are still supporting a single net_device for
> each GDM port. Subsequent patches will add support for multiple net_devices
> connected to the same GDM port.
>
> Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 312 ++++++++++++++++++-------------
> drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
> drivers/net/ethernet/airoha/airoha_ppe.c | 17 +-
> 3 files changed, 206 insertions(+), 136 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 6418fe0c9f80..b2684af2db76 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -600,6 +600,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
> struct airoha_qdma_desc *desc = &q->desc[q->tail];
> u32 hash, reason, msg1, desc_ctrl;
> struct airoha_gdm_port *port;
> + struct net_device *netdev;
> int data_len, len, p;
> struct page *page;
>
> @@ -626,6 +627,10 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
> goto free_frag;
>
> port = eth->ports[p];
> + if (!port->dev)
> + goto free_frag;
> +
> + netdev = port->dev->dev;
> if (!q->skb) { /* first buffer */
> q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
> q->buf_size);
> @@ -635,8 +640,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
> skb_reserve(q->skb, AIROHA_RX_HEADROOM);
> __skb_put(q->skb, len);
> skb_mark_for_recycle(q->skb);
> - q->skb->dev = port->dev;
> - q->skb->protocol = eth_type_trans(q->skb, port->dev);
> + q->skb->dev = netdev;
> + q->skb->protocol = eth_type_trans(q->skb, netdev);
> q->skb->ip_summed = CHECKSUM_UNNECESSARY;
> skb_record_rx_queue(q->skb, qid);
> } else { /* scattered frame */
> @@ -654,7 +659,7 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
> if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
> continue;
>
> - if (netdev_uses_dsa(port->dev)) {
> + if (netdev_uses_dsa(netdev)) {
> /* PPE module requires untagged packets to work
> * properly and it provides DSA port index via the
> * DMA descriptor. Report DSA tag to the DSA stack
> @@ -848,6 +853,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
>
> for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
> struct airoha_gdm_port *port = eth->ports[i];
> + struct airoha_gdm_dev *dev;
> int j;
>
> if (!port)
> @@ -856,11 +862,12 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
> if (port->qdma != qdma)
> continue;
>
> - for (j = 0; j < port->dev->num_tx_queues; j++) {
> + dev = port->dev;
> + for (j = 0; j < dev->dev->num_tx_queues; j++) {
> if (airoha_qdma_get_txq(qdma, j) != qid)
> continue;
>
> - netif_wake_subqueue(port->dev, j);
> + netif_wake_subqueue(dev->dev, j);
> }
> }
> q->txq_stopped = false;
> @@ -1700,19 +1707,20 @@ static void airoha_update_hw_stats(struct airoha_gdm_port *port)
> spin_unlock(&port->stats.lock);
> }
>
> -static int airoha_dev_open(struct net_device *dev)
> +static int airoha_dev_open(struct net_device *netdev)
> {
> - int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_qdma *qdma = port->qdma;
> u32 pse_port = FE_PSE_PORT_PPE1;
>
> - netif_tx_start_all_queues(dev);
> + netif_tx_start_all_queues(netdev);
> err = airoha_set_vip_for_gdm_port(port, true);
> if (err)
> return err;
>
> - if (netdev_uses_dsa(dev))
> + if (netdev_uses_dsa(netdev))
> airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
> GDM_STAG_EN_MASK);
> else
> @@ -1740,16 +1748,17 @@ static int airoha_dev_open(struct net_device *dev)
> return 0;
> }
>
> -static int airoha_dev_stop(struct net_device *dev)
> +static int airoha_dev_stop(struct net_device *netdev)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_qdma *qdma = port->qdma;
> int i;
>
> - netif_tx_disable(dev);
> + netif_tx_disable(netdev);
> airoha_set_vip_for_gdm_port(port, false);
> - for (i = 0; i < dev->num_tx_queues; i++)
> - netdev_tx_reset_subqueue(dev, i);
> + for (i = 0; i < netdev->num_tx_queues; i++)
> + netdev_tx_reset_subqueue(netdev, i);
>
> airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
> FE_PSE_PORT_DROP);
> @@ -1770,16 +1779,17 @@ static int airoha_dev_stop(struct net_device *dev)
> return 0;
> }
>
> -static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
> +static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> int err;
>
> - err = eth_mac_addr(dev, p);
> + err = eth_mac_addr(netdev, p);
> if (err)
> return err;
>
> - airoha_set_macaddr(port, dev->dev_addr);
> + airoha_set_macaddr(port, netdev->dev_addr);
>
> return 0;
> }
> @@ -1845,16 +1855,17 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
> return 0;
> }
>
> -static int airoha_dev_init(struct net_device *dev)
> +static int airoha_dev_init(struct net_device *netdev)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> - struct airoha_eth *eth = port->eth;
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> + struct airoha_eth *eth = dev->eth;
> int i;
>
> /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
> port->qdma = ð->qdma[!airoha_is_lan_gdm_port(port)];
> - port->dev->irq = port->qdma->irq_banks[0].irq;
> - airoha_set_macaddr(port, dev->dev_addr);
> + dev->dev->irq = port->qdma->irq_banks[0].irq;
> + airoha_set_macaddr(port, netdev->dev_addr);
>
> switch (port->id) {
> case AIROHA_GDM3_IDX:
> @@ -1879,10 +1890,11 @@ static int airoha_dev_init(struct net_device *dev)
> return 0;
> }
>
> -static void airoha_dev_get_stats64(struct net_device *dev,
> +static void airoha_dev_get_stats64(struct net_device *netdev,
> struct rtnl_link_stats64 *storage)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> unsigned int start;
>
> airoha_update_hw_stats(port);
> @@ -1901,36 +1913,39 @@ static void airoha_dev_get_stats64(struct net_device *dev,
> } while (u64_stats_fetch_retry(&port->stats.syncp, start));
> }
>
> -static int airoha_dev_change_mtu(struct net_device *dev, int mtu)
> +static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_eth *eth = port->qdma->eth;
> u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
>
> airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
> GDM_LONG_LEN_MASK,
> FIELD_PREP(GDM_LONG_LEN_MASK, len));
> - WRITE_ONCE(dev->mtu, mtu);
> + WRITE_ONCE(netdev->mtu, mtu);
>
> return 0;
> }
>
> -static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
> +static u16 airoha_dev_select_queue(struct net_device *netdev,
> + struct sk_buff *skb,
> struct net_device *sb_dev)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> int queue, channel;
>
> /* For dsa device select QoS channel according to the dsa user port
> * index, rely on port id otherwise. Select QoS queue based on the
> * skb priority.
> */
> - channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
> + channel = netdev_uses_dsa(netdev) ? skb_get_queue_mapping(skb) : port->id;
> channel = channel % AIROHA_NUM_QOS_CHANNELS;
> queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
> queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
>
> - return queue < dev->num_tx_queues ? queue : 0;
> + return queue < netdev->num_tx_queues ? queue : 0;
> }
>
> static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
> @@ -1994,9 +2009,10 @@ int airoha_get_fe_port(struct airoha_gdm_port *port)
> }
>
> static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> - struct net_device *dev)
> + struct net_device *netdev)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_qdma *qdma = port->qdma;
> u32 nr_frags, tag, msg0, msg1, len;
> struct airoha_queue_entry *e;
> @@ -2009,7 +2025,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> u8 fport;
>
> qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
> - tag = airoha_get_dsa_tag(skb, dev);
> + tag = airoha_get_dsa_tag(skb, netdev);
>
> msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
> qid / AIROHA_NUM_QOS_QUEUES) |
> @@ -2045,7 +2061,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
>
> spin_lock_bh(&q->lock);
>
> - txq = skb_get_tx_queue(dev, skb);
> + txq = skb_get_tx_queue(netdev, skb);
> nr_frags = 1 + skb_shinfo(skb)->nr_frags;
>
> if (q->queued + nr_frags >= q->ndesc) {
> @@ -2069,9 +2085,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> dma_addr_t addr;
> u32 val;
>
> - addr = dma_map_single(dev->dev.parent, data, len,
> + addr = dma_map_single(netdev->dev.parent, data, len,
> DMA_TO_DEVICE);
> - if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
> + if (unlikely(dma_mapping_error(netdev->dev.parent, addr)))
> goto error_unmap;
>
> list_move_tail(&e->list, &tx_list);
> @@ -2120,7 +2136,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
>
> error_unmap:
> list_for_each_entry(e, &tx_list, list) {
> - dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len,
> + dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len,
> DMA_TO_DEVICE);
> e->dma_addr = 0;
> }
> @@ -2129,25 +2145,27 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
> spin_unlock_bh(&q->lock);
> error:
> dev_kfree_skb_any(skb);
> - dev->stats.tx_dropped++;
> + netdev->stats.tx_dropped++;
>
> return NETDEV_TX_OK;
> }
>
> -static void airoha_ethtool_get_drvinfo(struct net_device *dev,
> +static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
> struct ethtool_drvinfo *info)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_eth *eth = port->qdma->eth;
>
> strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
> strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
> }
>
> -static void airoha_ethtool_get_mac_stats(struct net_device *dev,
> +static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
> struct ethtool_eth_mac_stats *stats)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> unsigned int start;
>
> airoha_update_hw_stats(port);
> @@ -2175,11 +2193,12 @@ static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
> };
>
> static void
> -airoha_ethtool_get_rmon_stats(struct net_device *dev,
> +airoha_ethtool_get_rmon_stats(struct net_device *netdev,
> struct ethtool_rmon_stats *stats,
> const struct ethtool_rmon_hist_range **ranges)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_hw_stats *hw_stats = &port->stats;
> unsigned int start;
>
> @@ -2204,11 +2223,12 @@ airoha_ethtool_get_rmon_stats(struct net_device *dev,
> } while (u64_stats_fetch_retry(&port->stats.syncp, start));
> }
>
> -static int airoha_qdma_set_chan_tx_sched(struct net_device *dev,
> +static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
> int channel, enum tx_sched_mode mode,
> const u16 *weights, u8 n_weights)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> int i;
>
> for (i = 0; i < AIROHA_NUM_TX_RING; i++)
> @@ -2293,10 +2313,12 @@ static int airoha_qdma_set_tx_ets_sched(struct net_device *dev, int channel,
> ARRAY_SIZE(w));
> }
>
> -static int airoha_qdma_get_tx_ets_stats(struct net_device *dev, int channel,
> +static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
> struct tc_ets_qopt_offload *opt)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> +
> u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
> REG_CNTR_VAL(channel << 1));
> u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
> @@ -2558,11 +2580,12 @@ static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma,
> mode, val);
> }
>
> -static int airoha_qdma_set_tx_rate_limit(struct net_device *dev,
> +static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
> int channel, u32 rate,
> u32 bucket_size)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> int i, err;
>
> for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
> @@ -2582,20 +2605,22 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *dev,
> return 0;
> }
>
> -static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev,
> +static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> struct tc_htb_qopt_offload *opt)
> {
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
> - int err, num_tx_queues = dev->real_num_tx_queues;
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + int err, num_tx_queues = netdev->real_num_tx_queues;
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
>
> if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
> return -EINVAL;
> }
>
> - err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
> + err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
> + opt->quantum);
> if (err) {
> NL_SET_ERR_MSG_MOD(opt->extack,
> "failed configuring htb offload");
> @@ -2605,9 +2630,10 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *dev,
> if (opt->command == TC_HTB_NODE_MODIFY)
> return 0;
>
> - err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1);
> + err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
> if (err) {
> - airoha_qdma_set_tx_rate_limit(dev, channel, 0, opt->quantum);
> + airoha_qdma_set_tx_rate_limit(netdev, channel, 0,
> + opt->quantum);
> NL_SET_ERR_MSG_MOD(opt->extack,
> "failed setting real_num_tx_queues");
> return err;
> @@ -2697,11 +2723,12 @@ static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f)
> return 0;
> }
>
> -static int airoha_dev_tc_matchall(struct net_device *dev,
> +static int airoha_dev_tc_matchall(struct net_device *netdev,
> struct tc_cls_matchall_offload *f)
> {
> enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> u32 rate = 0, bucket_size = 0;
>
> switch (f->command) {
> @@ -2736,18 +2763,19 @@ static int airoha_dev_tc_matchall(struct net_device *dev,
> static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
> void *type_data, void *cb_priv)
> {
> - struct net_device *dev = cb_priv;
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct net_device *netdev = cb_priv;
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> struct airoha_eth *eth = port->qdma->eth;
>
> - if (!tc_can_offload(dev))
> + if (!tc_can_offload(netdev))
> return -EOPNOTSUPP;
>
> switch (type) {
> case TC_SETUP_CLSFLOWER:
> return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data);
> case TC_SETUP_CLSMATCHALL:
> - return airoha_dev_tc_matchall(dev, type_data);
> + return airoha_dev_tc_matchall(netdev, type_data);
> default:
> return -EOPNOTSUPP;
> }
> @@ -2794,47 +2822,51 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
> }
> }
>
> -static void airoha_tc_remove_htb_queue(struct net_device *dev, int queue)
> +static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
>
> - netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
> - airoha_qdma_set_tx_rate_limit(dev, queue + 1, 0, 0);
> + netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
> + airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
> clear_bit(queue, port->qos_sq_bmap);
> }
>
> -static int airoha_tc_htb_delete_leaf_queue(struct net_device *dev,
> +static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
> struct tc_htb_qopt_offload *opt)
> {
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
>
> if (!test_bit(channel, port->qos_sq_bmap)) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
> return -EINVAL;
> }
>
> - airoha_tc_remove_htb_queue(dev, channel);
> + airoha_tc_remove_htb_queue(netdev, channel);
>
> return 0;
> }
>
> -static int airoha_tc_htb_destroy(struct net_device *dev)
> +static int airoha_tc_htb_destroy(struct net_device *netdev)
> {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
> int q;
>
> for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> - airoha_tc_remove_htb_queue(dev, q);
> + airoha_tc_remove_htb_queue(netdev, q);
>
> return 0;
> }
>
> -static int airoha_tc_get_htb_get_leaf_queue(struct net_device *dev,
> +static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
> struct tc_htb_qopt_offload *opt)
> {
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port = dev->port;
>
> if (!test_bit(channel, port->qos_sq_bmap)) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
> @@ -2870,8 +2902,8 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
> return 0;
> }
>
> -static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
> - void *type_data)
> +static int airoha_dev_tc_setup(struct net_device *dev,
> + enum tc_setup_type type, void *type_data)
> {
> switch (type) {
> case TC_SETUP_QDISC_ETS:
> @@ -2937,25 +2969,81 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
> }
> }
>
> -bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
> - struct airoha_gdm_port *port)
> +bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
> + struct airoha_gdm_dev *dev)
> {
> int i;
>
> for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
> - if (eth->ports[i] == port)
> + struct airoha_gdm_port *port = eth->ports[i];
> +
> + if (!port)
> + continue;
> +
> + if (port->dev == dev)
> return true;
> }
>
> return false;
> }
>
> +static int airoha_alloc_gdm_device(struct airoha_eth *eth,
> + struct airoha_gdm_port *port,
> + struct device_node *np)
> +{
> + struct airoha_gdm_dev *dev;
> + struct net_device *netdev;
> + int err;
> +
> + netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
> + AIROHA_NUM_NETDEV_TX_RINGS,
> + AIROHA_NUM_RX_RING);
> + if (!netdev) {
> + dev_err(eth->dev, "alloc_etherdev failed\n");
> + return -ENOMEM;
> + }
> +
> + netdev->netdev_ops = &airoha_netdev_ops;
> + netdev->ethtool_ops = &airoha_ethtool_ops;
> + netdev->max_mtu = AIROHA_MAX_MTU;
> + netdev->watchdog_timeo = 5 * HZ;
> + netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO6 |
> + NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
> + NETIF_F_HW_TC;
> + netdev->features |= netdev->hw_features;
> + netdev->vlan_features = netdev->hw_features;
> + netdev->dev.of_node = np;
> + SET_NETDEV_DEV(netdev, eth->dev);
> +
> + /* reserve hw queues for HTB offloading */
> + err = netif_set_real_num_tx_queues(netdev, AIROHA_NUM_TX_RING);
> + if (err)
> + return err;
> +
> + err = of_get_ethdev_address(np, netdev);
> + if (err) {
> + if (err == -EPROBE_DEFER)
> + return err;
> +
> + eth_hw_addr_random(netdev);
> + dev_info(eth->dev, "generated random MAC address %pM\n",
> + netdev->dev_addr);
> + }
> +
> + dev = netdev_priv(netdev);
> + dev->dev = netdev;
> + dev->port = port;
> + port->dev = dev;
> + dev->eth = eth;
> +
> + return 0;
> +}
> +
> static int airoha_alloc_gdm_port(struct airoha_eth *eth,
> struct device_node *np)
> {
> const __be32 *id_ptr = of_get_property(np, "reg", NULL);
> struct airoha_gdm_port *port;
> - struct net_device *dev;
> int err, p;
> u32 id;
>
> @@ -2977,53 +3065,22 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
> return -EINVAL;
> }
>
> - dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
> - AIROHA_NUM_NETDEV_TX_RINGS,
> - AIROHA_NUM_RX_RING);
> - if (!dev) {
> - dev_err(eth->dev, "alloc_etherdev failed\n");
> + port = devm_kzalloc(eth->dev, sizeof(*port), GFP_KERNEL);
> + if (!port)
> return -ENOMEM;
> - }
> -
> - dev->netdev_ops = &airoha_netdev_ops;
> - dev->ethtool_ops = &airoha_ethtool_ops;
> - dev->max_mtu = AIROHA_MAX_MTU;
> - dev->watchdog_timeo = 5 * HZ;
> - dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
> - NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
> - NETIF_F_SG | NETIF_F_TSO |
> - NETIF_F_HW_TC;
> - dev->features |= dev->hw_features;
> - dev->vlan_features = dev->hw_features;
> - dev->dev.of_node = np;
> - SET_NETDEV_DEV(dev, eth->dev);
>
> - /* reserve hw queues for HTB offloading */
> - err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
> - if (err)
> - return err;
> -
> - err = of_get_ethdev_address(np, dev);
> - if (err) {
> - if (err == -EPROBE_DEFER)
> - return err;
> -
> - eth_hw_addr_random(dev);
> - dev_info(eth->dev, "generated random MAC address %pM\n",
> - dev->dev_addr);
> - }
> -
> - port = netdev_priv(dev);
> u64_stats_init(&port->stats.syncp);
> spin_lock_init(&port->stats.lock);
> - port->eth = eth;
> - port->dev = dev;
> port->id = id;
> /* XXX: Read nbq from DTS */
> port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
> eth->ports[p] = port;
>
> - return airoha_metadata_dst_alloc(port);
> + err = airoha_metadata_dst_alloc(port);
> + if (err)
> + return err;
> +
> + return airoha_alloc_gdm_device(eth, port, np);
> }
>
> static int airoha_register_gdm_devices(struct airoha_eth *eth)
> @@ -3037,7 +3094,7 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth)
> if (!port)
> continue;
>
> - err = register_netdev(port->dev);
> + err = register_netdev(port->dev->dev);
> if (err)
> return err;
> }
> @@ -3146,12 +3203,14 @@ static int airoha_probe(struct platform_device *pdev)
>
> for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
> struct airoha_gdm_port *port = eth->ports[i];
> + struct airoha_gdm_dev *dev;
>
> if (!port)
> continue;
>
> - if (port->dev->reg_state == NETREG_REGISTERED)
> - unregister_netdev(port->dev);
> + dev = port->dev;
> + if (dev && dev->dev->reg_state == NETREG_REGISTERED)
> + unregister_netdev(dev->dev);
> airoha_metadata_dst_free(port);
> }
> airoha_hw_cleanup(eth);
> @@ -3172,11 +3231,14 @@ static void airoha_remove(struct platform_device *pdev)
>
> for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
> struct airoha_gdm_port *port = eth->ports[i];
> + struct airoha_gdm_dev *dev;
>
> if (!port)
> continue;
>
> - unregister_netdev(port->dev);
> + dev = port->dev;
> + if (dev)
> + unregister_netdev(dev->dev);
> airoha_metadata_dst_free(port);
> }
> airoha_hw_cleanup(eth);
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index d3781103abb5..c78cabbec753 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -535,10 +535,15 @@ struct airoha_qdma {
> struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
> };
>
> +struct airoha_gdm_dev {
> + struct airoha_gdm_port *port;
> + struct net_device *dev;
> + struct airoha_eth *eth;
> +};
> +
> struct airoha_gdm_port {
> struct airoha_qdma *qdma;
> - struct airoha_eth *eth;
> - struct net_device *dev;
> + struct airoha_gdm_dev *dev;
> int id;
> int nbq;
>
> @@ -662,8 +667,8 @@ static inline bool airoha_is_7583(struct airoha_eth *eth)
> }
>
> int airoha_get_fe_port(struct airoha_gdm_port *port);
> -bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
> - struct airoha_gdm_port *port);
> +bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
> + struct airoha_gdm_dev *dev);
>
> void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id,
> u8 fport);
> diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
> index 26da519236bf..af7af4097b98 100644
> --- a/drivers/net/ethernet/airoha/airoha_ppe.c
> +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
> @@ -298,12 +298,12 @@ static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br,
>
> static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
> struct airoha_foe_entry *hwe,
> - struct net_device *dev, int type,
> + struct net_device *netdev, int type,
> struct airoha_flow_data *data,
> int l4proto)
> {
> u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
> - int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev);
> + int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev);
> struct airoha_foe_mac_info_common *l2;
> u8 smac_id = 0xf;
>
> @@ -319,10 +319,11 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
> hwe->ib1 = val;
>
> val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f);
> - if (dev) {
> + if (netdev) {
> struct airoha_wdma_info info = {};
>
> - if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) {
> + if (!airoha_ppe_get_wdma_info(netdev, data->eth.h_dest,
> + &info)) {
> val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) |
> FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT,
> FE_PSE_PORT_CDM4);
> @@ -332,12 +333,14 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
> FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID,
> info.wcid);
> } else {
> - struct airoha_gdm_port *port = netdev_priv(dev);
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_gdm_port *port;
> u8 pse_port, channel;
>
> - if (!airoha_is_valid_gdm_port(eth, port))
> + if (!airoha_is_valid_gdm_dev(eth, dev))
> return -EINVAL;
>
> + port = dev->port;
> if (dsa_port >= 0 || eth->ports[1])
> pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
> : port->id;
> @@ -1473,7 +1476,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
> void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
> {
> struct airoha_eth *eth = port->qdma->eth;
> - struct net_device *dev = port->dev;
> + struct net_device *dev = port->dev->dev;
> const u8 *addr = dev->dev_addr;
> u32 val;
>
>
> --
> 2.54.0
>
Commenting on sashiko's report:
https://sashiko.dev/#/patchset/20260527-airoha-eth-multi-serdes-preliminary-v1-0-ec6ed73ef7fc%40kernel.org
- This isn't a bug introduced by this patch, but does airoha_get_dsa_tag()
lack packet length and linearization checks?
I do not think this is a real issue since airoha_eth dsa support can run
just with MTK dsa driver that adds MTK_HDR_LEN bytes into the skb
headroom.
Moreover, as pointed out by sashiko, this issue has not been introduced by
this patch.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct
2026-05-27 10:21 ` [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
@ 2026-05-28 12:49 ` Lorenzo Bianconi
0 siblings, 0 replies; 10+ messages in thread
From: Lorenzo Bianconi @ 2026-05-28 12:49 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, linux-arm-kernel, linux-mediatek, netdev,
Xuegang Lu
[-- Attachment #1: Type: text/plain, Size: 7919 bytes --]
> Since now multiple net_devices connected to different QDMA blocks can
> share the same GDM port, qos_sq_bmap field can be overwritten with the
> configuration obtained from a net_device connected to a different QDMA
> block. In order to fix the issue move qos_sq_bmap field from
> airoha_gdm_port struct to airoha_gdm_dev one.
> Add qos_channel_map bitmap in airoha_qdma struct to track if a shared
> QDMA channel is already in use by another net_device.
>
> Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 58 ++++++++++++++++++++------------
> drivers/net/ethernet/airoha/airoha_eth.h | 6 ++--
> 2 files changed, 40 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index c5d25f7640ac..4f77a0c22162 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2602,30 +2602,40 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
> return 0;
> }
>
> -static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> - struct tc_htb_qopt_offload *opt)
> +static int airoha_tc_htb_modify_queue(struct net_device *dev,
> + struct tc_htb_qopt_offload *opt)
> {
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
> - int err, num_tx_queues = netdev->real_num_tx_queues;
> - struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
>
> if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
> return -EINVAL;
> }
>
> - err = airoha_qdma_set_tx_rate_limit(netdev, channel, rate,
> - opt->quantum);
> - if (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,
> + struct tc_htb_qopt_offload *opt)
> +{
> + u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> + int err, num_tx_queues = netdev->real_num_tx_queues;
> + struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + struct airoha_qdma *qdma = dev->qdma;
> +
> + /* Here we need to check the requested QDMA channel is not already
> + * in use by another net_device running on the same QDMA block.
> + */
> + if (test_and_set_bit(channel, qdma->qos_channel_map)) {
> NL_SET_ERR_MSG_MOD(opt->extack,
> - "failed configuring htb offload");
> - return err;
> + "qdma qos channel already in use");
> + return -EBUSY;
> }
>
> - if (opt->command == TC_HTB_NODE_MODIFY)
> - return 0;
> + err = airoha_tc_htb_modify_queue(netdev, opt);
> + if (err)
> + goto error;
>
> err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
> if (err) {
> @@ -2633,13 +2643,17 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
> opt->quantum);
> NL_SET_ERR_MSG_MOD(opt->extack,
> "failed setting real_num_tx_queues");
> - return err;
> + goto error;
> }
>
> - set_bit(channel, port->qos_sq_bmap);
> + set_bit(channel, dev->qos_sq_bmap);
> opt->qid = AIROHA_NUM_TX_RING + channel;
>
> return 0;
> +error:
> + clear_bit(channel, qdma->qos_channel_map);
> +
> + return err;
> }
>
> static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
> @@ -2820,11 +2834,13 @@ static int airoha_dev_setup_tc_block(struct net_device *dev,
> static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
> + struct airoha_qdma *qdma = dev->qdma;
>
> netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
> airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
> - clear_bit(queue, port->qos_sq_bmap);
> +
> + clear_bit(queue, qdma->qos_channel_map);
> + clear_bit(queue, dev->qos_sq_bmap);
> }
>
> static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
> @@ -2832,9 +2848,8 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
> {
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
>
> - if (!test_bit(channel, port->qos_sq_bmap)) {
> + if (!test_bit(channel, dev->qos_sq_bmap)) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
> return -EINVAL;
> }
> @@ -2847,10 +2862,9 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
> static int airoha_tc_htb_destroy(struct net_device *netdev)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
> int q;
>
> - for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> + for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
> airoha_tc_remove_htb_queue(netdev, q);
>
> return 0;
> @@ -2861,9 +2875,8 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
> {
> u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> - struct airoha_gdm_port *port = dev->port;
>
> - if (!test_bit(channel, port->qos_sq_bmap)) {
> + if (!test_bit(channel, dev->qos_sq_bmap)) {
> NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
> return -EINVAL;
> }
> @@ -2882,6 +2895,7 @@ static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
> case TC_HTB_DESTROY:
> return airoha_tc_htb_destroy(dev);
> case TC_HTB_NODE_MODIFY:
> + return airoha_tc_htb_modify_queue(dev, opt);
> case TC_HTB_LEAF_ALLOC_QUEUE:
> return airoha_tc_htb_alloc_leaf_queue(dev, opt);
> case TC_HTB_LEAF_DEL:
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index f6f59d25abd9..a308a770116b 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -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];
> +
> + DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
> };
>
> struct airoha_gdm_dev {
> @@ -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 {
> @@ -549,8 +553,6 @@ struct airoha_gdm_port {
>
> struct airoha_hw_stats stats;
>
> - DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> -
> /* qos stats counters */
> u64 cpu_tx_packets;
> u64 fwd_tx_packets;
>
> --
> 2.54.0
>
commenting on sashiko's report:
https://sashiko.dev/#/patchset/20260527-airoha-eth-multi-serdes-preliminary-v1-0-ec6ed73ef7fc%40kernel.org
- This isn't a bug introduced by this patch, but does this modify operation
skip verifying if the requesting net_device actually owns the channel?
- Since this issue has not been itroduced by this patch, I will fix it with a
dedicated patch.
- This is a pre-existing issue, but does this manage real_num_tx_queues as an
active queue counter rather than a bounding limit?
- Since this issue has not been itroduced by this patch, I will fix it with a
dedicated patch.
- This isn't a bug introduced by this patch, but is there an off-by-one error
when clearing the TX rate limit for the removed queue?
- Since this issue has not been itroduced by this patch, I will fix it with a
dedicated patch.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct
2026-05-27 10:21 ` [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
2026-05-28 12:38 ` Lorenzo Bianconi
@ 2026-05-28 13:42 ` Alexander Lobakin
1 sibling, 0 replies; 10+ messages in thread
From: Alexander Lobakin @ 2026-05-28 13:42 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-arm-kernel, linux-mediatek,
netdev, Xuegang Lu
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Wed, 27 May 2026 12:21:15 +0200
> EN7581 and AN7583 SoCs support connecting multiple external SerDes to GDM3
> or GDM4 ports via a hw arbiter that manages the traffic in a TDM manner.
> As a result multiple net_devices can connect to the same GDM{3,4} port
> and there is a theoretical "1:n" relation between GDM port and
> net_devices.
> Introduce airoha_gdm_dev struct to collect net_device related info (e.g.
> net_device and external phy pointer). Please note this is just a
> preliminary patch and we are still supporting a single net_device for
> each GDM port. Subsequent patches will add support for multiple net_devices
> connected to the same GDM port.
>
> Tested-by: Xuegang Lu <xuegang.lu@airoha.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
[...]
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index d3781103abb5..c78cabbec753 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -535,10 +535,15 @@ struct airoha_qdma {
> struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
> };
>
> +struct airoha_gdm_dev {
> + struct airoha_gdm_port *port;
> + struct net_device *dev;
Nit: we now have priv_to_netdev() (I hope I remember its name
correctly), so that we no longer need to store a netdev backpointer in
netdev_priv structures.
Just an option, up to you.
> + struct airoha_eth *eth;
> +};
> +
> struct airoha_gdm_port {
> struct airoha_qdma *qdma;
> - struct airoha_eth *eth;
> - struct net_device *dev;
> + struct airoha_gdm_dev *dev;
> int id;
> int nbq;
>
Thanks,
Olek
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-05-28 13:42 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-27 10:21 [PATCH net-next 0/6] net: airoha: Preliminary patches to support multiple net_devices connected to the same GDM port Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 1/6] net: airoha: Introduce airoha_gdm_dev struct Lorenzo Bianconi
2026-05-28 12:38 ` Lorenzo Bianconi
2026-05-28 13:42 ` Alexander Lobakin
2026-05-27 10:21 ` [PATCH net-next 2/6] net: airoha: Move airoha_qdma pointer in " Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 3/6] net: airoha: Rely on airoha_gdm_dev pointer in airoha_is_lan_gdm_port() Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 4/6] net: airoha: Move qos_sq_bmap in airoha_gdm_dev struct Lorenzo Bianconi
2026-05-28 12:49 ` Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 5/6] net: airoha: Move {cpu,fwd}_tx_packets " Lorenzo Bianconi
2026-05-27 10:21 ` [PATCH net-next 6/6] net: airoha: Rename airoha_set_gdm2_loopback in airoha_enable_gdm2_loopback Lorenzo Bianconi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox