]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
airoha: introduce HW-GRO support 23828/head
authorLorenzo Bianconi <lorenzo@kernel.org>
Tue, 16 Jun 2026 09:47:18 +0000 (11:47 +0200)
committerChristian Marangi <ansuelsmth@gmail.com>
Wed, 17 Jun 2026 18:05:27 +0000 (20:05 +0200)
- Introduce HW-GRO support in order to allow bridging HW-GRO interfaces
- Enable LRO for queues (7-0)
- Rework LAN/WAN dynamic switching according to upstream requests:
  now in order to set GDM3/GDM4 as WAN it is necessary to enable Qdisc
  offloading without any dedicated ethool command.

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://github.com/openwrt/openwrt/pull/23828
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
20 files changed:
target/linux/airoha/patches-6.18/916-01-net-airoha-use-int-instead-of-atomic_t-for-qdma-user.patch [new file with mode: 0644]
target/linux/airoha/patches-6.18/916-02-net-airoha-Implement-HW-GRO-TCP-support.patch [new file with mode: 0644]
target/linux/airoha/patches-6.18/916-net-airoha-Implement-LRO-TCP-support.patch [deleted file]
target/linux/airoha/patches-6.18/920-01-net-airoha-Introduce-airoha_gdm_dev-struct.patch
target/linux/airoha/patches-6.18/920-02-net-airoha-Move-airoha_qdma-pointer-in-airoha_gdm_de.patch
target/linux/airoha/patches-6.18/920-03-net-airoha-Rely-on-airoha_gdm_dev-pointer-in-airoha_.patch
target/linux/airoha/patches-6.18/920-04-net-airoha-Move-qos_sq_bmap-in-airoha_gdm_dev-struct.patch
target/linux/airoha/patches-6.18/920-05-net-airoha-Move-cpu-fwd-_tx_packets-in-airoha_gdm_de.patch
target/linux/airoha/patches-6.18/920-06-net-airoha-Support-multiple-net_devices-for-a-single.patch
target/linux/airoha/patches-6.18/920-07-net-airoha-Do-not-stop-GDM-port-if-it-is-shared.patch
target/linux/airoha/patches-6.18/920-08-net-airoha-Introduce-WAN-device-flag.patch
target/linux/airoha/patches-6.18/920-09-net-airoha-Support-multiple-LAN-WAN-interfaces-for-h.patch
target/linux/airoha/patches-6.18/920-10-net-airoha-Rename-airoha_set_gdm2_loopback-in-airoha.patch
target/linux/airoha/patches-6.18/920-12-net-airoha-Add-ethtool-priv_flags-callbacks.patch [deleted file]
target/linux/airoha/patches-6.18/920-12-net-airoha-defer-GDM3-GDM4-WAN-mode-and-GDM2-loopbac.patch [new file with mode: 0644]
target/linux/airoha/patches-6.18/920-13-net-airoha-Rework-MTU-configuration.patch
target/linux/airoha/patches-6.18/920-14-net-airoha-Better-handle-MIB-for-GDM-with-multiple-p.patch
target/linux/airoha/patches-6.18/920-15-net-airoha-fix-wrong-airoha_get_fe_port.patch
target/linux/airoha/patches-6.18/921-net-airoha-move-get_sport-callback-at-the-beginning-.patch [new file with mode: 0644]
target/linux/airoha/patches-6.18/922-net-airoha-simplify-WAN-device-check-in-airoha_dev_i.patch [new file with mode: 0644]

diff --git a/target/linux/airoha/patches-6.18/916-01-net-airoha-use-int-instead-of-atomic_t-for-qdma-user.patch b/target/linux/airoha/patches-6.18/916-01-net-airoha-use-int-instead-of-atomic_t-for-qdma-user.patch
new file mode 100644 (file)
index 0000000..5d4ecf6
--- /dev/null
@@ -0,0 +1,49 @@
+From a459b560e58be327689e9bd8c0a6be9a4f163366 Mon Sep 17 00:00:00 2001
+Message-ID: <a459b560e58be327689e9bd8c0a6be9a4f163366.1781641894.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 11 Jun 2026 23:55:51 +0200
+Subject: [PATCH] net: airoha: use int instead of atomic_t for qdma users
+ counter
+
+QDMA users counter is always accessed holding RTNL lock so we do not
+require atomic_t for it.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Reviewed-by: Simon Horman <horms@kernel.org>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 4 ++--
+ drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -1761,7 +1761,7 @@ static int airoha_dev_open(struct net_de
+       airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
+                       GLOBAL_CFG_TX_DMA_EN_MASK |
+                       GLOBAL_CFG_RX_DMA_EN_MASK);
+-      atomic_inc(&qdma->users);
++      qdma->users++;
+       if (port->id == AIROHA_GDM2_IDX &&
+           airoha_ppe_is_enabled(qdma->eth, 1)) {
+@@ -1788,7 +1788,7 @@ static int airoha_dev_stop(struct net_de
+       airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+                                   FE_PSE_PORT_DROP);
+-      if (atomic_dec_and_test(&qdma->users)) {
++      if (!--qdma->users) {
+               airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
+                                 GLOBAL_CFG_TX_DMA_EN_MASK |
+                                 GLOBAL_CFG_RX_DMA_EN_MASK);
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -525,7 +525,7 @@ struct airoha_qdma {
+       struct airoha_eth *eth;
+       void __iomem *regs;
+-      atomic_t users;
++      int users;
+       struct airoha_irq_bank irq_banks[AIROHA_MAX_NUM_IRQ_BANKS];
diff --git a/target/linux/airoha/patches-6.18/916-02-net-airoha-Implement-HW-GRO-TCP-support.patch b/target/linux/airoha/patches-6.18/916-02-net-airoha-Implement-HW-GRO-TCP-support.patch
new file mode 100644 (file)
index 0000000..7598a22
--- /dev/null
@@ -0,0 +1,622 @@
+From 598e1ddfe85ad0f4778eeadd5d878209dd931280 Mon Sep 17 00:00:00 2001
+Message-ID: <598e1ddfe85ad0f4778eeadd5d878209dd931280.1779112628.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Thu, 14 May 2026 20:25:19 +0200
+Subject: [PATCH] net: airoha: Implement LRO TCP support
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c  | 178 ++++++++++++++++++++--
+ drivers/net/ethernet/airoha/airoha_eth.h  |  23 +++
+ drivers/net/ethernet/airoha/airoha_regs.h |  22 ++-
+ 3 files changed, 208 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -11,8 +11,10 @@
+ #include <linux/pcs/pcs.h>
+ #include <linux/u64_stats_sync.h>
+ #include <net/dst_metadata.h>
++#include <net/ip6_checksum.h>
+ #include <net/page_pool/helpers.h>
+ #include <net/pkt_cls.h>
++#include <net/tcp.h>
+ #include <uapi/linux/ppp_defs.h>
+ #include "airoha_regs.h"
+@@ -439,6 +441,73 @@ static void airoha_fe_crsn_qsel_init(str
+                                CDM_CRSN_QSEL_Q1));
+ }
++static void airoha_fe_lro_rxq_enable(struct airoha_eth *eth, int qdma_id,
++                                   int lro_queue_index, int qid,
++                                   int buf_size)
++{
++      int id = qdma_id + 1;
++
++      airoha_fe_rmw(eth, REG_CDM_LRO_LIMIT(id),
++                    CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK,
++                    FIELD_PREP(CDM_LRO_AGG_SIZE_MASK, buf_size) |
++                    FIELD_PREP(CDM_LRO_AGG_NUM_MASK,
++                               AIROHA_RXQ_LRO_MAX_AGG_COUNT));
++      airoha_fe_rmw(eth, REG_CDM_LRO_AGE_TIME(id),
++                    CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK,
++                    FIELD_PREP(CDM_LRO_AGE_TIME_MASK,
++                               AIROHA_RXQ_LRO_MAX_AGE_TIME) |
++                    FIELD_PREP(CDM_LRO_AGG_TIME_MASK,
++                               AIROHA_RXQ_LRO_MAX_AGG_TIME));
++      airoha_fe_rmw(eth, REG_CDM_LRO_RXQ(id, lro_queue_index),
++                    LRO_RXQ_MASK(lro_queue_index),
++                    __field_prep(LRO_RXQ_MASK(lro_queue_index), qid));
++      airoha_fe_set(eth, REG_CDM_LRO_EN(id), BIT(lro_queue_index));
++}
++
++static void airoha_fe_lro_disable(struct airoha_eth *eth, int qdma_id)
++{
++      int i, id = qdma_id + 1;
++
++      airoha_fe_clear(eth, REG_CDM_LRO_EN(id), LRO_RXQ_EN_MASK);
++      airoha_fe_clear(eth, REG_CDM_LRO_LIMIT(id),
++                      CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK);
++      airoha_fe_clear(eth, REG_CDM_LRO_AGE_TIME(id),
++                      CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK);
++      for (i = 0; i < AIROHA_MAX_NUM_LRO_QUEUES; i++)
++              airoha_fe_clear(eth, REG_CDM_LRO_RXQ(id, i), LRO_RXQ_MASK(i));
++}
++
++static bool airoha_fe_lro_is_enabled(struct airoha_eth *eth, int qdma_id)
++{
++      return airoha_fe_get(eth, REG_CDM_LRO_EN(qdma_id + 1),
++                           LRO_RXQ_EN_MASK);
++}
++
++static void airoha_dev_lro_enable(struct airoha_gdm_port *port)
++{
++      struct airoha_qdma *qdma = port->qdma;
++      struct airoha_eth *eth = qdma->eth;
++      int qdma_id = qdma - &eth->qdma[0];
++      int i, lro_queue_index = 0;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++              struct airoha_queue *q = &qdma->q_rx[i];
++              u32 size;
++
++              if (!q->ndesc)
++                      continue;
++
++              if (!airoha_qdma_is_lro_queue(q))
++                      continue;
++
++              size = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
++              size = min_t(u32, size, CDM_LRO_AGG_SIZE_MASK);
++              airoha_fe_lro_rxq_enable(eth, qdma_id, lro_queue_index, i,
++                                       size);
++              lro_queue_index++;
++      }
++}
++
+ static int airoha_fe_init(struct airoha_eth *eth)
+ {
+       airoha_fe_maccr_init(eth);
+@@ -558,6 +627,7 @@ static int airoha_qdma_fill_rx_queue(str
+               e->dma_addr = page_pool_get_dma_addr(page) + offset;
+               e->dma_len = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
++              WRITE_ONCE(desc->tcp_ts_reply, 0);
+               val = FIELD_PREP(QDMA_DESC_LEN_MASK, e->dma_len);
+               WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
+               WRITE_ONCE(desc->addr, cpu_to_le32(e->dma_addr));
+@@ -603,12 +673,169 @@ static int airoha_qdma_get_gdm_port(stru
+       return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
+ }
++static struct sk_buff *airoha_qdma_lro_rx_skb(struct airoha_queue *q,
++                                            struct airoha_qdma_desc *desc,
++                                            struct airoha_queue_entry *e)
++{
++      u32 len, th_off, tcp_ack_seq, agg_count, data_off, data_len;
++      u32 desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
++      u32 msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
++      u32 msg2 = le32_to_cpu(READ_ONCE(desc->msg2));
++      u32 msg3 = le32_to_cpu(READ_ONCE(desc->msg3));
++      struct skb_shared_info *shinfo;
++      u16 tcp_win, l2_len;
++      struct sk_buff *skb;
++      struct tcphdr *th;
++      struct page *page;
++      bool ipv4, ipv6;
++
++      ipv4 = FIELD_GET(QDMA_ETH_RXMSG_IP4_MASK, msg1);
++      ipv6 = FIELD_GET(QDMA_ETH_RXMSG_IP6_MASK, msg1);
++      if (!ipv4 && !ipv6)
++              return NULL;
++
++      l2_len = FIELD_GET(QDMA_ETH_RXMSG_L2_LEN_MASK, msg2);
++      len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
++
++      if (ipv4) {
++              struct iphdr *iph;
++
++              if (len < l2_len + sizeof(*iph))
++                      return NULL;
++
++              iph = (struct iphdr *)(e->buf + l2_len);
++              if (iph->protocol != IPPROTO_TCP)
++                      return NULL;
++
++              if (iph->ihl < 5)
++                      return NULL;
++
++              th_off = l2_len + (iph->ihl << 2);
++              if (len < th_off)
++                      return NULL;
++
++              iph->tot_len = cpu_to_be16(len - l2_len);
++              iph->check = 0;
++              iph->check = ip_fast_csum((void *)iph, iph->ihl);
++      } else {
++              struct ipv6hdr *ip6h;
++
++              th_off = l2_len + sizeof(*ip6h);
++              if (len < th_off)
++                      return NULL;
++
++              ip6h = (struct ipv6hdr *)(e->buf + l2_len);
++              if (ip6h->nexthdr != NEXTHDR_TCP)
++                      return NULL;
++
++              ip6h->payload_len = cpu_to_be16(len - th_off);
++      }
++
++      if (len < th_off + sizeof(*th))
++              return NULL;
++
++      th = (struct tcphdr *)(e->buf + th_off);
++      if (th->doff < 5)
++              return NULL;
++
++      data_off = th_off + (th->doff << 2);
++      if (len < data_off)
++              return NULL;
++
++      tcp_win = FIELD_GET(QDMA_ETH_RXMSG_TCP_WIN_MASK, msg3);
++      tcp_ack_seq = le32_to_cpu(READ_ONCE(desc->data));
++      th->ack_seq = cpu_to_be32(tcp_ack_seq);
++      th->window = cpu_to_be16(tcp_win);
++
++      /* Check tcp timestamp option */
++      if (th->doff == (sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4) {
++              u32 topt = get_unaligned_be32(th + 1);
++
++              if (topt == ((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
++                           (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
++                      u8 *ptr = (u8 *)th + sizeof(*th) + 2 * sizeof(__be32);
++                      __le32 tcp_ts_reply = READ_ONCE(desc->tcp_ts_reply);
++
++                      put_unaligned_be32(le32_to_cpu(tcp_ts_reply), ptr);
++              }
++      }
++
++      if (ipv4) {
++              struct iphdr *iph = (struct iphdr *)(e->buf + l2_len);
++
++              th->check = ~tcp_v4_check(len - th_off, iph->saddr,
++                                        iph->daddr, 0);
++      } else {
++              struct ipv6hdr *ip6h = (struct ipv6hdr *)(e->buf + l2_len);
++
++              th->check = ~tcp_v6_check(len - th_off, &ip6h->saddr,
++                                        &ip6h->daddr, 0);
++      }
++
++      skb = napi_alloc_skb(&q->napi, data_off);
++      if (!skb)
++              return NULL;
++
++      __skb_put(skb, data_off);
++      memcpy(skb->data, e->buf, data_off);
++
++      page = virt_to_head_page(e->buf);
++      data_len = len - data_off;
++      shinfo = skb_shinfo(skb);
++      skb_add_rx_frag(skb, shinfo->nr_frags, page,
++                      e->buf + data_off - page_address(page), data_len,
++                      q->buf_size);
++
++      shinfo->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
++      agg_count = FIELD_GET(QDMA_ETH_RXMSG_AGG_COUNT_MASK, msg2);
++      shinfo->gso_size = DIV_ROUND_UP(data_len, agg_count);
++      shinfo->gso_segs = agg_count;
++
++      skb->csum_start = skb_headroom(skb) + th_off;
++      skb->csum_offset = offsetof(struct tcphdr, check);
++      skb->ip_summed = CHECKSUM_PARTIAL;
++
++      return skb;
++}
++
++static struct sk_buff *airoha_qdma_build_rx_skb(struct airoha_queue *q,
++                                              struct airoha_qdma_desc *desc,
++                                              struct airoha_queue_entry *e,
++                                              struct net_device *netdev)
++{
++      u32 msg2 = le32_to_cpu(READ_ONCE(desc->msg2));
++      int qid = q - &q->qdma->q_rx[0];
++      struct sk_buff *skb;
++
++      if (FIELD_GET(QDMA_ETH_RXMSG_AGG_COUNT_MASK, msg2) > 1) { /* LRO */
++              skb = airoha_qdma_lro_rx_skb(q, desc, e);
++              if (!skb)
++                      return NULL;
++      } else {
++              u32 desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
++              u32 len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
++
++              skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM, q->buf_size);
++              if (!skb)
++                      return NULL;
++
++              skb_reserve(skb, AIROHA_RX_HEADROOM);
++              __skb_put(skb, len);
++              skb->ip_summed = CHECKSUM_UNNECESSARY;
++      }
++
++      skb_mark_for_recycle(skb);
++      skb->dev = netdev;
++      skb_record_rx_queue(skb, qid);
++      skb->protocol = eth_type_trans(skb, netdev);
++
++      return skb;
++}
++
+ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
+ {
+       enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool);
+-      struct airoha_qdma *qdma = q->qdma;
+-      struct airoha_eth *eth = qdma->eth;
+-      int qid = q - &qdma->q_rx[0];
++      struct airoha_eth *eth = q->qdma->eth;
+       int done = 0;
+       while (done < budget) {
+@@ -643,18 +870,9 @@ static int airoha_qdma_rx_process(struct
+               port = eth->ports[p];
+               if (!q->skb) { /* first buffer */
+-                      q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
+-                                              q->buf_size);
++                      q->skb = airoha_qdma_build_rx_skb(q, desc, e, port->dev);
+                       if (!q->skb)
+                               goto free_frag;
+-
+-                      skb_reserve(q->skb, AIROHA_RX_HEADROOM);
+-                      __skb_put(q->skb, len);
+-                      skb_mark_for_recycle(q->skb);
+-                      q->skb->dev = port->dev;
+-                      q->skb->protocol = eth_type_trans(q->skb, port->dev);
+-                      q->skb->ip_summed = CHECKSUM_UNNECESSARY;
+-                      skb_record_rx_queue(q->skb, qid);
+               } else { /* scattered frame */
+                       struct skb_shared_info *shinfo = skb_shinfo(q->skb);
+                       int nr_frags = shinfo->nr_frags;
+@@ -743,12 +961,10 @@ static int airoha_qdma_rx_napi_poll(stru
+ static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
+                                    struct airoha_qdma *qdma, int ndesc)
+ {
+-      const struct page_pool_params pp_params = {
+-              .order = 0,
++      struct page_pool_params pp_params = {
+               .pool_size = 256,
+               .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+               .dma_dir = DMA_FROM_DEVICE,
+-              .max_len = PAGE_SIZE,
+               .nid = NUMA_NO_NODE,
+               .dev = qdma->eth->dev,
+               .napi = &q->napi,
+@@ -756,9 +972,10 @@ static int airoha_qdma_init_rx_queue(str
+       struct airoha_eth *eth = qdma->eth;
+       int qid = q - &qdma->q_rx[0], thr;
+       dma_addr_t dma_addr;
++      bool lro_q;
+-      q->buf_size = PAGE_SIZE / 2;
+       q->qdma = qdma;
++      lro_q = airoha_qdma_is_lro_queue(q);
+       q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
+                               GFP_KERNEL);
+@@ -770,6 +987,9 @@ static int airoha_qdma_init_rx_queue(str
+       if (!q->desc)
+               return -ENOMEM;
++      pp_params.order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0;
++      pp_params.max_len = PAGE_SIZE << pp_params.order;
++
+       q->page_pool = page_pool_create(&pp_params);
+       if (IS_ERR(q->page_pool)) {
+               int err = PTR_ERR(q->page_pool);
+@@ -778,6 +998,7 @@ static int airoha_qdma_init_rx_queue(str
+               return err;
+       }
++      q->buf_size = lro_q ? pp_params.max_len : pp_params.max_len / 2;
+       q->ndesc = ndesc;
+       netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
+@@ -791,7 +1012,12 @@ static int airoha_qdma_init_rx_queue(str
+                       FIELD_PREP(RX_RING_THR_MASK, thr));
+       airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
+                       FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
+-      airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid), RX_RING_SG_EN_MASK);
++      if (lro_q)
++              airoha_qdma_clear(qdma, REG_RX_SCATTER_CFG(qid),
++                                RX_RING_SG_EN_MASK);
++      else
++              airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid),
++                              RX_RING_SG_EN_MASK);
+       airoha_qdma_fill_rx_queue(q);
+@@ -813,6 +1039,7 @@ static void airoha_qdma_cleanup_rx_queue
+                                       page_pool_get_dma_dir(q->page_pool));
+               page_pool_put_full_page(q->page_pool, page, false);
+               /* Reset DMA descriptor */
++              WRITE_ONCE(desc->tcp_ts_reply, 0);
+               WRITE_ONCE(desc->ctrl, 0);
+               WRITE_ONCE(desc->addr, 0);
+               WRITE_ONCE(desc->data, 0);
+@@ -1720,13 +1947,45 @@ static void airoha_update_hw_stats(struc
+       spin_unlock(&port->stats.lock);
+ }
++static void airoha_update_netdev_features(struct airoha_gdm_port *port)
++{
++      struct airoha_eth *eth = port->eth;
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
++              struct airoha_gdm_port *p = eth->ports[i];
++              struct net_device *netdev;
++
++              if (!p)
++                      continue;
++
++              netdev = p->dev;
++              if (netdev->reg_state != NETREG_REGISTERED)
++                      continue;
++
++              netdev_update_features(netdev);
++      }
++}
++
+ static int airoha_dev_open(struct net_device *dev)
+ {
+       int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN;
+       struct airoha_gdm_port *port = netdev_priv(dev);
+       struct airoha_qdma *qdma = port->qdma;
++      struct airoha_eth *eth = qdma->eth;
++      int qdma_id = qdma - &eth->qdma[0];
+       u32 pse_port = FE_PSE_PORT_PPE1;
++      /* HW LRO is configured on the QDMA and it is shared between
++       * all the devices using it. Refuse to open a second device on
++       * the same QDMA if LRO is enabled on any device sharing it.
++       */
++      if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
++              netdev_warn(dev, "required to disable HW GRO on QDMA%d\n",
++                          qdma_id);
++              return -EBUSY;
++      }
++
+ #if defined(CONFIG_PCS_AIROHA)
+       if (airhoa_is_phy_external(port)) {
+               err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
+@@ -1747,13 +2006,13 @@ static int airoha_dev_open(struct net_de
+       /* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
+       if (netdev_uses_dsa(dev) || port->id > 2)
+-              airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
++              airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id),
+                             GDM_STAG_EN_MASK);
+       else
+-              airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
++              airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
+                               GDM_STAG_EN_MASK);
+-      airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
++      airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
+                     GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+                     FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+                     FIELD_PREP(GDM_LONG_LEN_MASK, len));
+@@ -1763,14 +2022,18 @@ static int airoha_dev_open(struct net_de
+                       GLOBAL_CFG_RX_DMA_EN_MASK);
+       qdma->users++;
+-      if (port->id == AIROHA_GDM2_IDX &&
+-          airoha_ppe_is_enabled(qdma->eth, 1)) {
++      if (port->id == AIROHA_GDM2_IDX && airoha_ppe_is_enabled(eth, 1)) {
+               /* For PPE2 always use secondary cpu port. */
+               pse_port = FE_PSE_PORT_PPE2;
+       }
+-      airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+                                   pse_port);
++      if (dev->features & NETIF_F_GRO_HW)
++              airoha_dev_lro_enable(port);
++
++      airoha_update_netdev_features(port);
++
+       return 0;
+ }
+@@ -1778,6 +2041,7 @@ static int airoha_dev_stop(struct net_de
+ {
+       struct airoha_gdm_port *port = netdev_priv(dev);
+       struct airoha_qdma *qdma = port->qdma;
++      struct airoha_eth *eth = qdma->eth;
+       int i;
+       netif_tx_disable(dev);
+@@ -1785,7 +2049,7 @@ static int airoha_dev_stop(struct net_de
+       for (i = 0; i < dev->num_tx_queues; i++)
+               netdev_tx_reset_subqueue(dev, i);
+-      airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+                                   FE_PSE_PORT_DROP);
+       if (!--qdma->users) {
+@@ -1801,6 +2065,8 @@ static int airoha_dev_stop(struct net_de
+               }
+       }
++      airoha_update_netdev_features(port);
++
+ #if defined(CONFIG_PCS_AIROHA)
+       if (airhoa_is_phy_external(port)) {
+               phylink_stop(port->phylink);
+@@ -2034,6 +2300,38 @@ int airoha_get_fe_port(struct airoha_gdm
+       }
+ }
++static netdev_features_t airoha_dev_fix_features(struct net_device *dev,
++                                               netdev_features_t features)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_qdma *qdma = port->qdma;
++
++      if (qdma->users > 1)
++              features &= ~NETIF_F_GRO_HW;
++
++      return features;
++}
++
++static int airoha_dev_set_features(struct net_device *dev,
++                                 netdev_features_t features)
++{
++      netdev_features_t diff = dev->features ^ features;
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_qdma *qdma = port->qdma;
++      struct airoha_eth *eth = qdma->eth;
++      int qdma_id = qdma - &eth->qdma[0];
++
++      if (!(diff & NETIF_F_GRO_HW))
++              return 0;
++
++      if (features & NETIF_F_GRO_HW)
++              airoha_dev_lro_enable(port);
++      else
++              airoha_fe_lro_disable(eth, qdma_id);
++
++      return 0;
++}
++
+ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
+                                  struct net_device *dev)
+ {
+@@ -2933,6 +3231,8 @@ static const struct net_device_ops airoh
+       .ndo_stop               = airoha_dev_stop,
+       .ndo_change_mtu         = airoha_dev_change_mtu,
+       .ndo_select_queue       = airoha_dev_select_queue,
++      .ndo_fix_features       = airoha_dev_fix_features,
++      .ndo_set_features       = airoha_dev_set_features,
+       .ndo_start_xmit         = airoha_dev_xmit,
+       .ndo_get_stats64        = airoha_dev_get_stats64,
+       .ndo_set_mac_address    = airoha_dev_set_macaddr,
+@@ -3150,12 +3450,9 @@ static int airoha_alloc_gdm_port(struct
+       dev->ethtool_ops = &airoha_ethtool_ops;
+       dev->max_mtu = AIROHA_MAX_MTU;
+       dev->watchdog_timeo = 5 * HZ;
+-      dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+-                         NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
+-                         NETIF_F_SG | NETIF_F_TSO |
+-                         NETIF_F_HW_TC;
+-      dev->features |= dev->hw_features;
+-      dev->vlan_features = dev->hw_features;
++      dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
++      dev->features |= AIROHA_HW_FEATURES;
++      dev->vlan_features = AIROHA_HW_FEATURES;
+       dev->dev.of_node = np;
+       SET_NETDEV_DEV(dev, eth->dev);
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -43,6 +43,18 @@
+        (_n) == 15 ? 128 :             \
+        (_n) ==  0 ? 1024 : 16)
++#define AIROHA_LRO_PAGE_ORDER         order_base_2(SZ_16K / PAGE_SIZE)
++#define AIROHA_MAX_NUM_LRO_QUEUES     8
++#define AIROHA_RXQ_LRO_EN_MASK                GENMASK(7, 0)
++#define AIROHA_RXQ_LRO_MAX_AGG_COUNT  64
++#define AIROHA_RXQ_LRO_MAX_AGG_TIME   100
++#define AIROHA_RXQ_LRO_MAX_AGE_TIME   2000
++
++#define AIROHA_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)
++
+ #define PSE_RSV_PAGES                 128
+ #define PSE_QUEUE_RSV_PAGES           64
+@@ -666,6 +678,18 @@ static inline bool airoha_is_7583(struct
+       return eth->soc->version == 0x7583;
+ }
++static inline bool airoha_qdma_is_lro_queue(struct airoha_queue *q)
++{
++      struct airoha_qdma *qdma = q->qdma;
++      int qid = q - &qdma->q_rx[0];
++
++      /* EN7581 SoC supports at most 8 LRO rx queues */
++      BUILD_BUG_ON(hweight32(AIROHA_RXQ_LRO_EN_MASK) >
++                   AIROHA_MAX_NUM_LRO_QUEUES);
++
++      return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
++}
++
+ int airoha_get_fe_port(struct airoha_gdm_port *port);
+ bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
+                             struct airoha_gdm_port *port);
+--- a/drivers/net/ethernet/airoha/airoha_regs.h
++++ b/drivers/net/ethernet/airoha/airoha_regs.h
+@@ -122,6 +122,20 @@
+ #define CDM_CRSN_QSEL_REASON_MASK(_n) \
+       GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
++#define REG_CDM_LRO_RXQ(_n, _m)               (CDM_BASE(_n) + 0x78 + ((_m) & 0x4))
++#define LRO_RXQ_MASK(_n)              GENMASK(4 + (((_n) & 0x3) << 3), ((_n) & 0x3) << 3)
++
++#define REG_CDM_LRO_EN(_n)            (CDM_BASE(_n) + 0x80)
++#define LRO_RXQ_EN_MASK                       GENMASK(7, 0)
++
++#define REG_CDM_LRO_LIMIT(_n)         (CDM_BASE(_n) + 0x84)
++#define CDM_LRO_AGG_NUM_MASK          GENMASK(23, 16)
++#define CDM_LRO_AGG_SIZE_MASK         GENMASK(15, 0)
++
++#define REG_CDM_LRO_AGE_TIME(_n)      (CDM_BASE(_n) + 0x88)
++#define CDM_LRO_AGE_TIME_MASK         GENMASK(31, 16)
++#define CDM_LRO_AGG_TIME_MASK         GENMASK(15, 0)
++
+ #define REG_GDM_FWD_CFG(_n)           GDM_BASE(_n)
+ #define GDM_PAD_EN_MASK                       BIT(28)
+ #define GDM_DROP_CRC_ERR_MASK         BIT(23)
+@@ -895,9 +909,15 @@
+ #define QDMA_ETH_RXMSG_SPORT_MASK     GENMASK(25, 21)
+ #define QDMA_ETH_RXMSG_CRSN_MASK      GENMASK(20, 16)
+ #define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0)
++/* RX MSG2 */
++#define QDMA_ETH_RXMSG_AGG_COUNT_MASK GENMASK(31, 24)
++#define QDMA_ETH_RXMSG_L2_LEN_MASK    GENMASK(6, 0)
++/* RX MSG3 */
++#define QDMA_ETH_RXMSG_AGG_LEN_MASK   GENMASK(31, 16)
++#define QDMA_ETH_RXMSG_TCP_WIN_MASK   GENMASK(15, 0)
+ struct airoha_qdma_desc {
+-      __le32 rsv;
++      __le32 tcp_ts_reply;
+       __le32 ctrl;
+       __le32 addr;
+       __le32 data;
diff --git a/target/linux/airoha/patches-6.18/916-net-airoha-Implement-LRO-TCP-support.patch b/target/linux/airoha/patches-6.18/916-net-airoha-Implement-LRO-TCP-support.patch
deleted file mode 100644 (file)
index 7636ecb..0000000
+++ /dev/null
@@ -1,392 +0,0 @@
-From 598e1ddfe85ad0f4778eeadd5d878209dd931280 Mon Sep 17 00:00:00 2001
-Message-ID: <598e1ddfe85ad0f4778eeadd5d878209dd931280.1779112628.git.lorenzo@kernel.org>
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Thu, 14 May 2026 20:25:19 +0200
-Subject: [PATCH] net: airoha: Implement LRO TCP support
-
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- drivers/net/ethernet/airoha/airoha_eth.c  | 178 ++++++++++++++++++++--
- drivers/net/ethernet/airoha/airoha_eth.h  |  23 +++
- drivers/net/ethernet/airoha/airoha_regs.h |  22 ++-
- 3 files changed, 208 insertions(+), 15 deletions(-)
-
---- a/drivers/net/ethernet/airoha/airoha_eth.c
-+++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -13,6 +13,7 @@
- #include <net/dst_metadata.h>
- #include <net/page_pool/helpers.h>
- #include <net/pkt_cls.h>
-+#include <net/tcp.h>
- #include <uapi/linux/ppp_defs.h>
- #include "airoha_regs.h"
-@@ -439,6 +440,48 @@ static void airoha_fe_crsn_qsel_init(str
-                                CDM_CRSN_QSEL_Q1));
- }
-+static void airoha_fe_lro_init_rx_queue(struct airoha_eth *eth, int qdma_id,
-+                                      int lro_queue_index, int qid,
-+                                      int buf_size)
-+{
-+      int id = qdma_id + 1;
-+
-+      airoha_fe_rmw(eth, REG_CDM_LRO_LIMIT(id),
-+                    CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK,
-+                    FIELD_PREP(CDM_LRO_AGG_SIZE_MASK, buf_size) |
-+                    FIELD_PREP(CDM_LRO_AGG_NUM_MASK,
-+                               AIROHA_RXQ_LRO_MAX_AGG_COUNT));
-+      airoha_fe_rmw(eth, REG_CDM_LRO_AGE_TIME(id),
-+                    CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK,
-+                    FIELD_PREP(CDM_LRO_AGE_TIME_MASK,
-+                               AIROHA_RXQ_LRO_MAX_AGE_TIME) |
-+                    FIELD_PREP(CDM_LRO_AGG_TIME_MASK,
-+                               AIROHA_RXQ_LRO_MAX_AGG_TIME));
-+      airoha_fe_rmw(eth, REG_CDM_LRO_RXQ(id, lro_queue_index),
-+                    LRO_RXQ_MASK(lro_queue_index),
-+                    __field_prep(LRO_RXQ_MASK(lro_queue_index), qid));
-+      airoha_fe_set(eth, REG_CDM_LRO_EN(id), BIT(lro_queue_index));
-+}
-+
-+static void airoha_fe_lro_disable(struct airoha_eth *eth, int qdma_id)
-+{
-+      int i, id = qdma_id + 1;
-+
-+      airoha_fe_clear(eth, REG_CDM_LRO_EN(id), LRO_RXQ_EN_MASK);
-+      airoha_fe_clear(eth, REG_CDM_LRO_LIMIT(id),
-+                      CDM_LRO_AGG_NUM_MASK | CDM_LRO_AGG_SIZE_MASK);
-+      airoha_fe_clear(eth, REG_CDM_LRO_AGE_TIME(id),
-+                      CDM_LRO_AGE_TIME_MASK | CDM_LRO_AGG_TIME_MASK);
-+      for (i = 0; i < AIROHA_MAX_NUM_LRO_QUEUES; i++)
-+              airoha_fe_clear(eth, REG_CDM_LRO_RXQ(id, i), LRO_RXQ_MASK(i));
-+}
-+
-+static bool airoha_fe_lro_is_enabled(struct airoha_eth *eth, int qdma_id)
-+{
-+      return airoha_fe_get(eth, REG_CDM_LRO_EN(qdma_id + 1),
-+                           LRO_RXQ_EN_MASK);
-+}
-+
- static int airoha_fe_init(struct airoha_eth *eth)
- {
-       airoha_fe_maccr_init(eth);
-@@ -603,6 +646,85 @@ static int airoha_qdma_get_gdm_port(stru
-       return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
- }
-+static int airoha_qdma_lro_rx_process(struct airoha_queue *q,
-+                                    struct airoha_qdma_desc *desc)
-+{
-+      u32 desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
-+      u32 msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
-+      u32 msg2 = le32_to_cpu(READ_ONCE(desc->msg2));
-+      u32 msg3 = le32_to_cpu(READ_ONCE(desc->msg3));
-+      struct sk_buff *skb = q->skb;
-+      u32 len, th_off, tcp_ack_seq;
-+      u16 tcp_win, l2_len;
-+      struct tcphdr *th;
-+      bool ipv4, ipv6;
-+
-+      if (FIELD_GET(QDMA_ETH_RXMSG_AGG_COUNT_MASK, msg2) <= 1)
-+              return 0;
-+
-+      ipv4 = FIELD_GET(QDMA_ETH_RXMSG_IP4_MASK, msg1);
-+      ipv6 = FIELD_GET(QDMA_ETH_RXMSG_IP6_MASK, msg1);
-+      if (!ipv4 && !ipv6)
-+              return -EOPNOTSUPP;
-+
-+      l2_len = FIELD_GET(QDMA_ETH_RXMSG_L2_LEN_MASK, msg2);
-+      len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
-+      if (ipv4) {
-+              struct iphdr *iph;
-+
-+              if (!pskb_may_pull(skb, l2_len + sizeof(*iph)))
-+                      return -EINVAL;
-+
-+              iph = (struct iphdr *)(skb->data + l2_len);
-+              if (iph->protocol != IPPROTO_TCP)
-+                      return -EOPNOTSUPP;
-+
-+              iph->tot_len = cpu_to_be16(len - l2_len);
-+              iph->check = 0;
-+              iph->check = ip_fast_csum((void *)iph, iph->ihl);
-+              th_off = l2_len + (iph->ihl << 2);
-+      } else {
-+              struct ipv6hdr *ip6h;
-+
-+              if (!pskb_may_pull(skb, l2_len + sizeof(*ip6h)))
-+                      return -EINVAL;
-+
-+              ip6h = (struct ipv6hdr *)(skb->data + l2_len);
-+              if (ip6h->nexthdr != NEXTHDR_TCP)
-+                      return -EOPNOTSUPP;
-+
-+              ip6h->payload_len = cpu_to_be16(len - l2_len - sizeof(*ip6h));
-+              th_off = l2_len + sizeof(*ip6h);
-+      }
-+
-+      tcp_win = FIELD_GET(QDMA_ETH_RXMSG_TCP_WIN_MASK, msg3);
-+      tcp_ack_seq = le32_to_cpu(READ_ONCE(desc->data));
-+
-+      if (!pskb_may_pull(skb, th_off + sizeof(*th)))
-+              return -EINVAL;
-+
-+      th = (struct tcphdr *)(skb->data + th_off);
-+      th->ack_seq = cpu_to_be32(tcp_ack_seq);
-+      th->window = cpu_to_be16(tcp_win);
-+
-+      /* Check tcp timestamp option */
-+      if (th->doff == (sizeof(*th) + TCPOLEN_TSTAMP_ALIGNED) / 4) {
-+              __be32 *topt = (__be32 *)(th + 1);
-+
-+              if (*topt == cpu_to_be32((TCPOPT_NOP << 24) |
-+                                       (TCPOPT_NOP << 16) |
-+                                       (TCPOPT_TIMESTAMP << 8) |
-+                                       TCPOLEN_TIMESTAMP)) {
-+                      __le32 tcp_ts_reply = READ_ONCE(desc->tcp_ts_reply);
-+
-+                      put_unaligned_be32(le32_to_cpu(tcp_ts_reply),
-+                                         topt + 2);
-+              }
-+      }
-+
-+      return 0;
-+}
-+
- static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
- {
-       enum dma_data_direction dir = page_pool_get_dma_dir(q->page_pool);
-@@ -650,11 +772,15 @@ static int airoha_qdma_rx_process(struct
-                       skb_reserve(q->skb, AIROHA_RX_HEADROOM);
-                       __skb_put(q->skb, len);
--                      skb_mark_for_recycle(q->skb);
-                       q->skb->dev = port->dev;
--                      q->skb->protocol = eth_type_trans(q->skb, port->dev);
-                       q->skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       skb_record_rx_queue(q->skb, qid);
-+
-+                      if (airoha_qdma_lro_rx_process(q, desc) < 0)
-+                              goto free_frag;
-+
-+                      q->skb->protocol = eth_type_trans(q->skb, port->dev);
-+                      skb_mark_for_recycle(q->skb);
-               } else { /* scattered frame */
-                       struct skb_shared_info *shinfo = skb_shinfo(q->skb);
-                       int nr_frags = shinfo->nr_frags;
-@@ -743,12 +869,10 @@ static int airoha_qdma_rx_napi_poll(stru
- static int airoha_qdma_init_rx_queue(struct airoha_queue *q,
-                                    struct airoha_qdma *qdma, int ndesc)
- {
--      const struct page_pool_params pp_params = {
--              .order = 0,
-+      struct page_pool_params pp_params = {
-               .pool_size = 256,
-               .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
-               .dma_dir = DMA_FROM_DEVICE,
--              .max_len = PAGE_SIZE,
-               .nid = NUMA_NO_NODE,
-               .dev = qdma->eth->dev,
-               .napi = &q->napi,
-@@ -756,9 +880,10 @@ static int airoha_qdma_init_rx_queue(str
-       struct airoha_eth *eth = qdma->eth;
-       int qid = q - &qdma->q_rx[0], thr;
-       dma_addr_t dma_addr;
-+      bool lro_q;
--      q->buf_size = PAGE_SIZE / 2;
-       q->qdma = qdma;
-+      lro_q = airoha_qdma_is_lro_queue(q);
-       q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry),
-                               GFP_KERNEL);
-@@ -770,6 +895,9 @@ static int airoha_qdma_init_rx_queue(str
-       if (!q->desc)
-               return -ENOMEM;
-+      pp_params.order = lro_q ? AIROHA_LRO_PAGE_ORDER : 0;
-+      pp_params.max_len = PAGE_SIZE << pp_params.order;
-+
-       q->page_pool = page_pool_create(&pp_params);
-       if (IS_ERR(q->page_pool)) {
-               int err = PTR_ERR(q->page_pool);
-@@ -778,6 +906,7 @@ static int airoha_qdma_init_rx_queue(str
-               return err;
-       }
-+      q->buf_size = lro_q ? pp_params.max_len : pp_params.max_len / 2;
-       q->ndesc = ndesc;
-       netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
-@@ -2034,6 +2163,67 @@ int airoha_get_fe_port(struct airoha_gdm
-       }
- }
-+static int airoha_dev_set_features(struct net_device *dev,
-+                                 netdev_features_t features)
-+{
-+      netdev_features_t diff = dev->features ^ features;
-+      struct airoha_gdm_port *port = netdev_priv(dev);
-+      struct airoha_qdma *qdma = port->qdma;
-+      struct airoha_eth *eth = qdma->eth;
-+      int qdma_id = qdma - &eth->qdma[0];
-+      int i;
-+
-+      if (!(diff & NETIF_F_LRO))
-+              return 0;
-+
-+      if (netif_running(dev))
-+              return -EBUSY;
-+
-+      /* reset LRO configuration */
-+      if (features & NETIF_F_LRO) {
-+              int lro_queue_index = 0;
-+
-+              if (airoha_fe_lro_is_enabled(eth, qdma_id))
-+                      return 0;
-+
-+              for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
-+                      struct airoha_queue *q = &qdma->q_rx[i];
-+                      u32 size;
-+
-+                      if (!q->ndesc)
-+                              continue;
-+
-+                      if (!airoha_qdma_is_lro_queue(q))
-+                              continue;
-+
-+                      size = SKB_WITH_OVERHEAD(AIROHA_RX_LEN(q->buf_size));
-+                      size = min_t(u32, size, CDM_LRO_AGG_SIZE_MASK);
-+                      airoha_fe_lro_init_rx_queue(eth, qdma_id,
-+                                                  lro_queue_index, i, size);
-+                      lro_queue_index++;
-+              }
-+      } else {
-+              for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
-+                      struct airoha_gdm_port *p = eth->ports[i];
-+
-+                      if (!p)
-+                              continue;
-+
-+                      if (p->qdma != qdma)
-+                              continue;
-+
-+                      if (p->dev == dev)
-+                              continue;
-+
-+                      if (p->dev->features & NETIF_F_LRO)
-+                              return 0;
-+              }
-+              airoha_fe_lro_disable(eth, qdma_id);
-+      }
-+
-+      return 0;
-+}
-+
- static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
-                                  struct net_device *dev)
- {
-@@ -2933,6 +3123,7 @@ static const struct net_device_ops airoh
-       .ndo_stop               = airoha_dev_stop,
-       .ndo_change_mtu         = airoha_dev_change_mtu,
-       .ndo_select_queue       = airoha_dev_select_queue,
-+      .ndo_set_features       = airoha_dev_set_features,
-       .ndo_start_xmit         = airoha_dev_xmit,
-       .ndo_get_stats64        = airoha_dev_get_stats64,
-       .ndo_set_mac_address    = airoha_dev_set_macaddr,
-@@ -3150,12 +3341,9 @@ static int airoha_alloc_gdm_port(struct
-       dev->ethtool_ops = &airoha_ethtool_ops;
-       dev->max_mtu = AIROHA_MAX_MTU;
-       dev->watchdog_timeo = 5 * HZ;
--      dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
--                         NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
--                         NETIF_F_SG | NETIF_F_TSO |
--                         NETIF_F_HW_TC;
--      dev->features |= dev->hw_features;
--      dev->vlan_features = dev->hw_features;
-+      dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
-+      dev->features |= AIROHA_HW_FEATURES;
-+      dev->vlan_features = AIROHA_HW_FEATURES;
-       dev->dev.of_node = np;
-       SET_NETDEV_DEV(dev, eth->dev);
---- a/drivers/net/ethernet/airoha/airoha_eth.h
-+++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -43,6 +43,18 @@
-        (_n) == 15 ? 128 :             \
-        (_n) ==  0 ? 1024 : 16)
-+#define AIROHA_LRO_PAGE_ORDER         2
-+#define AIROHA_MAX_NUM_LRO_QUEUES     8
-+#define AIROHA_RXQ_LRO_EN_MASK                0xff000000
-+#define AIROHA_RXQ_LRO_MAX_AGG_COUNT  64
-+#define AIROHA_RXQ_LRO_MAX_AGG_TIME   100
-+#define AIROHA_RXQ_LRO_MAX_AGE_TIME   2000 /* 1ms */
-+
-+#define AIROHA_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)
-+
- #define PSE_RSV_PAGES                 128
- #define PSE_QUEUE_RSV_PAGES           64
-@@ -666,6 +678,18 @@ static inline bool airoha_is_7583(struct
-       return eth->soc->version == 0x7583;
- }
-+static inline bool airoha_qdma_is_lro_queue(struct airoha_queue *q)
-+{
-+      struct airoha_qdma *qdma = q->qdma;
-+      int qid = q - &qdma->q_rx[0];
-+
-+      /* EN7581 SoC supports at most 8 LRO rx queues */
-+      BUILD_BUG_ON(hweight32(AIROHA_RXQ_LRO_EN_MASK) >
-+                   AIROHA_MAX_NUM_LRO_QUEUES);
-+
-+      return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
-+}
-+
- int airoha_get_fe_port(struct airoha_gdm_port *port);
- bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
-                             struct airoha_gdm_port *port);
---- a/drivers/net/ethernet/airoha/airoha_regs.h
-+++ b/drivers/net/ethernet/airoha/airoha_regs.h
-@@ -122,6 +122,20 @@
- #define CDM_CRSN_QSEL_REASON_MASK(_n) \
-       GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
-+#define REG_CDM_LRO_RXQ(_n, _m)               (CDM_BASE(_n) + 0x78 + ((_m) & 0x4))
-+#define LRO_RXQ_MASK(_n)              GENMASK(4 + (((_n) & 0x3) << 3), ((_n) & 0x3) << 3)
-+
-+#define REG_CDM_LRO_EN(_n)            (CDM_BASE(_n) + 0x80)
-+#define LRO_RXQ_EN_MASK                       GENMASK(7, 0)
-+
-+#define REG_CDM_LRO_LIMIT(_n)         (CDM_BASE(_n) + 0x84)
-+#define CDM_LRO_AGG_NUM_MASK          GENMASK(23, 16)
-+#define CDM_LRO_AGG_SIZE_MASK         GENMASK(15, 0)
-+
-+#define REG_CDM_LRO_AGE_TIME(_n)      (CDM_BASE(_n) + 0x88)
-+#define CDM_LRO_AGE_TIME_MASK         GENMASK(31, 16)
-+#define CDM_LRO_AGG_TIME_MASK         GENMASK(15, 0)
-+
- #define REG_GDM_FWD_CFG(_n)           GDM_BASE(_n)
- #define GDM_PAD_EN_MASK                       BIT(28)
- #define GDM_DROP_CRC_ERR_MASK         BIT(23)
-@@ -895,9 +909,15 @@
- #define QDMA_ETH_RXMSG_SPORT_MASK     GENMASK(25, 21)
- #define QDMA_ETH_RXMSG_CRSN_MASK      GENMASK(20, 16)
- #define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0)
-+/* RX MSG2 */
-+#define QDMA_ETH_RXMSG_AGG_COUNT_MASK GENMASK(31, 24)
-+#define QDMA_ETH_RXMSG_L2_LEN_MASK    GENMASK(6, 0)
-+/* RX MSG3 */
-+#define QDMA_ETH_RXMSG_AGG_LEN_MASK   GENMASK(31, 16)
-+#define QDMA_ETH_RXMSG_TCP_WIN_MASK   GENMASK(15, 0)
- struct airoha_qdma_desc {
--      __le32 rsv;
-+      __le32 tcp_ts_reply;
-       __le32 ctrl;
-       __le32 addr;
-       __le32 data;
index 6d2eee04462105e63adbf9446f54157e60b6bf84..f92593c504a29ae60329ae0337656c1480784da4 100644 (file)
@@ -25,7 +25,18 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -738,6 +738,7 @@ static int airoha_qdma_rx_process(struct
+@@ -483,8 +483,9 @@ static bool airoha_fe_lro_is_enabled(str
+                            LRO_RXQ_EN_MASK);
+ }
+-static void airoha_dev_lro_enable(struct airoha_gdm_port *port)
++static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
+ {
++      struct airoha_gdm_port *port = dev->port;
+       struct airoha_qdma *qdma = port->qdma;
+       struct airoha_eth *eth = qdma->eth;
+       int qdma_id = qdma - &eth->qdma[0];
+@@ -843,6 +844,7 @@ static int airoha_qdma_rx_process(struct
                struct airoha_qdma_desc *desc = &q->desc[q->tail];
                u32 hash, reason, msg1, desc_ctrl;
                struct airoha_gdm_port *port;
@@ -33,35 +44,21 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                int data_len, len, p;
                struct page *page;
  
-@@ -764,6 +765,10 @@ static int airoha_qdma_rx_process(struct
+@@ -869,8 +871,12 @@ static int airoha_qdma_rx_process(struct
                        goto free_frag;
  
                port = eth->ports[p];
 +              if (!port->dev)
 +                      goto free_frag;
 +
-+              netdev = port->dev->dev;
++              netdev = netdev_from_priv(port->dev);
                if (!q->skb) { /* first buffer */
-                       q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
-                                               q->buf_size);
-@@ -772,14 +777,14 @@ static int airoha_qdma_rx_process(struct
-                       skb_reserve(q->skb, AIROHA_RX_HEADROOM);
-                       __skb_put(q->skb, len);
--                      q->skb->dev = port->dev;
-+                      q->skb->dev = netdev;
-                       q->skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       skb_record_rx_queue(q->skb, qid);
-                       if (airoha_qdma_lro_rx_process(q, desc) < 0)
+-                      q->skb = airoha_qdma_build_rx_skb(q, desc, e, port->dev);
++                      q->skb = airoha_qdma_build_rx_skb(q, desc, e, netdev);
+                       if (!q->skb)
                                goto free_frag;
--                      q->skb->protocol = eth_type_trans(q->skb, port->dev);
-+                      q->skb->protocol = eth_type_trans(q->skb, netdev);
-                       skb_mark_for_recycle(q->skb);
                } else { /* scattered frame */
-                       struct skb_shared_info *shinfo = skb_shinfo(q->skb);
-@@ -796,7 +801,7 @@ static int airoha_qdma_rx_process(struct
+@@ -888,7 +894,7 @@ static int airoha_qdma_rx_process(struct
                if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
                        continue;
  
@@ -70,33 +67,55 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        /* PPE module requires untagged packets to work
                         * properly and it provides DSA port index via the
                         * DMA descriptor. Report DSA tag to the DSA stack
-@@ -993,6 +998,7 @@ static void airoha_qdma_wake_netdev_txqs
+@@ -1091,6 +1097,7 @@ static void airoha_qdma_wake_netdev_txqs
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
-+              struct airoha_gdm_dev *dev;
++              struct net_device *netdev;
                int j;
  
                if (!port)
-@@ -1001,11 +1007,12 @@ static void airoha_qdma_wake_netdev_txqs
+@@ -1099,11 +1106,12 @@ static void airoha_qdma_wake_netdev_txqs
                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++) {
++              netdev = netdev_from_priv(port->dev);
++              for (j = 0; j < netdev->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);
++                      netif_wake_subqueue(netdev, j);
                }
        }
        q->txq_stopped = false;
-@@ -1849,33 +1856,34 @@ static void airoha_update_hw_stats(struc
+@@ -1947,9 +1955,10 @@ static void airoha_update_hw_stats(struc
        spin_unlock(&port->stats.lock);
  }
  
+-static void airoha_update_netdev_features(struct airoha_gdm_port *port)
++static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
+ {
+-      struct airoha_eth *eth = port->eth;
++      struct airoha_gdm_port *port = dev->port;
++      struct airoha_eth *eth = dev->eth;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+@@ -1959,7 +1968,7 @@ static void airoha_update_netdev_feature
+               if (!p)
+                       continue;
+-              netdev = p->dev;
++              netdev = netdev_from_priv(p->dev);
+               if (netdev->reg_state != NETREG_REGISTERED)
+                       continue;
+@@ -1967,10 +1976,11 @@ static void airoha_update_netdev_feature
+       }
+ }
 -static int airoha_dev_open(struct net_device *dev)
 +static int airoha_dev_open(struct net_device *netdev)
  {
@@ -106,16 +125,28 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      struct airoha_gdm_dev *dev = netdev_priv(netdev);
 +      struct airoha_gdm_port *port = dev->port;
        struct airoha_qdma *qdma = port->qdma;
-       u32 pse_port = FE_PSE_PORT_PPE1;
+       struct airoha_eth *eth = qdma->eth;
+       int qdma_id = qdma - &eth->qdma[0];
+@@ -1981,31 +1991,32 @@ static int airoha_dev_open(struct net_de
+        * the same QDMA if LRO is enabled on any device sharing it.
+        */
+       if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
+-              netdev_warn(dev, "required to disable HW GRO on QDMA%d\n",
++              netdev_warn(netdev, "required to disable HW GRO on QDMA%d\n",
+                           qdma_id);
+               return -EBUSY;
+       }
  
  #if defined(CONFIG_PCS_AIROHA)
        if (airhoa_is_phy_external(port)) {
 -              err = phylink_of_phy_connect(port->phylink, dev->dev.of_node, 0);
-+              err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
++              err = phylink_of_phy_connect(dev->phylink,
++                                           netdev->dev.of_node, 0);
                if (err) {
 -                      netdev_err(dev, "%s: could not attach PHY: %d\n", __func__,
-+                      netdev_err(netdev, "%s: could not attach PHY: %d\n", __func__,
-                                  err);
+-                                 err);
++                      netdev_err(netdev, "%s: could not attach PHY: %d\n",
++                                 __func__, err);
                        return err;
                }
  
@@ -133,10 +164,21 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        /* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
 -      if (netdev_uses_dsa(dev) || port->id > 2)
 +      if (netdev_uses_dsa(netdev) || port->id > 2)
-               airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+               airoha_fe_set(eth, REG_GDM_INGRESS_CFG(port->id),
                              GDM_STAG_EN_MASK);
        else
-@@ -1903,16 +1911,17 @@ static int airoha_dev_open(struct net_de
+@@ -2029,25 +2040,26 @@ static int airoha_dev_open(struct net_de
+       airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+                                   pse_port);
+-      if (dev->features & NETIF_F_GRO_HW)
+-              airoha_dev_lro_enable(port);
++      if (netdev->features & NETIF_F_GRO_HW)
++              airoha_dev_lro_enable(dev);
+-      airoha_update_netdev_features(port);
++      airoha_update_netdev_features(dev);
        return 0;
  }
  
@@ -147,6 +189,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      struct airoha_gdm_dev *dev = netdev_priv(netdev);
 +      struct airoha_gdm_port *port = dev->port;
        struct airoha_qdma *qdma = port->qdma;
+       struct airoha_eth *eth = qdma->eth;
        int i;
  
 -      netif_tx_disable(dev);
@@ -157,9 +200,14 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      for (i = 0; i < netdev->num_tx_queues; i++)
 +              netdev_tx_reset_subqueue(netdev, i);
  
-       airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+       airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
                                    FE_PSE_PORT_DROP);
-@@ -1932,24 +1941,25 @@ static int airoha_dev_stop(struct net_de
+@@ -2065,28 +2077,29 @@ static int airoha_dev_stop(struct net_de
+               }
+       }
+-      airoha_update_netdev_features(port);
++      airoha_update_netdev_features(dev);
  
  #if defined(CONFIG_PCS_AIROHA)
        if (airhoa_is_phy_external(port)) {
@@ -191,7 +239,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        return 0;
  }
-@@ -2015,16 +2025,17 @@ static int airoha_set_gdm2_loopback(stru
+@@ -2152,16 +2165,17 @@ static int airoha_set_gdm2_loopback(stru
        return 0;
  }
  
@@ -209,12 +257,12 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
 -      port->dev->irq = port->qdma->irq_banks[0].irq;
 -      airoha_set_macaddr(port, dev->dev_addr);
-+      dev->dev->irq = port->qdma->irq_banks[0].irq;
++      netdev->irq = port->qdma->irq_banks[0].irq;
 +      airoha_set_macaddr(port, netdev->dev_addr);
  
        switch (port->id) {
        case AIROHA_GDM3_IDX:
-@@ -2049,10 +2060,11 @@ static int airoha_dev_init(struct net_de
+@@ -2186,10 +2200,11 @@ static int airoha_dev_init(struct net_de
        return 0;
  }
  
@@ -228,7 +276,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        unsigned int start;
  
        airoha_update_hw_stats(port);
-@@ -2071,36 +2083,39 @@ static void airoha_dev_get_stats64(struc
+@@ -2208,36 +2223,39 @@ static void airoha_dev_get_stats64(struc
        } while (u64_stats_fetch_retry(&port->stats.syncp, start));
  }
  
@@ -275,10 +323,24 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
  
  static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
-@@ -2163,11 +2178,12 @@ int airoha_get_fe_port(struct airoha_gdm
+@@ -2300,10 +2318,11 @@ int airoha_get_fe_port(struct airoha_gdm
        }
  }
  
+-static netdev_features_t airoha_dev_fix_features(struct net_device *dev,
++static netdev_features_t airoha_dev_fix_features(struct net_device *netdev,
+                                                netdev_features_t features)
+ {
+-      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_gdm_dev *dev = netdev_priv(netdev);
++      struct airoha_gdm_port *port = dev->port;
+       struct airoha_qdma *qdma = port->qdma;
+       if (qdma->users > 1)
+@@ -2312,11 +2331,12 @@ static netdev_features_t airoha_dev_fix_
+       return features;
+ }
 -static int airoha_dev_set_features(struct net_device *dev,
 +static int airoha_dev_set_features(struct net_device *netdev,
                                   netdev_features_t features)
@@ -291,38 +353,16 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        struct airoha_qdma *qdma = port->qdma;
        struct airoha_eth *eth = qdma->eth;
        int qdma_id = qdma - &eth->qdma[0];
-@@ -2176,7 +2192,7 @@ static int airoha_dev_set_features(struc
-       if (!(diff & NETIF_F_LRO))
+@@ -2325,7 +2345,7 @@ static int airoha_dev_set_features(struc
                return 0;
  
--      if (netif_running(dev))
-+      if (netif_running(netdev))
-               return -EBUSY;
-       /* reset LRO configuration */
-@@ -2205,6 +2221,7 @@ static int airoha_dev_set_features(struc
-       } else {
-               for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
-                       struct airoha_gdm_port *p = eth->ports[i];
-+                      struct airoha_gdm_dev *d;
-                       if (!p)
-                               continue;
-@@ -2212,10 +2229,11 @@ static int airoha_dev_set_features(struc
-                       if (p->qdma != qdma)
-                               continue;
--                      if (p->dev == dev)
-+                      d = p->dev;
-+                      if (d->dev == netdev)
-                               continue;
--                      if (p->dev->features & NETIF_F_LRO)
-+                      if (d->dev->features & NETIF_F_LRO)
-                               return 0;
-               }
+       if (features & NETIF_F_GRO_HW)
+-              airoha_dev_lro_enable(port);
++              airoha_dev_lro_enable(dev);
+       else
                airoha_fe_lro_disable(eth, qdma_id);
-@@ -2225,9 +2243,10 @@ static int airoha_dev_set_features(struc
+@@ -2333,9 +2353,10 @@ static int airoha_dev_set_features(struc
  }
  
  static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
@@ -335,7 +375,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        struct airoha_qdma *qdma = port->qdma;
        u32 nr_frags, tag, msg0, msg1, len;
        struct airoha_queue_entry *e;
-@@ -2240,7 +2259,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2348,7 +2369,7 @@ static netdev_tx_t airoha_dev_xmit(struc
        u8 fport;
  
        qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
@@ -344,7 +384,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
                          qid / AIROHA_NUM_QOS_QUEUES) |
-@@ -2276,7 +2295,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2384,7 +2405,7 @@ static netdev_tx_t airoha_dev_xmit(struc
  
        spin_lock_bh(&q->lock);
  
@@ -353,7 +393,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        nr_frags = 1 + skb_shinfo(skb)->nr_frags;
  
        if (q->queued + nr_frags >= q->ndesc) {
-@@ -2300,9 +2319,9 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2408,9 +2429,9 @@ static netdev_tx_t airoha_dev_xmit(struc
                dma_addr_t addr;
                u32 val;
  
@@ -365,7 +405,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        goto error_unmap;
  
                list_move_tail(&e->list, &tx_list);
-@@ -2351,7 +2370,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2459,7 +2480,7 @@ static netdev_tx_t airoha_dev_xmit(struc
  
  error_unmap:
        list_for_each_entry(e, &tx_list, list) {
@@ -374,7 +414,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                                 DMA_TO_DEVICE);
                e->dma_addr = 0;
        }
-@@ -2360,25 +2379,27 @@ error_unmap:
+@@ -2468,25 +2489,27 @@ error_unmap:
        spin_unlock_bh(&q->lock);
  error:
        dev_kfree_skb_any(skb);
@@ -407,7 +447,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        unsigned int start;
  
        airoha_update_hw_stats(port);
-@@ -2406,11 +2427,12 @@ static const struct ethtool_rmon_hist_ra
+@@ -2514,11 +2537,12 @@ static const struct ethtool_rmon_hist_ra
  };
  
  static void
@@ -422,7 +462,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        struct airoha_hw_stats *hw_stats = &port->stats;
        unsigned int start;
  
-@@ -2435,11 +2457,12 @@ airoha_ethtool_get_rmon_stats(struct net
+@@ -2543,11 +2567,12 @@ airoha_ethtool_get_rmon_stats(struct net
        } while (u64_stats_fetch_retry(&port->stats.syncp, start));
  }
  
@@ -437,7 +477,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        int i;
  
        for (i = 0; i < AIROHA_NUM_TX_RING; i++)
-@@ -2524,10 +2547,12 @@ static int airoha_qdma_set_tx_ets_sched(
+@@ -2632,10 +2657,12 @@ static int airoha_qdma_set_tx_ets_sched(
                                             ARRAY_SIZE(w));
  }
  
@@ -452,7 +492,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
                                            REG_CNTR_VAL(channel << 1));
        u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
-@@ -2789,11 +2814,12 @@ static int airoha_qdma_set_trtcm_token_b
+@@ -2897,11 +2924,12 @@ static int airoha_qdma_set_trtcm_token_b
                                           mode, val);
  }
  
@@ -467,7 +507,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        int i, err;
  
        for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
-@@ -2813,20 +2839,22 @@ static int airoha_qdma_set_tx_rate_limit
+@@ -2921,20 +2949,22 @@ static int airoha_qdma_set_tx_rate_limit
        return 0;
  }
  
@@ -494,7 +534,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        if (err) {
                NL_SET_ERR_MSG_MOD(opt->extack,
                                   "failed configuring htb offload");
-@@ -2836,9 +2864,10 @@ static int airoha_tc_htb_alloc_leaf_queu
+@@ -2944,9 +2974,10 @@ static int airoha_tc_htb_alloc_leaf_queu
        if (opt->command == TC_HTB_NODE_MODIFY)
                return 0;
  
@@ -507,7 +547,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                NL_SET_ERR_MSG_MOD(opt->extack,
                                   "failed setting real_num_tx_queues");
                return err;
-@@ -2928,11 +2957,12 @@ static int airoha_tc_matchall_act_valida
+@@ -3036,11 +3067,12 @@ static int airoha_tc_matchall_act_valida
        return 0;
  }
  
@@ -522,7 +562,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 rate = 0, bucket_size = 0;
  
        switch (f->command) {
-@@ -2967,18 +2997,19 @@ static int airoha_dev_tc_matchall(struct
+@@ -3075,18 +3107,19 @@ static int airoha_dev_tc_matchall(struct
  static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type,
                                        void *type_data, void *cb_priv)
  {
@@ -546,7 +586,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        default:
                return -EOPNOTSUPP;
        }
-@@ -3025,47 +3056,51 @@ static int airoha_dev_setup_tc_block(str
+@@ -3133,47 +3166,51 @@ static int airoha_dev_setup_tc_block(str
        }
  }
  
@@ -610,7 +650,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        if (!test_bit(channel, port->qos_sq_bmap)) {
                NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
-@@ -3101,8 +3136,8 @@ static int airoha_tc_setup_qdisc_htb(str
+@@ -3209,8 +3246,8 @@ static int airoha_tc_setup_qdisc_htb(str
        return 0;
  }
  
@@ -621,7 +661,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  {
        switch (type) {
        case TC_SETUP_QDISC_ETS:
-@@ -3174,13 +3209,18 @@ static void airoha_metadata_dst_free(str
+@@ -3283,13 +3320,18 @@ static void airoha_metadata_dst_free(str
        }
  }
  
@@ -643,7 +683,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        return true;
        }
  
-@@ -3192,8 +3232,9 @@ static void airoha_mac_link_up(struct ph
+@@ -3301,8 +3343,9 @@ static void airoha_mac_link_up(struct ph
                               unsigned int mode, phy_interface_t interface,
                               int speed, int duplex, bool tx_pause, bool rx_pause)
  {
@@ -655,7 +695,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        struct airoha_qdma *qdma = port->qdma;
        struct airoha_eth *eth = qdma->eth;
        u32 frag_size_tx, frag_size_rx;
-@@ -3249,65 +3290,122 @@ static int airoha_fill_available_pcs(str
+@@ -3358,65 +3401,121 @@ static int airoha_fill_available_pcs(str
                                        &num_available_pcs);
  }
  
@@ -753,7 +793,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      netdev->ethtool_ops = &airoha_ethtool_ops;
 +      netdev->max_mtu = AIROHA_MAX_MTU;
 +      netdev->watchdog_timeo = 5 * HZ;
-+      netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
++      netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
 +      netdev->features |= AIROHA_HW_FEATURES;
 +      netdev->vlan_features = AIROHA_HW_FEATURES;
 +      netdev->dev.of_node = np;
@@ -775,7 +815,6 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      }
 +
 +      dev = netdev_priv(netdev);
-+      dev->dev = netdev;
 +      dev->port = port;
 +      port->dev = dev;
 +      dev->eth = eth;
@@ -800,7 +839,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        int err, p;
        u32 id;
  
-@@ -3329,58 +3427,22 @@ static int airoha_alloc_gdm_port(struct
+@@ -3438,58 +3537,22 @@ static int airoha_alloc_gdm_port(struct
                return -EINVAL;
        }
  
@@ -818,7 +857,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -      dev->ethtool_ops = &airoha_ethtool_ops;
 -      dev->max_mtu = AIROHA_MAX_MTU;
 -      dev->watchdog_timeo = 5 * HZ;
--      dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
+-      dev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
 -      dev->features |= AIROHA_HW_FEATURES;
 -      dev->vlan_features = AIROHA_HW_FEATURES;
 -      dev->dev.of_node = np;
@@ -865,16 +904,16 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
  
  static int airoha_register_gdm_devices(struct airoha_eth *eth)
-@@ -3394,7 +3456,7 @@ static int airoha_register_gdm_devices(s
+@@ -3503,7 +3566,7 @@ static int airoha_register_gdm_devices(s
                if (!port)
                        continue;
  
 -              err = register_netdev(port->dev);
-+              err = register_netdev(port->dev->dev);
++              err = register_netdev(netdev_from_priv(port->dev));
                if (err)
                        return err;
        }
-@@ -3503,16 +3565,18 @@ error_napi_stop:
+@@ -3612,16 +3675,22 @@ error_napi_stop:
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
@@ -885,18 +924,23 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
 -              if (port->dev->reg_state == NETREG_REGISTERED) {
 +              dev = port->dev;
-+              if (dev && dev->dev->reg_state == NETREG_REGISTERED) {
++              if (dev) {
++                      struct net_device *netdev = netdev_from_priv(dev);
++
++                      if (netdev->reg_state == NETREG_REGISTERED) {
  #if defined(CONFIG_PCS_AIROHA)
-                       if (airhoa_is_phy_external(port))
+-                      if (airhoa_is_phy_external(port))
 -                              phylink_destroy(port->phylink);
-+                              phylink_destroy(dev->phylink);
++                              if (airhoa_is_phy_external(port))
++                                      phylink_destroy(dev->phylink);
  #endif
 -                      unregister_netdev(port->dev);
-+                      unregister_netdev(dev->dev);
++                              unregister_netdev(netdev);
++                      }
                }
                airoha_metadata_dst_free(port);
        }
-@@ -3534,15 +3598,19 @@ static void airoha_remove(struct platfor
+@@ -3643,15 +3712,19 @@ static void airoha_remove(struct platfor
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
@@ -914,25 +958,24 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +
 +              dev = port->dev;
 +              if (dev)
-+                      unregister_netdev(dev->dev);
++                      unregister_netdev(netdev_from_priv(dev));
                airoha_metadata_dst_free(port);
        }
        airoha_hw_cleanup(eth);
 --- a/drivers/net/ethernet/airoha/airoha_eth.h
 +++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -547,17 +547,22 @@ struct airoha_qdma {
+@@ -547,17 +547,21 @@ struct airoha_qdma {
        struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
  };
  
 -struct airoha_gdm_port {
 -      struct airoha_qdma *qdma;
--      struct airoha_eth *eth;
 +struct airoha_gdm_dev {
 +      struct airoha_gdm_port *port;
-       struct net_device *dev;
+       struct airoha_eth *eth;
+-      struct net_device *dev;
 -      int id;
 -      int nbq;
-+      struct airoha_eth *eth;
  
  #if defined(CONFIG_PCS_AIROHA)
        struct phylink *phylink;
@@ -948,7 +991,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        struct airoha_hw_stats stats;
  
-@@ -691,8 +696,8 @@ static inline bool airoha_qdma_is_lro_qu
+@@ -691,8 +695,8 @@ static inline bool airoha_qdma_is_lro_qu
  }
  
  int airoha_get_fe_port(struct airoha_gdm_port *port);
@@ -1007,12 +1050,33 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        if (dsa_port >= 0 || eth->ports[1])
                                pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
                                                         : port->id;
-@@ -1485,7 +1488,7 @@ void airoha_ppe_check_skb(struct airoha_
+@@ -1484,8 +1487,8 @@ void airoha_ppe_check_skb(struct airoha_
  void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
  {
++      struct net_device *dev = netdev_from_priv(port->dev);
        struct airoha_eth *eth = port->qdma->eth;
 -      struct net_device *dev = port->dev;
-+      struct net_device *dev = port->dev->dev;
        const u8 *addr = dev->dev_addr;
        u32 val;
  
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -2760,6 +2760,17 @@ static inline void *netdev_priv(const st
+       return (void *)dev->priv;
+ }
++/**
++ * netdev_from_priv() - get network device from priv
++ * @priv: network device private data
++ *
++ * Returns: net_device to which @priv belongs
++ */
++static inline struct net_device *netdev_from_priv(const void *priv)
++{
++      return container_of(priv, struct net_device, priv);
++}
++
+ /* Set the sysfs physical device reference for the network logical device
+  * if set prior to registration will cause a symlink during initialization.
+  */
index 8f3dd2d1d1eb881993262b682c5475891fe7e3aa..b95371056fc105bceaf8f17490d039315f5b6dfd 100644 (file)
@@ -24,7 +24,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -80,9 +80,10 @@ static bool airhoa_is_phy_external(struc
+@@ -81,9 +81,10 @@ static bool airhoa_is_phy_external(struc
  }
  #endif
  
@@ -37,7 +37,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 val, reg;
  
        reg = airoha_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
-@@ -94,7 +95,7 @@ static void airoha_set_macaddr(struct ai
+@@ -95,7 +96,7 @@ static void airoha_set_macaddr(struct ai
        airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
        airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
  
@@ -46,7 +46,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
  
  static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
-@@ -110,10 +111,10 @@ static void airoha_set_gdm_port_fwd_cfg(
+@@ -111,10 +112,10 @@ static void airoha_set_gdm_port_fwd_cfg(
                      FIELD_PREP(GDM_UCFQ_MASK, val));
  }
  
@@ -60,7 +60,24 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 vip_port;
  
        vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
-@@ -1004,10 +1005,13 @@ static void airoha_qdma_wake_netdev_txqs
+@@ -485,8 +486,7 @@ static bool airoha_fe_lro_is_enabled(str
+ static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
+ {
+-      struct airoha_gdm_port *port = dev->port;
+-      struct airoha_qdma *qdma = port->qdma;
++      struct airoha_qdma *qdma = dev->qdma;
+       struct airoha_eth *eth = qdma->eth;
+       int qdma_id = qdma - &eth->qdma[0];
+       int i, lro_queue_index = 0;
+@@ -1097,13 +1097,18 @@ static void airoha_qdma_wake_netdev_txqs
+       for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+               struct airoha_gdm_port *port = eth->ports[i];
++              struct airoha_gdm_dev *dev;
+               struct net_device *netdev;
+               int j;
                if (!port)
                        continue;
  
@@ -72,11 +89,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +              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;
-@@ -1712,9 +1716,10 @@ static void airoha_qdma_stop_napi(struct
+               netdev = netdev_from_priv(port->dev);
+@@ -1811,9 +1816,10 @@ static void airoha_qdma_stop_napi(struct
        }
  }
  
@@ -89,16 +103,16 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 val, i = 0;
  
        spin_lock(&port->stats.lock);
-@@ -1861,7 +1866,7 @@ static int airoha_dev_open(struct net_de
+@@ -1981,7 +1987,7 @@ static int airoha_dev_open(struct net_de
        int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
 -      struct airoha_qdma *qdma = port->qdma;
 +      struct airoha_qdma *qdma = dev->qdma;
+       struct airoha_eth *eth = qdma->eth;
+       int qdma_id = qdma - &eth->qdma[0];
        u32 pse_port = FE_PSE_PORT_PPE1;
- #if defined(CONFIG_PCS_AIROHA)
-@@ -1878,7 +1883,7 @@ static int airoha_dev_open(struct net_de
+@@ -2011,7 +2017,7 @@ static int airoha_dev_open(struct net_de
  #endif
  
        netif_tx_start_all_queues(netdev);
@@ -107,12 +121,13 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        if (err)
                return err;
  
-@@ -1915,11 +1920,11 @@ static int airoha_dev_stop(struct net_de
+@@ -2052,12 +2058,12 @@ static int airoha_dev_stop(struct net_de
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
 -      struct airoha_qdma *qdma = port->qdma;
 +      struct airoha_qdma *qdma = dev->qdma;
+       struct airoha_eth *eth = qdma->eth;
        int i;
  
        netif_tx_disable(netdev);
@@ -121,7 +136,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        for (i = 0; i < netdev->num_tx_queues; i++)
                netdev_tx_reset_subqueue(netdev, i);
  
-@@ -1952,21 +1957,21 @@ static int airoha_dev_stop(struct net_de
+@@ -2092,21 +2098,21 @@ static int airoha_dev_stop(struct net_de
  static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -147,7 +162,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 val, pse_port, chan;
        int i, src_port;
  
-@@ -2013,7 +2018,7 @@ static int airoha_set_gdm2_loopback(stru
+@@ -2153,7 +2159,7 @@ static int airoha_set_gdm2_loopback(stru
                      __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2));
  
        for (i = 0; i < eth->soc->num_ppe; i++)
@@ -156,20 +171,20 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
                u32 mask = FC_ID_OF_SRC_PORT_MASK(nbq);
-@@ -2033,9 +2038,9 @@ static int airoha_dev_init(struct net_de
+@@ -2173,9 +2179,9 @@ static int airoha_dev_init(struct net_de
        int i;
  
        /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
 -      port->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
--      dev->dev->irq = port->qdma->irq_banks[0].irq;
+-      netdev->irq = port->qdma->irq_banks[0].irq;
 -      airoha_set_macaddr(port, netdev->dev_addr);
 +      dev->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
-+      dev->dev->irq = dev->qdma->irq_banks[0].irq;
++      netdev->irq = dev->qdma->irq_banks[0].irq;
 +      airoha_set_macaddr(dev, netdev->dev_addr);
  
        switch (port->id) {
        case AIROHA_GDM3_IDX:
-@@ -2044,7 +2049,7 @@ static int airoha_dev_init(struct net_de
+@@ -2184,7 +2190,7 @@ static int airoha_dev_init(struct net_de
                if (!eth->ports[1]) {
                        int err;
  
@@ -178,7 +193,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        if (err)
                                return err;
                }
-@@ -2054,8 +2059,7 @@ static int airoha_dev_init(struct net_de
+@@ -2194,8 +2200,7 @@ static int airoha_dev_init(struct net_de
        }
  
        for (i = 0; i < eth->soc->num_ppe; i++)
@@ -188,7 +203,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        return 0;
  }
-@@ -2067,7 +2071,7 @@ static void airoha_dev_get_stats64(struc
+@@ -2207,7 +2212,7 @@ static void airoha_dev_get_stats64(struc
        struct airoha_gdm_port *port = dev->port;
        unsigned int start;
  
@@ -197,7 +212,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        do {
                start = u64_stats_fetch_begin(&port->stats.syncp);
                storage->rx_packets = port->stats.rx_ok_pkts;
-@@ -2087,8 +2091,8 @@ static int airoha_dev_change_mtu(struct
+@@ -2227,8 +2232,8 @@ static int airoha_dev_change_mtu(struct
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
@@ -207,7 +222,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
                      GDM_LONG_LEN_MASK,
-@@ -2162,10 +2166,10 @@ static u32 airoha_get_dsa_tag(struct sk_
+@@ -2302,10 +2307,10 @@ static u32 airoha_get_dsa_tag(struct sk_
  #endif
  }
  
@@ -221,7 +236,17 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        switch (eth->soc->version) {
        case 0x7583:
-@@ -2183,8 +2187,7 @@ static int airoha_dev_set_features(struc
+@@ -2322,8 +2327,7 @@ static netdev_features_t airoha_dev_fix_
+                                                netdev_features_t features)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_gdm_port *port = dev->port;
+-      struct airoha_qdma *qdma = port->qdma;
++      struct airoha_qdma *qdma = dev->qdma;
+       if (qdma->users > 1)
+               features &= ~NETIF_F_GRO_HW;
+@@ -2336,8 +2340,7 @@ static int airoha_dev_set_features(struc
  {
        netdev_features_t diff = netdev->features ^ features;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -230,21 +255,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      struct airoha_qdma *qdma = dev->qdma;
        struct airoha_eth *eth = qdma->eth;
        int qdma_id = qdma - &eth->qdma[0];
-       int i;
-@@ -2226,10 +2229,10 @@ static int airoha_dev_set_features(struc
-                       if (!p)
-                               continue;
--                      if (p->qdma != qdma)
-+                      d = p->dev;
-+                      if (d->qdma != qdma)
-                               continue;
  
--                      d = p->dev;
-                       if (d->dev == netdev)
-                               continue;
-@@ -2246,8 +2249,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2356,8 +2359,7 @@ static netdev_tx_t airoha_dev_xmit(struc
                                   struct net_device *netdev)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -254,7 +266,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 nr_frags, tag, msg0, msg1, len;
        struct airoha_queue_entry *e;
        struct netdev_queue *txq;
-@@ -2285,7 +2287,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2395,7 +2397,7 @@ static netdev_tx_t airoha_dev_xmit(struc
                }
        }
  
@@ -263,7 +275,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
               FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
  
-@@ -2388,8 +2390,7 @@ static void airoha_ethtool_get_drvinfo(s
+@@ -2498,8 +2500,7 @@ static void airoha_ethtool_get_drvinfo(s
                                       struct ethtool_drvinfo *info)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -273,7 +285,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
        strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
-@@ -2402,7 +2403,7 @@ static void airoha_ethtool_get_mac_stats
+@@ -2512,7 +2513,7 @@ static void airoha_ethtool_get_mac_stats
        struct airoha_gdm_port *port = dev->port;
        unsigned int start;
  
@@ -282,7 +294,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        do {
                start = u64_stats_fetch_begin(&port->stats.syncp);
                stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
-@@ -2442,7 +2443,7 @@ airoha_ethtool_get_rmon_stats(struct net
+@@ -2552,7 +2553,7 @@ airoha_ethtool_get_rmon_stats(struct net
                     ARRAY_SIZE(hw_stats->rx_len) + 1);
  
        *ranges = airoha_ethtool_rmon_ranges;
@@ -291,7 +303,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        do {
                int i;
  
-@@ -2462,18 +2463,17 @@ static int airoha_qdma_set_chan_tx_sched
+@@ -2572,18 +2573,17 @@ static int airoha_qdma_set_chan_tx_sched
                                         const u16 *weights, u8 n_weights)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -312,7 +324,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                               TWRR_RW_CMD_MASK |
                               FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
                               FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
-@@ -2481,13 +2481,12 @@ static int airoha_qdma_set_chan_tx_sched
+@@ -2591,13 +2591,12 @@ static int airoha_qdma_set_chan_tx_sched
                err = read_poll_timeout(airoha_qdma_rr, status,
                                        status & TWRR_RW_CMD_DONE,
                                        USEC_PER_MSEC, 10 * USEC_PER_MSEC,
@@ -328,7 +340,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        CHAN_QOS_MODE_MASK(channel),
                        __field_prep(CHAN_QOS_MODE_MASK(channel), mode));
  
-@@ -2553,9 +2552,9 @@ static int airoha_qdma_get_tx_ets_stats(
+@@ -2663,9 +2662,9 @@ static int airoha_qdma_get_tx_ets_stats(
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
  
@@ -340,7 +352,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                                            REG_CNTR_VAL((channel << 1) + 1));
        u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
                         (fwd_tx_packets - port->fwd_tx_packets);
-@@ -2819,17 +2818,16 @@ static int airoha_qdma_set_tx_rate_limit
+@@ -2929,17 +2928,16 @@ static int airoha_qdma_set_tx_rate_limit
                                         u32 bucket_size)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -360,7 +372,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                                                         REG_EGRESS_TRTCM_CFG,
                                                         i, rate, bucket_size);
                if (err)
-@@ -2879,11 +2877,11 @@ static int airoha_tc_htb_alloc_leaf_queu
+@@ -2989,11 +2987,11 @@ static int airoha_tc_htb_alloc_leaf_queu
        return 0;
  }
  
@@ -374,7 +386,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        int i;
  
        for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
-@@ -2962,7 +2960,6 @@ static int airoha_dev_tc_matchall(struct
+@@ -3072,7 +3070,6 @@ static int airoha_dev_tc_matchall(struct
  {
        enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -382,7 +394,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 rate = 0, bucket_size = 0;
  
        switch (f->command) {
-@@ -2987,7 +2984,7 @@ static int airoha_dev_tc_matchall(struct
+@@ -3097,7 +3094,7 @@ static int airoha_dev_tc_matchall(struct
                fallthrough;
        }
        case TC_CLSMATCHALL_DESTROY:
@@ -391,7 +403,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                                                unit_type);
        default:
                return -EOPNOTSUPP;
-@@ -2999,8 +2996,7 @@ static int airoha_dev_setup_tc_block_cb(
+@@ -3109,8 +3106,7 @@ static int airoha_dev_setup_tc_block_cb(
  {
        struct net_device *netdev = cb_priv;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -401,7 +413,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        if (!tc_can_offload(netdev))
                return -EOPNOTSUPP;
-@@ -3235,7 +3231,7 @@ static void airoha_mac_link_up(struct ph
+@@ -3346,7 +3342,7 @@ static void airoha_mac_link_up(struct ph
        struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
                                                  phylink_config);
        struct airoha_gdm_port *port = dev->port;
@@ -417,10 +429,10 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  struct airoha_gdm_dev {
        struct airoha_gdm_port *port;
 +      struct airoha_qdma *qdma;
-       struct net_device *dev;
        struct airoha_eth *eth;
  
-@@ -559,7 +560,6 @@ struct airoha_gdm_dev {
+ #if defined(CONFIG_PCS_AIROHA)
+@@ -558,7 +559,6 @@ struct airoha_gdm_dev {
  };
  
  struct airoha_gdm_port {
@@ -428,7 +440,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        struct airoha_gdm_dev *dev;
        int id;
        int nbq;
-@@ -695,19 +695,18 @@ static inline bool airoha_qdma_is_lro_qu
+@@ -694,19 +694,18 @@ static inline bool airoha_qdma_is_lro_qu
        return !!(AIROHA_RXQ_LRO_EN_MASK & BIT(qid));
  }
  
@@ -483,11 +495,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port)
 +void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
  {
+-      struct net_device *dev = netdev_from_priv(port->dev);
 -      struct airoha_eth *eth = port->qdma->eth;
--      struct net_device *dev = port->dev->dev;
 -      const u8 *addr = dev->dev_addr;
++      struct net_device *netdev = netdev_from_priv(dev);
 +      struct airoha_gdm_port *port = dev->port;
-+      struct net_device *netdev = dev->dev;
 +      struct airoha_eth *eth = dev->eth;
 +      const u8 *addr = netdev->dev_addr;
        u32 val;
index 3ae068204bcb0302eb185a09dc1bff88b7ddae88..64ed102d3a63ef99555435434b49f62061c7c7f0 100644 (file)
@@ -23,7 +23,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -82,12 +82,10 @@ static bool airhoa_is_phy_external(struc
+@@ -83,12 +83,10 @@ static bool airhoa_is_phy_external(struc
  
  static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
  {
@@ -37,18 +37,18 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
        airoha_fe_wr(eth, reg, val);
  
-@@ -2038,7 +2036,7 @@ static int airoha_dev_init(struct net_de
+@@ -2179,7 +2177,7 @@ static int airoha_dev_init(struct net_de
        int i;
  
        /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
 -      dev->qdma = &eth->qdma[!airoha_is_lan_gdm_port(port)];
 +      dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
-       dev->dev->irq = dev->qdma->irq_banks[0].irq;
+       netdev->irq = dev->qdma->irq_banks[0].irq;
        airoha_set_macaddr(dev, netdev->dev_addr);
  
 --- a/drivers/net/ethernet/airoha/airoha_eth.h
 +++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -664,8 +664,10 @@ static inline u16 airoha_qdma_get_txq(st
+@@ -663,8 +663,10 @@ static inline u16 airoha_qdma_get_txq(st
        return qid % ARRAY_SIZE(qdma->q_tx);
  }
  
index 7e0077124accb89abd42dc6937869ef9e8d8e521..3bbcaab93669f9ce3a1bd6b9373df3669b4994bc 100644 (file)
@@ -22,7 +22,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -2835,30 +2835,40 @@ static int airoha_qdma_set_tx_rate_limit
+@@ -2945,30 +2945,46 @@ static int airoha_qdma_set_tx_rate_limit
        return 0;
  }
  
@@ -36,6 +36,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -      int err, num_tx_queues = netdev->real_num_tx_queues;
 -      struct airoha_gdm_dev *dev = netdev_priv(netdev);
 -      struct airoha_gdm_port *port = dev->port;
++      int err;
  
        if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
                NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
@@ -45,7 +46,13 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -      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);
++      err = airoha_qdma_set_tx_rate_limit(dev, channel, rate, opt->quantum);
++      if (err)
+               NL_SET_ERR_MSG_MOD(opt->extack,
+                                  "failed configuring htb offload");
+-              return err;
++
++      return err;
 +}
 +
 +static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
@@ -60,9 +67,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +       * in use by another net_device running on the same QDMA block.
 +       */
 +      if (test_and_set_bit(channel, qdma->qos_channel_map)) {
-               NL_SET_ERR_MSG_MOD(opt->extack,
--                                 "failed configuring htb offload");
--              return err;
++              NL_SET_ERR_MSG_MOD(opt->extack,
 +                                 "qdma qos channel already in use");
 +              return -EBUSY;
        }
@@ -75,7 +80,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        err = netif_set_real_num_tx_queues(netdev, num_tx_queues + 1);
        if (err) {
-@@ -2866,13 +2876,17 @@ static int airoha_tc_htb_alloc_leaf_queu
+@@ -2976,13 +2992,17 @@ static int airoha_tc_htb_alloc_leaf_queu
                                              opt->quantum);
                NL_SET_ERR_MSG_MOD(opt->extack,
                                   "failed setting real_num_tx_queues");
@@ -95,7 +100,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
  
  static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
-@@ -3053,11 +3067,13 @@ static int airoha_dev_setup_tc_block(str
+@@ -3163,11 +3183,13 @@ static int airoha_dev_setup_tc_block(str
  static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -111,7 +116,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
  
  static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
-@@ -3065,9 +3081,8 @@ static int airoha_tc_htb_delete_leaf_que
+@@ -3175,9 +3197,8 @@ static int airoha_tc_htb_delete_leaf_que
  {
        u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -122,7 +127,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
                return -EINVAL;
        }
-@@ -3080,10 +3095,9 @@ static int airoha_tc_htb_delete_leaf_que
+@@ -3190,10 +3211,9 @@ static int airoha_tc_htb_delete_leaf_que
  static int airoha_tc_htb_destroy(struct net_device *netdev)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -134,7 +139,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                airoha_tc_remove_htb_queue(netdev, q);
  
        return 0;
-@@ -3094,9 +3108,8 @@ static int airoha_tc_get_htb_get_leaf_qu
+@@ -3204,9 +3224,8 @@ static int airoha_tc_get_htb_get_leaf_qu
  {
        u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -145,7 +150,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
                return -EINVAL;
        }
-@@ -3115,6 +3128,7 @@ static int airoha_tc_setup_qdisc_htb(str
+@@ -3225,6 +3244,7 @@ static int airoha_tc_setup_qdisc_htb(str
        case TC_HTB_DESTROY:
                return airoha_tc_htb_destroy(dev);
        case TC_HTB_NODE_MODIFY:
@@ -164,7 +169,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  };
  
  struct airoha_gdm_dev {
-@@ -557,6 +559,8 @@ struct airoha_gdm_dev {
+@@ -556,6 +558,8 @@ struct airoha_gdm_dev {
        struct phylink *phylink;
        struct phylink_config phylink_config;
  #endif
@@ -173,7 +178,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  };
  
  struct airoha_gdm_port {
-@@ -566,8 +570,6 @@ struct airoha_gdm_port {
+@@ -565,8 +569,6 @@ struct airoha_gdm_port {
  
        struct airoha_hw_stats stats;
  
index 9522e36b71dd9e2ad4eb04009ecf0daa549efa3e..dfe8662b5639912a40bb693cd9f73d864610abc4 100644 (file)
@@ -21,7 +21,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -2548,19 +2548,17 @@ static int airoha_qdma_get_tx_ets_stats(
+@@ -2658,19 +2658,17 @@ static int airoha_qdma_get_tx_ets_stats(
                                        struct tc_ets_qopt_offload *opt)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -50,7 +50,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
 --- a/drivers/net/ethernet/airoha/airoha_eth.h
 +++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -561,6 +561,9 @@ struct airoha_gdm_dev {
+@@ -560,6 +560,9 @@ struct airoha_gdm_dev {
  #endif
  
        DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
@@ -60,7 +60,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  };
  
  struct airoha_gdm_port {
-@@ -570,10 +573,6 @@ struct airoha_gdm_port {
+@@ -569,10 +572,6 @@ struct airoha_gdm_port {
  
        struct airoha_hw_stats stats;
  
index ad848a432a7cf4ccb6abd75c8462965f391d945b..870b24371b5f32c44860bd32575ed0e2727170c4 100644 (file)
@@ -56,7 +56,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -115,7 +115,7 @@ static int airoha_set_vip_for_gdm_port(s
+@@ -116,7 +116,7 @@ static int airoha_set_vip_for_gdm_port(s
        struct airoha_eth *eth = dev->eth;
        u32 vip_port;
  
@@ -65,7 +65,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        if (enable) {
                airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
                airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
-@@ -619,30 +619,26 @@ static int airoha_qdma_fill_rx_queue(str
+@@ -646,30 +646,26 @@ static int airoha_qdma_fill_rx_queue(str
        return nframes;
  }
  
@@ -100,82 +100,65 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +
 +      if (p >= ARRAY_SIZE(eth->ports))
 +              return ERR_PTR(-ENODEV);
-+
+-      return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
 +      port = eth->ports[p];
 +      if (!port)
 +              return ERR_PTR(-ENODEV);
 +
 +      if (d >= ARRAY_SIZE(port->devs))
 +              return ERR_PTR(-ENODEV);
--      return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
++
 +      return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
  }
  
- static int airoha_qdma_lro_rx_process(struct airoha_queue *q,
-@@ -736,9 +732,8 @@ static int airoha_qdma_rx_process(struct
+ static struct sk_buff *airoha_qdma_lro_rx_skb(struct airoha_queue *q,
+@@ -841,9 +837,9 @@ static int airoha_qdma_rx_process(struct
                struct airoha_queue_entry *e = &q->entry[q->tail];
                struct airoha_qdma_desc *desc = &q->desc[q->tail];
                u32 hash, reason, msg1, desc_ctrl;
 -              struct airoha_gdm_port *port;
--              struct net_device *netdev;
--              int data_len, len, p;
 +              struct airoha_gdm_dev *dev;
+               struct net_device *netdev;
+-              int data_len, len, p;
 +              int data_len, len;
                struct page *page;
  
                desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
-@@ -759,15 +754,10 @@ static int airoha_qdma_rx_process(struct
+@@ -864,15 +860,11 @@ static int airoha_qdma_rx_process(struct
                if (!len || data_len < len)
                        goto free_frag;
  
 -              p = airoha_qdma_get_gdm_port(eth, desc);
 -              if (p < 0 || !eth->ports[p])
--                      goto free_frag;
--
--              port = eth->ports[p];
--              if (!port->dev)
 +              dev = airoha_qdma_get_gdm_dev(eth, desc);
 +              if (IS_ERR(dev))
                        goto free_frag;
  
--              netdev = port->dev->dev;
+-              port = eth->ports[p];
+-              if (!port->dev)
+-                      goto free_frag;
+-
+-              netdev = netdev_from_priv(port->dev);
++              netdev = netdev_from_priv(dev);
                if (!q->skb) { /* first buffer */
-                       q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
-                                               q->buf_size);
-@@ -776,14 +766,14 @@ static int airoha_qdma_rx_process(struct
-                       skb_reserve(q->skb, AIROHA_RX_HEADROOM);
-                       __skb_put(q->skb, len);
--                      q->skb->dev = netdev;
-+                      q->skb->dev = dev->dev;
-                       q->skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       skb_record_rx_queue(q->skb, qid);
-                       if (airoha_qdma_lro_rx_process(q, desc) < 0)
-                               goto free_frag;
--                      q->skb->protocol = eth_type_trans(q->skb, netdev);
-+                      q->skb->protocol = eth_type_trans(q->skb, dev->dev);
-                       skb_mark_for_recycle(q->skb);
-               } else { /* scattered frame */
-                       struct skb_shared_info *shinfo = skb_shinfo(q->skb);
-@@ -800,7 +790,9 @@ static int airoha_qdma_rx_process(struct
-               if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl))
+                       q->skb = airoha_qdma_build_rx_skb(q, desc, e, netdev);
+                       if (!q->skb)
+@@ -893,6 +885,8 @@ static int airoha_qdma_rx_process(struct
                        continue;
  
--              if (netdev_uses_dsa(netdev)) {
-+              if (netdev_uses_dsa(dev->dev)) {
+               if (netdev_uses_dsa(netdev)) {
 +                      struct airoha_gdm_port *port = dev->port;
 +
                        /* PPE module requires untagged packets to work
                         * properly and it provides DSA port index via the
                         * DMA descriptor. Report DSA tag to the DSA stack
-@@ -997,24 +989,27 @@ static void airoha_qdma_wake_netdev_txqs
+@@ -1095,26 +1089,29 @@ static void airoha_qdma_wake_netdev_txqs
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
 -              struct airoha_gdm_dev *dev;
+-              struct net_device *netdev;
 -              int j;
 +              int d;
  
@@ -187,6 +170,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -                      continue;
 +              for (d = 0; d < ARRAY_SIZE(port->devs); d++) {
 +                      struct airoha_gdm_dev *dev = port->devs[d];
++                      struct net_device *netdev;
 +                      int j;
  
 -              if (dev->qdma != qdma)
@@ -194,36 +178,75 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +                      if (!dev)
 +                              continue;
  
--              for (j = 0; j < dev->dev->num_tx_queues; j++) {
+-              netdev = netdev_from_priv(port->dev);
+-              for (j = 0; j < netdev->num_tx_queues; j++) {
 -                      if (airoha_qdma_get_txq(qdma, j) != qid)
 +                      if (dev->qdma != qdma)
                                continue;
  
--                      netif_wake_subqueue(dev->dev, j);
-+                      for (j = 0; j < dev->dev->num_tx_queues; j++) {
+-                      netif_wake_subqueue(netdev, j);
++                      netdev = netdev_from_priv(dev);
++                      for (j = 0; j < netdev->num_tx_queues; j++) {
 +                              if (airoha_qdma_get_txq(qdma, j) != qid)
 +                                      continue;
 +
-+                              netif_wake_subqueue(dev->dev, j);
++                              netif_wake_subqueue(netdev, j);
 +                      }
                }
        }
        q->txq_stopped = false;
-@@ -1903,11 +1898,9 @@ static int airoha_dev_open(struct net_de
+@@ -1961,22 +1958,32 @@ static void airoha_update_hw_stats(struc
+ static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
+ {
+-      struct airoha_gdm_port *port = dev->port;
+       struct airoha_eth *eth = dev->eth;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+-              struct airoha_gdm_port *p = eth->ports[i];
+-              struct net_device *netdev;
++              struct airoha_gdm_port *port = eth->ports[i];
++              int j;
+-              if (!p)
++              if (!port)
+                       continue;
+-              netdev = netdev_from_priv(p->dev);
+-              if (netdev->reg_state != NETREG_REGISTERED)
+-                      continue;
++              for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
++                      struct airoha_gdm_dev *iter_dev = port->devs[j];
++                      struct net_device *netdev;
++
++                      if (!iter_dev || iter_dev == dev)
++                              continue;
++
++                      if (iter_dev->qdma != dev->qdma)
++                              continue;
++
++                      netdev = netdev_from_priv(iter_dev);
++                      if (netdev->reg_state != NETREG_REGISTERED)
++                              continue;
+-              netdev_update_features(netdev);
++                      netdev_update_features(netdev);
++              }
+       }
+ }
+@@ -2037,7 +2044,8 @@ static int airoha_dev_open(struct net_de
                        GLOBAL_CFG_RX_DMA_EN_MASK);
-       atomic_inc(&qdma->users);
+       qdma->users++;
  
--      if (port->id == AIROHA_GDM2_IDX &&
--          airoha_ppe_is_enabled(qdma->eth, 1)) {
--              /* For PPE2 always use secondary cpu port. */
+-      if (port->id == AIROHA_GDM2_IDX && airoha_ppe_is_enabled(eth, 1)) {
 +      if (!airoha_is_lan_gdm_dev(dev) &&
-+          airoha_ppe_is_enabled(qdma->eth, 1))
++          airoha_ppe_is_enabled(eth, 1)) {
+               /* For PPE2 always use secondary cpu port. */
                pse_port = FE_PSE_PORT_PPE2;
--      }
-       airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
-                                   pse_port);
-@@ -2002,7 +1995,7 @@ static int airoha_set_gdm2_loopback(stru
+       }
+@@ -2143,7 +2151,7 @@ static int airoha_set_gdm2_loopback(stru
        airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
        airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
  
@@ -232,7 +255,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        if (src_port < 0)
                return src_port;
  
-@@ -2019,7 +2012,7 @@ static int airoha_set_gdm2_loopback(stru
+@@ -2160,7 +2168,7 @@ static int airoha_set_gdm2_loopback(stru
                airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
  
        if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
@@ -241,7 +264,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
                airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
                              __field_prep(mask, AIROHA_GDM2_IDX));
-@@ -2033,7 +2026,7 @@ static int airoha_dev_init(struct net_de
+@@ -2174,7 +2182,7 @@ static int airoha_dev_init(struct net_de
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
        struct airoha_eth *eth = dev->eth;
@@ -250,7 +273,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
        dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
-@@ -2056,8 +2049,8 @@ static int airoha_dev_init(struct net_de
+@@ -2197,8 +2205,8 @@ static int airoha_dev_init(struct net_de
                break;
        }
  
@@ -261,42 +284,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        return 0;
  }
-@@ -2222,20 +2215,26 @@ static int airoha_dev_set_features(struc
-       } else {
-               for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
-                       struct airoha_gdm_port *p = eth->ports[i];
--                      struct airoha_gdm_dev *d;
-+                      int j;
-                       if (!p)
-                               continue;
--                      d = p->dev;
--                      if (d->qdma != qdma)
--                              continue;
-+                      for (j = 0; j < ARRAY_SIZE(p->devs); j++) {
-+                              struct airoha_gdm_dev *d = p->devs[j];
--                      if (d->dev == netdev)
--                              continue;
-+                              if (!d)
-+                                      continue;
--                      if (d->dev->features & NETIF_F_LRO)
--                              return 0;
-+                              if (d->qdma != qdma)
-+                                      continue;
-+
-+                              if (d->dev == netdev)
-+                                      continue;
-+
-+                              if (d->dev->features & NETIF_F_LRO)
-+                                      return 0;
-+                      }
-               }
-               airoha_fe_lro_disable(eth, qdma_id);
-       }
-@@ -2286,7 +2285,8 @@ static netdev_tx_t airoha_dev_xmit(struc
+@@ -2396,7 +2404,8 @@ static netdev_tx_t airoha_dev_xmit(struc
        }
  
        fport = airoha_get_fe_port(dev);
@@ -306,7 +294,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
               FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
  
        q = &qdma->q_tx[qid];
-@@ -3222,12 +3222,15 @@ bool airoha_is_valid_gdm_dev(struct airo
+@@ -3339,12 +3348,15 @@ bool airoha_is_valid_gdm_dev(struct airo
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
@@ -324,7 +312,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        }
  
        return false;
-@@ -3351,10 +3354,11 @@ static int airoha_setup_phylink(struct n
+@@ -3468,10 +3480,11 @@ static int airoha_setup_phylink(struct n
  
  static int airoha_alloc_gdm_device(struct airoha_eth *eth,
                                   struct airoha_gdm_port *port,
@@ -338,15 +326,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        int err;
  
        netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
-@@ -3372,7 +3376,6 @@ static int airoha_alloc_gdm_device(struc
-       netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_LRO;
+@@ -3489,7 +3502,6 @@ static int airoha_alloc_gdm_device(struc
+       netdev->hw_features = AIROHA_HW_FEATURES | NETIF_F_GRO_HW;
        netdev->features |= AIROHA_HW_FEATURES;
        netdev->vlan_features = AIROHA_HW_FEATURES;
 -      netdev->dev.of_node = np;
        SET_NETDEV_DEV(netdev, eth->dev);
  
        /* reserve hw queues for HTB offloading */
-@@ -3390,11 +3393,25 @@ static int airoha_alloc_gdm_device(struc
+@@ -3507,10 +3519,24 @@ static int airoha_alloc_gdm_device(struc
                         netdev->dev_addr);
        }
  
@@ -364,7 +352,6 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +
 +      netdev->dev.of_node = of_node_get(np);
        dev = netdev_priv(netdev);
-       dev->dev = netdev;
        dev->port = port;
 -      port->dev = dev;
        dev->eth = eth;
@@ -373,7 +360,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
  #if defined(CONFIG_PCS_AIROHA)
        if (airhoa_is_phy_external(port)) {
-@@ -3412,7 +3429,8 @@ static int airoha_alloc_gdm_port(struct
+@@ -3528,7 +3554,8 @@ static int airoha_alloc_gdm_port(struct
  {
        const __be32 *id_ptr = of_get_property(np, "reg", NULL);
        struct airoha_gdm_port *port;
@@ -383,7 +370,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        u32 id;
  
        if (!id_ptr) {
-@@ -3440,15 +3458,51 @@ static int airoha_alloc_gdm_port(struct
+@@ -3556,15 +3583,51 @@ static int airoha_alloc_gdm_port(struct
        u64_stats_init(&port->stats.syncp);
        spin_lock_init(&port->stats.lock);
        port->id = id;
@@ -438,7 +425,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
  
  static int airoha_register_gdm_devices(struct airoha_eth *eth)
-@@ -3457,14 +3511,22 @@ static int airoha_register_gdm_devices(s
+@@ -3573,14 +3636,22 @@ static int airoha_register_gdm_devices(s
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
@@ -448,7 +435,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                if (!port)
                        continue;
  
--              err = register_netdev(port->dev->dev);
+-              err = register_netdev(netdev_from_priv(port->dev));
 -              if (err)
 -                      return err;
 +              for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
@@ -458,14 +445,14 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +                      if (!dev)
 +                              continue;
 +
-+                      err = register_netdev(dev->dev);
++                      err = register_netdev(netdev_from_priv(dev));
 +                      if (err)
 +                              return err;
 +              }
        }
  
        set_bit(DEV_STATE_REGISTERED, &eth->state);
-@@ -3571,18 +3633,27 @@ error_napi_stop:
+@@ -3687,15 +3758,19 @@ error_napi_stop:
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
@@ -476,30 +463,28 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                        continue;
  
 -              dev = port->dev;
--              if (dev && dev->dev->reg_state == NETREG_REGISTERED) {
+-              if (dev) {
+-                      struct net_device *netdev = netdev_from_priv(dev);
 +              for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
 +                      struct airoha_gdm_dev *dev = port->devs[j];
 +                      struct net_device *netdev;
 +
 +                      if (!dev)
 +                              continue;
-+
-+                      netdev = dev->dev;
-+                      if (netdev->reg_state == NETREG_REGISTERED) {
++                      netdev = netdev_from_priv(dev);
+                       if (netdev->reg_state == NETREG_REGISTERED) {
  #if defined(CONFIG_PCS_AIROHA)
--                      if (airhoa_is_phy_external(port))
--                              phylink_destroy(dev->phylink);
-+                              if (airhoa_is_phy_external(port))
-+                                      phylink_destroy(dev->phylink);
+                               if (airhoa_is_phy_external(port))
+@@ -3703,6 +3778,7 @@ error_napi_stop:
  #endif
--                      unregister_netdev(dev->dev);
-+                              unregister_netdev(netdev);
-+                      }
+                               unregister_netdev(netdev);
+                       }
 +                      of_node_put(netdev->dev.of_node);
                }
                airoha_metadata_dst_free(port);
        }
-@@ -3604,19 +3675,26 @@ static void airoha_remove(struct platfor
+@@ -3724,19 +3800,27 @@ static void airoha_remove(struct platfor
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
                struct airoha_gdm_port *port = eth->ports[i];
@@ -517,21 +502,23 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +                              continue;
 +
  #if defined(CONFIG_PCS_AIROHA)
-               if (airhoa_is_phy_external(port))
-                       phylink_destroy(dev->phylink);
+-              if (airhoa_is_phy_external(port))
+-                      phylink_destroy(dev->phylink);
++                      if (airhoa_is_phy_external(port))
++                              phylink_destroy(dev->phylink);
  #endif
--
 -              dev = port->dev;
 -              if (dev)
--                      unregister_netdev(dev->dev);
-+                      netdev = dev->dev;
+-                      unregister_netdev(netdev_from_priv(dev));
++                      netdev = netdev_from_priv(dev);
 +                      unregister_netdev(netdev);
 +                      of_node_put(netdev->dev.of_node);
 +              }
                airoha_metadata_dst_free(port);
        }
        airoha_hw_cleanup(eth);
-@@ -3678,6 +3756,39 @@ static u32 airoha_en7581_get_vip_port(st
+@@ -3798,6 +3882,39 @@ static u32 airoha_en7581_get_vip_port(st
        return 0;
  }
  
@@ -571,7 +558,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  static const char * const an7583_xsi_rsts_names[] = {
        "hsi0-mac",
        "hsi1-mac",
-@@ -3726,6 +3837,36 @@ static u32 airoha_an7583_get_vip_port(st
+@@ -3846,6 +3963,36 @@ static u32 airoha_an7583_get_vip_port(st
        return 0;
  }
  
@@ -608,7 +595,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  static const struct airoha_eth_soc_data en7581_soc_data = {
        .version = 0x7581,
        .xsi_rsts_names = en7581_xsi_rsts_names,
-@@ -3734,6 +3875,7 @@ static const struct airoha_eth_soc_data
+@@ -3854,6 +4001,7 @@ static const struct airoha_eth_soc_data
        .ops = {
                .get_sport = airoha_en7581_get_sport,
                .get_vip_port = airoha_en7581_get_vip_port,
@@ -616,7 +603,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        },
  };
  
-@@ -3745,6 +3887,7 @@ static const struct airoha_eth_soc_data
+@@ -3865,6 +4013,7 @@ static const struct airoha_eth_soc_data
        .ops = {
                .get_sport = airoha_an7583_get_sport,
                .get_vip_port = airoha_an7583_get_vip_port,
@@ -634,17 +621,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  #define AIROHA_MAX_NUM_QDMA           2
  #define AIROHA_MAX_NUM_IRQ_BANKS      4
  #define AIROHA_MAX_DSA_PORTS          7
-@@ -552,8 +553,8 @@ struct airoha_qdma {
- struct airoha_gdm_dev {
-       struct airoha_gdm_port *port;
-       struct airoha_qdma *qdma;
--      struct net_device *dev;
-       struct airoha_eth *eth;
-+      struct net_device *dev;
- #if defined(CONFIG_PCS_AIROHA)
-       struct phylink *phylink;
-@@ -564,12 +565,13 @@ struct airoha_gdm_dev {
+@@ -563,12 +564,13 @@ struct airoha_gdm_dev {
        /* qos stats counters */
        u64 cpu_tx_packets;
        u64 fwd_tx_packets;
@@ -660,7 +637,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        struct airoha_hw_stats stats;
  
-@@ -605,6 +607,8 @@ struct airoha_eth_soc_data {
+@@ -604,6 +606,8 @@ struct airoha_eth_soc_data {
        struct {
                int (*get_sport)(struct airoha_gdm_port *port, int nbq);
                u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq);
index eafefba314a76d12f39349bc32fdcfcdd6257aee..7dc33792bd8f11a714231b87946b9be3a7f10893 100644 (file)
@@ -23,25 +23,27 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -1859,8 +1859,8 @@ static int airoha_dev_open(struct net_de
+@@ -1992,10 +1992,10 @@ static int airoha_dev_open(struct net_de
        int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
 +      u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
        struct airoha_qdma *qdma = dev->qdma;
+       struct airoha_eth *eth = qdma->eth;
+       int qdma_id = qdma - &eth->qdma[0];
 -      u32 pse_port = FE_PSE_PORT_PPE1;
  
- #if defined(CONFIG_PCS_AIROHA)
-       if (airhoa_is_phy_external(port)) {
-@@ -1888,10 +1888,20 @@ static int airoha_dev_open(struct net_de
-               airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+       /* HW LRO is configured on the QDMA and it is shared between
+        * all the devices using it. Refuse to open a second device on
+@@ -2034,10 +2034,20 @@ static int airoha_dev_open(struct net_de
+               airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
                                GDM_STAG_EN_MASK);
  
--      airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+-      airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
 -                    GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
 -                    FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
 -                    FIELD_PREP(GDM_LONG_LEN_MASK, len));
-+      cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
++      cur_len = airoha_fe_get(eth, REG_GDM_LEN_CFG(port->id),
 +                              GDM_LONG_LEN_MASK);
 +      if (!port->users || len > cur_len) {
 +              /* Opening a sibling net_device with a larger MTU updates the
@@ -49,7 +51,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +               * multiple net_devices with different MTUs to share the same
 +               * GDM port.
 +               */
-+              airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
++              airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
 +                            GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
 +                            FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
 +                            FIELD_PREP(GDM_LONG_LEN_MASK, len));
@@ -58,7 +60,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
                        GLOBAL_CFG_TX_DMA_EN_MASK |
-@@ -1907,6 +1917,30 @@ static int airoha_dev_open(struct net_de
+@@ -2060,6 +2070,30 @@ static int airoha_dev_open(struct net_de
        return 0;
  }
  
@@ -75,7 +77,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +              if (!dev)
 +                      continue;
 +
-+              netdev = dev->dev;
++              netdev = netdev_from_priv(dev);
 +              if (netif_running(netdev))
 +                      len = max_t(u32, len, netdev->mtu);
 +      }
@@ -89,22 +91,21 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  static int airoha_dev_stop(struct net_device *netdev)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
-@@ -1919,8 +1953,12 @@ static int airoha_dev_stop(struct net_de
+@@ -2073,8 +2107,11 @@ static int airoha_dev_stop(struct net_de
        for (i = 0; i < netdev->num_tx_queues; i++)
                netdev_tx_reset_subqueue(netdev, i);
  
--      airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
+-      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
 -                                  FE_PSE_PORT_DROP);
 +      if (--port->users)
-+              airoha_set_port_mtu(dev->eth, port);
++              airoha_set_port_mtu(eth, port);
 +      else
-+              airoha_set_gdm_port_fwd_cfg(qdma->eth,
-+                                          REG_GDM_FWD_CFG(port->id),
++              airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
 +                                          FE_PSE_PORT_DROP);
  
-       if (atomic_dec_and_test(&qdma->users)) {
+       if (!--qdma->users) {
                airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
-@@ -2082,13 +2120,10 @@ static int airoha_dev_change_mtu(struct
+@@ -2238,13 +2275,10 @@ static int airoha_dev_change_mtu(struct
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
@@ -122,7 +123,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
 --- a/drivers/net/ethernet/airoha/airoha_eth.h
 +++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -572,6 +572,7 @@ struct airoha_gdm_dev {
+@@ -571,6 +571,7 @@ struct airoha_gdm_dev {
  struct airoha_gdm_port {
        struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
        int id;
index 9451c6fcee61d71e7d00d66f16fea5d4cfa74ba8..a21fa3f05bd7210097baa9d81b981566e211e423 100644 (file)
@@ -29,14 +29,16 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -2059,36 +2059,80 @@ static int airoha_set_gdm2_loopback(stru
+@@ -2214,36 +2214,81 @@ static int airoha_set_gdm2_loopback(stru
        return 0;
  }
  
 -static int airoha_dev_init(struct net_device *netdev)
 +static struct airoha_gdm_dev *
 +airoha_get_wan_gdm_dev(struct airoha_eth *eth)
-+{
+ {
+-      struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_gdm_port *port = dev->port;
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
@@ -58,15 +60,14 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +}
 +
 +static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
- {
--      struct airoha_gdm_dev *dev = netdev_priv(netdev);
--      struct airoha_gdm_port *port = dev->port;
++{
++      struct net_device *netdev = netdev_from_priv(dev);
        struct airoha_eth *eth = dev->eth;
        int ppe_id;
  
        /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
        dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
-       dev->dev->irq = dev->qdma->irq_banks[0].irq;
+       netdev->irq = dev->qdma->irq_banks[0].irq;
 -      airoha_set_macaddr(dev, netdev->dev_addr);
 +
 +      ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
@@ -138,7 +139,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  struct airoha_gdm_dev {
        struct airoha_gdm_port *port;
        struct airoha_qdma *qdma;
-@@ -566,6 +570,7 @@ struct airoha_gdm_dev {
+@@ -565,6 +569,7 @@ struct airoha_gdm_dev {
        u64 cpu_tx_packets;
        u64 fwd_tx_packets;
  
@@ -146,7 +147,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        int nbq;
  };
  
-@@ -672,13 +677,7 @@ static inline u16 airoha_qdma_get_txq(st
+@@ -671,13 +676,7 @@ static inline u16 airoha_qdma_get_txq(st
  
  static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
  {
index be9382c9eb8a915b802b2f81a4b5b74b162f9d62..553bd3cbf57533c62805f67ae2da85ee0708fbc5 100644 (file)
@@ -30,7 +30,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -80,20 +80,74 @@ static bool airhoa_is_phy_external(struc
+@@ -81,20 +81,76 @@ static bool airhoa_is_phy_external(struc
  }
  #endif
  
@@ -66,7 +66,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +                          airoha_is_lan_gdm_dev(dev))
 +                              continue;
 +
-+                      netdev = iter_dev->dev;
++                      netdev = netdev_from_priv(iter_dev);
 +                      if (netdev->reg_state != NETREG_REGISTERED)
 +                              continue;
 +
@@ -85,9 +85,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +               * the same for each net_device with the same LAN/WAN
 +               * configuration.
 +               */
++              struct net_device *netdev = netdev_from_priv(dev);
++
 +              dev_warn(eth->dev,
 +                       "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
-+                       dev->dev->name, ref_addr[0], ref_addr[1],
++                       netdev->name, ref_addr[0], ref_addr[1],
 +                       ref_addr[2]);
 +              dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
 +
@@ -103,15 +105,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -      airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
 +      airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin);
 +      airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax);
++
++      airoha_ppe_init_upd_mem(dev, addr);
  
 -      airoha_ppe_init_upd_mem(dev);
-+      airoha_ppe_init_upd_mem(dev, addr);
-+
 +      return 0;
  }
  
  static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
-@@ -1986,13 +2040,18 @@ static int airoha_dev_stop(struct net_de
+@@ -2141,13 +2197,18 @@ static int airoha_dev_stop(struct net_de
  static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -134,7 +136,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  }
 --- a/drivers/net/ethernet/airoha/airoha_eth.h
 +++ b/drivers/net/ethernet/airoha/airoha_eth.h
-@@ -713,7 +713,7 @@ void airoha_ppe_check_skb(struct airoha_
+@@ -712,7 +712,7 @@ void airoha_ppe_check_skb(struct airoha_
  int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
  int airoha_ppe_init(struct airoha_eth *eth);
  void airoha_ppe_deinit(struct airoha_eth *eth);
@@ -152,8 +154,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
 +void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr)
  {
+-      struct net_device *netdev = netdev_from_priv(dev);
        struct airoha_gdm_port *port = dev->port;
--      struct net_device *netdev = dev->dev;
        struct airoha_eth *eth = dev->eth;
 -      const u8 *addr = netdev->dev_addr;
        u32 val;
index aca2ae25764c31a026144010047f7c1dcec4c664..92ce90e7ca9b15334f74f0e851677e2aa8b7bb86 100644 (file)
@@ -17,7 +17,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -2056,7 +2056,7 @@ static int airoha_dev_set_macaddr(struct
+@@ -2213,7 +2213,7 @@ static int airoha_dev_set_macaddr(struct
        return 0;
  }
  
@@ -26,7 +26,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  {
        struct airoha_gdm_port *port = dev->port;
        struct airoha_eth *eth = dev->eth;
-@@ -2188,7 +2188,7 @@ static int airoha_dev_init(struct net_de
+@@ -2346,7 +2346,7 @@ static int airoha_dev_init(struct net_de
            (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
                int err;
  
diff --git a/target/linux/airoha/patches-6.18/920-12-net-airoha-Add-ethtool-priv_flags-callbacks.patch b/target/linux/airoha/patches-6.18/920-12-net-airoha-Add-ethtool-priv_flags-callbacks.patch
deleted file mode 100644 (file)
index f55b972..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-From e928621a0bbd010b75624c77105ce31f2b8d2faf Mon Sep 17 00:00:00 2001
-Message-ID: <e928621a0bbd010b75624c77105ce31f2b8d2faf.1779348625.git.lorenzo@kernel.org>
-In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
-References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
-From: Lorenzo Bianconi <lorenzo@kernel.org>
-Date: Sat, 13 Dec 2025 09:45:09 +0100
-Subject: [PATCH 12/13] net: airoha: Add ethtool priv_flags callbacks
-
-Introduce ethtool priv_flags callbacks in order to allow the user to
-select if the configured device will be used as hw lan or wan and enable
-or disable gdm2 loopback (used for hw QoS).
-
-Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
-Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
----
- drivers/net/ethernet/airoha/airoha_eth.c  | 173 ++++++++++++++++++++++
- drivers/net/ethernet/airoha/airoha_regs.h |   1 +
- 2 files changed, 174 insertions(+)
-
---- a/drivers/net/ethernet/airoha/airoha_eth.c
-+++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -1916,6 +1916,11 @@ static int airoha_dev_open(struct net_de
-       u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
-       struct airoha_qdma *qdma = dev->qdma;
-+      if (port->id == AIROHA_GDM2_IDX && airoha_is_lan_gdm_dev(dev)) {
-+              /* GDM2 can be used just as wan */
-+              return -EBUSY;
-+      }
-+
- #if defined(CONFIG_PCS_AIROHA)
-       if (airhoa_is_phy_external(port)) {
-               err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
-@@ -2118,6 +2123,45 @@ static int airoha_enable_gdm2_loopback(s
-       return 0;
- }
-+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
-+{
-+      struct airoha_eth *eth = dev->eth;
-+      int i, src_port;
-+      u32 pse_port;
-+
-+      src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
-+      if (src_port < 0)
-+              return src_port;
-+
-+      airoha_fe_clear(eth,
-+                      REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
-+                      SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
-+
-+      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
-+                                  FE_PSE_PORT_DROP);
-+      airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
-+                      LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
-+      pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
-+                                               : FE_PSE_PORT_PPE1;
-+      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
-+                                  pse_port);
-+
-+      airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
-+                    FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
-+
-+      for (i = 0; i < eth->soc->num_ppe; i++)
-+              airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
-+                              DFT_CPORT_MASK(AIROHA_GDM2_IDX));
-+
-+      /* Enable VIP and IFC for GDM2 */
-+      airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
-+      airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
-+
-+      airoha_fe_wr(eth, REG_SRC_PORT_FC_MAP6, FC_MAP6_DEF_VALUE);
-+
-+      return 0;
-+}
-+
- static struct airoha_gdm_dev *
- airoha_get_wan_gdm_dev(struct airoha_eth *eth)
- {
-@@ -2522,6 +2566,80 @@ error:
-       return NETDEV_TX_OK;
- }
-+struct airoha_ethool_priv_flags {
-+      char name[ETH_GSTRING_LEN];
-+      int (*handler)(struct net_device *netdev, u32 flags);
-+};
-+
-+static int airoha_dev_set_wan_flag(struct net_device *netdev, u32 flags)
-+{
-+      struct airoha_gdm_dev *dev = netdev_priv(netdev);
-+      struct airoha_gdm_port *port = dev->port;
-+      struct airoha_eth *eth = dev->eth;
-+
-+      if (!((dev->flags ^ flags) & AIROHA_PRIV_F_WAN))
-+              return 0;
-+
-+      if (netif_running(netdev))
-+              return -EBUSY;
-+
-+      if (flags & AIROHA_PRIV_F_WAN) {
-+              struct airoha_gdm_dev *wan_dev;
-+
-+              /* Verify the wan device is not already configured */
-+              wan_dev = airoha_get_wan_gdm_dev(eth);
-+              if (wan_dev && wan_dev != dev)
-+                      return -EBUSY;
-+
-+              switch (port->id) {
-+              case AIROHA_GDM2_IDX:
-+                      dev->flags |= AIROHA_PRIV_F_WAN;
-+                      airoha_dev_set_qdma(dev);
-+                      break;
-+              case AIROHA_GDM3_IDX:
-+              case AIROHA_GDM4_IDX: {
-+                      int err;
-+
-+                      dev->flags |= AIROHA_PRIV_F_WAN;
-+                      airoha_dev_set_qdma(dev);
-+
-+                      err = airoha_enable_gdm2_loopback(dev);
-+                      if (err) {
-+                              dev->flags &= ~AIROHA_PRIV_F_WAN;
-+                              return err;
-+                      }
-+                      break;
-+              }
-+              default:
-+                      return -EOPNOTSUPP;
-+              }
-+      } else {
-+              switch (port->id) {
-+              case AIROHA_GDM3_IDX:
-+              case AIROHA_GDM4_IDX: {
-+                      int err;
-+
-+                      err = airoha_disable_gdm2_loopback(dev);
-+                      if (err)
-+                              return err;
-+                      break;
-+              }
-+              default:
-+                      break;
-+              }
-+
-+              dev->flags &= ~AIROHA_PRIV_F_WAN;
-+              airoha_dev_set_qdma(dev);
-+      }
-+
-+      return airoha_set_macaddr(dev, netdev->dev_addr);
-+}
-+
-+static const struct airoha_ethool_priv_flags airoha_eth_priv_flags[] = {
-+      { "wan", airoha_dev_set_wan_flag },
-+};
-+#define AIROHA_PRIV_FLAGS_STR_LEN     ARRAY_SIZE(airoha_eth_priv_flags)
-+
- static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
-                                      struct ethtool_drvinfo *info)
- {
-@@ -2530,6 +2648,7 @@ static void airoha_ethtool_get_drvinfo(s
-       strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
-       strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
-+      info->n_priv_flags = AIROHA_PRIV_FLAGS_STR_LEN;
- }
- static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
-@@ -2594,6 +2713,56 @@ airoha_ethtool_get_rmon_stats(struct net
-       } while (u64_stats_fetch_retry(&port->stats.syncp, start));
- }
-+static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
-+{
-+      int i;
-+
-+      for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++) {
-+              int err;
-+
-+              if (!airoha_eth_priv_flags[i].handler)
-+                      continue;
-+
-+              err = airoha_eth_priv_flags[i].handler(netdev, flags);
-+              if (err)
-+                      return err;
-+      }
-+
-+      return 0;
-+}
-+
-+static u32 airoha_ethtool_get_priv_flags(struct net_device *netdev)
-+{
-+      struct airoha_gdm_dev *dev = netdev_priv(netdev);
-+
-+      return dev->flags;
-+}
-+
-+static int airoha_ethtool_get_sset_count(struct net_device *netdev, int sset)
-+{
-+      switch (sset) {
-+      case ETH_SS_PRIV_FLAGS:
-+              return AIROHA_PRIV_FLAGS_STR_LEN;
-+      default:
-+              return -EOPNOTSUPP;
-+      }
-+}
-+
-+static void airoha_ethtool_get_strings(struct net_device *netdev,
-+                                     u32 stringset, u8 *data)
-+{
-+      int i;
-+
-+      switch (stringset) {
-+      case ETH_SS_PRIV_FLAGS:
-+              for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++)
-+                      ethtool_puts(&data, airoha_eth_priv_flags[i].name);
-+              break;
-+      default:
-+              break;
-+      }
-+}
-+
- 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)
-@@ -3315,6 +3484,10 @@ static const struct ethtool_ops airoha_e
-       .get_rmon_stats         = airoha_ethtool_get_rmon_stats,
-       .get_link_ksettings     = phy_ethtool_get_link_ksettings,
-       .get_link               = ethtool_op_get_link,
-+      .set_priv_flags         = airoha_ethtool_set_priv_flags,
-+      .get_priv_flags         = airoha_ethtool_get_priv_flags,
-+      .get_sset_count         = airoha_ethtool_get_sset_count,
-+      .get_strings            = airoha_ethtool_get_strings,
- };
- static void airoha_mac_config(struct phylink_config *config, unsigned int mode,
---- a/drivers/net/ethernet/airoha/airoha_regs.h
-+++ b/drivers/net/ethernet/airoha/airoha_regs.h
-@@ -402,6 +402,7 @@
- #define REG_SRC_PORT_FC_MAP6          0x2298
- #define FC_ID_OF_SRC_PORT_MASK(_n)    GENMASK(4 + ((_n) << 3), ((_n) << 3))
-+#define FC_MAP6_DEF_VALUE             0x1b1a1918
- #define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
diff --git a/target/linux/airoha/patches-6.18/920-12-net-airoha-defer-GDM3-GDM4-WAN-mode-and-GDM2-loopbac.patch b/target/linux/airoha/patches-6.18/920-12-net-airoha-defer-GDM3-GDM4-WAN-mode-and-GDM2-loopbac.patch
new file mode 100644 (file)
index 0000000..2c16f2a
--- /dev/null
@@ -0,0 +1,668 @@
+From e928621a0bbd010b75624c77105ce31f2b8d2faf Mon Sep 17 00:00:00 2001
+Message-ID: <e928621a0bbd010b75624c77105ce31f2b8d2faf.1779348625.git.lorenzo@kernel.org>
+In-Reply-To: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
+References: <e15783f7c987e199ecf80b3d858ed5a86d33c508.1779348625.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 13 Dec 2025 09:45:09 +0100
+Subject: [PATCH 12/13] net: airoha: Add ethtool priv_flags callbacks
+
+Introduce ethtool priv_flags callbacks in order to allow the user to
+select if the configured device will be used as hw lan or wan and enable
+or disable gdm2 loopback (used for hw QoS).
+
+Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c  | 173 ++++++++++++++++++++++
+ drivers/net/ethernet/airoha/airoha_regs.h |   1 +
+ 2 files changed, 174 insertions(+)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -540,7 +540,7 @@ static bool airoha_fe_lro_is_enabled(str
+ static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
+ {
+-      struct airoha_qdma *qdma = dev->qdma;
++      struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+       struct airoha_eth *eth = qdma->eth;
+       int qdma_id = qdma - &eth->qdma[0];
+       int i, lro_queue_index = 0;
+@@ -1158,7 +1158,7 @@ static void airoha_qdma_wake_netdev_txqs
+                       if (!dev)
+                               continue;
+-                      if (dev->qdma != qdma)
++                      if (rcu_access_pointer(dev->qdma) != qdma)
+                               continue;
+                       netdev = netdev_from_priv(dev);
+@@ -2031,7 +2031,8 @@ static void airoha_update_netdev_feature
+                       if (!iter_dev || iter_dev == dev)
+                               continue;
+-                      if (iter_dev->qdma != dev->qdma)
++                      if (rcu_access_pointer(iter_dev->qdma) !=
++                          rcu_access_pointer(dev->qdma))
+                               continue;
+                       netdev = netdev_from_priv(iter_dev);
+@@ -2043,20 +2044,57 @@ static void airoha_update_netdev_feature
+       }
+ }
++static void airoha_qdma_start(struct airoha_qdma *qdma)
++{
++      airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
++                      GLOBAL_CFG_TX_DMA_EN_MASK |
++                      GLOBAL_CFG_RX_DMA_EN_MASK);
++      qdma->users++;
++}
++
++static void airoha_qdma_stop(struct airoha_qdma *qdma)
++{
++      u32 status;
++
++      if (--qdma->users)
++              return;
++
++      airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
++                        GLOBAL_CFG_TX_DMA_EN_MASK |
++                        GLOBAL_CFG_RX_DMA_EN_MASK);
++
++      if (read_poll_timeout(airoha_qdma_rr, status,
++                            !(status & (GLOBAL_CFG_TX_DMA_BUSY_MASK |
++                                        GLOBAL_CFG_RX_DMA_BUSY_MASK)),
++                            USEC_PER_MSEC, 50 * USEC_PER_MSEC, true,
++                            qdma, REG_QDMA_GLOBAL_CFG))
++              dev_warn(qdma->eth->dev, "QDMA DMA engine busy timeout\n");
++
++      for (int i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
++              if (!qdma->q_tx[i].ndesc)
++                      continue;
++
++              airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
++      }
++}
++
+ static int airoha_dev_open(struct net_device *netdev)
+ {
+       int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+       struct airoha_gdm_port *port = dev->port;
+       u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
+-      struct airoha_qdma *qdma = dev->qdma;
+-      struct airoha_eth *eth = qdma->eth;
+-      int qdma_id = qdma - &eth->qdma[0];
++      struct airoha_eth *eth = dev->eth;
++      struct airoha_qdma *qdma;
++      int qdma_id;
+       /* HW LRO is configured on the QDMA and it is shared between
+        * all the devices using it. Refuse to open a second device on
+        * the same QDMA if LRO is enabled on any device sharing it.
+        */
++      qdma = airoha_qdma_deref(dev);
++      qdma_id = qdma - &eth->qdma[0];
++
+       if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
+               netdev_warn(netdev, "required to disable HW GRO on QDMA%d\n",
+                           qdma_id);
+@@ -2105,10 +2143,7 @@ static int airoha_dev_open(struct net_de
+       }
+       port->users++;
+-      airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
+-                      GLOBAL_CFG_TX_DMA_EN_MASK |
+-                      GLOBAL_CFG_RX_DMA_EN_MASK);
+-      qdma->users++;
++      airoha_qdma_start(qdma);
+       if (!airoha_is_lan_gdm_dev(dev) &&
+           airoha_ppe_is_enabled(eth, 1)) {
+@@ -2154,8 +2189,7 @@ static int airoha_dev_stop(struct net_de
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+       struct airoha_gdm_port *port = dev->port;
+-      struct airoha_qdma *qdma = dev->qdma;
+-      struct airoha_eth *eth = qdma->eth;
++      struct airoha_qdma *qdma;
+       int i;
+       netif_tx_disable(netdev);
+@@ -2163,25 +2197,14 @@ static int airoha_dev_stop(struct net_de
+       for (i = 0; i < netdev->num_tx_queues; i++)
+               netdev_tx_reset_subqueue(netdev, i);
++      qdma = airoha_qdma_deref(dev);
+       if (--port->users)
+-              airoha_set_port_mtu(eth, port);
++              airoha_set_port_mtu(dev->eth, port);
+       else
+-              airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
++              airoha_set_gdm_port_fwd_cfg(qdma->eth,
++                                          REG_GDM_FWD_CFG(port->id),
+                                           FE_PSE_PORT_DROP);
+-
+-      if (!--qdma->users) {
+-              airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
+-                                GLOBAL_CFG_TX_DMA_EN_MASK |
+-                                GLOBAL_CFG_RX_DMA_EN_MASK);
+-
+-              for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
+-                      if (!qdma->q_tx[i].ndesc)
+-                              continue;
+-
+-                      airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
+-              }
+-      }
+-
++      airoha_qdma_stop(qdma);
+       airoha_update_netdev_features(dev);
+ #if defined(CONFIG_PCS_AIROHA)
+@@ -2275,6 +2298,53 @@ static int airoha_enable_gdm2_loopback(s
+       return 0;
+ }
++static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
++{
++      struct airoha_gdm_port *port = dev->port;
++      struct airoha_eth *eth = dev->eth;
++      int i, src_port;
++      u32 pse_port;
++
++      src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
++      if (src_port < 0)
++              return src_port;
++
++      airoha_fe_clear(eth,
++                      REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
++                      SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
++
++      airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
++                    GDM_STRIP_CRC_MASK);
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
++                                  FE_PSE_PORT_DROP);
++      airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
++                      LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
++      pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
++                                               : FE_PSE_PORT_PPE1;
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
++                                  pse_port);
++
++      airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
++                    FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
++
++      for (i = 0; i < eth->soc->num_ppe; i++)
++              airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
++                              DFT_CPORT_MASK(AIROHA_GDM2_IDX));
++
++      /* Enable VIP and IFC for GDM2 */
++      airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
++      airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
++
++      if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
++              u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
++
++              airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
++                            FC_MAP6_DEF_VALUE & mask);
++      }
++
++      return 0;
++}
++
+ static struct airoha_gdm_dev *
+ airoha_get_wan_gdm_dev(struct airoha_eth *eth)
+ {
+@@ -2301,15 +2371,30 @@ airoha_get_wan_gdm_dev(struct airoha_eth
+ static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
+ {
+       struct net_device *netdev = netdev_from_priv(dev);
++      struct airoha_qdma *cur_qdma, *qdma;
+       struct airoha_eth *eth = dev->eth;
+       int ppe_id;
+       /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
+-      dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
+-      netdev->irq = dev->qdma->irq_banks[0].irq;
++      qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
++      cur_qdma = airoha_qdma_deref(dev);
++      if (netif_running(netdev))
++              airoha_qdma_start(qdma);
++
++      rcu_assign_pointer(dev->qdma, qdma);
++      netdev->irq = qdma->irq_banks[0].irq;
+       ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
+       airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
++
++      if (!cur_qdma)
++              return;
++
++      synchronize_rcu();
++      netif_tx_wake_all_queues(netdev);
++
++      if (netif_running(netdev))
++              airoha_qdma_stop(cur_qdma);
+ }
+ static int airoha_dev_init(struct net_device *netdev)
+@@ -2473,8 +2558,9 @@ static netdev_features_t airoha_dev_fix_
+                                                netdev_features_t features)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_qdma *qdma = dev->qdma;
++      struct airoha_qdma *qdma;
++      qdma = airoha_qdma_deref(dev);
+       if (qdma->users > 1)
+               features &= ~NETIF_F_GRO_HW;
+@@ -2486,9 +2572,8 @@ static int airoha_dev_set_features(struc
+ {
+       netdev_features_t diff = netdev->features ^ features;
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_qdma *qdma = dev->qdma;
+-      struct airoha_eth *eth = qdma->eth;
+-      int qdma_id = qdma - &eth->qdma[0];
++      struct airoha_qdma *qdma = airoha_qdma_deref(dev);
++      int qdma_id = qdma - &dev->eth->qdma[0];
+       if (!(diff & NETIF_F_GRO_HW))
+               return 0;
+@@ -2496,7 +2581,7 @@ static int airoha_dev_set_features(struc
+       if (features & NETIF_F_GRO_HW)
+               airoha_dev_lro_enable(dev);
+       else
+-              airoha_fe_lro_disable(eth, qdma_id);
++              airoha_fe_lro_disable(dev->eth, qdma_id);
+       return 0;
+ }
+@@ -2505,9 +2590,9 @@ static netdev_tx_t airoha_dev_xmit(struc
+                                  struct net_device *netdev)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_qdma *qdma = dev->qdma;
+       u32 nr_frags, tag, msg0, msg1, len;
+       struct airoha_queue_entry *e;
++      struct airoha_qdma *qdma;
+       struct netdev_queue *txq;
+       struct airoha_queue *q;
+       LIST_HEAD(tx_list);
+@@ -2516,6 +2601,8 @@ static netdev_tx_t airoha_dev_xmit(struc
+       u16 index;
+       u8 fport;
++      rcu_read_lock();
++      qdma = rcu_dereference(dev->qdma);
+       qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
+       tag = airoha_get_dsa_tag(skb, netdev);
+@@ -2562,6 +2649,8 @@ static netdev_tx_t airoha_dev_xmit(struc
+               netif_tx_stop_queue(txq);
+               q->txq_stopped = true;
+               spin_unlock_bh(&q->lock);
++              rcu_read_unlock();
++
+               return NETDEV_TX_BUSY;
+       }
+@@ -2624,6 +2713,7 @@ static netdev_tx_t airoha_dev_xmit(struc
+                               FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
+       spin_unlock_bh(&q->lock);
++      rcu_read_unlock();
+       return NETDEV_TX_OK;
+@@ -2639,6 +2729,7 @@ error_unmap:
+ error:
+       dev_kfree_skb_any(skb);
+       netdev->stats.tx_dropped++;
++      rcu_read_unlock();
+       return NETDEV_TX_OK;
+ }
+@@ -2720,17 +2811,19 @@ static int airoha_qdma_set_chan_tx_sched
+                                        const u16 *weights, u8 n_weights)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
++      struct airoha_qdma *qdma;
+       int i;
++      qdma = airoha_qdma_deref(dev);
+       for (i = 0; i < AIROHA_NUM_TX_RING; i++)
+-              airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
++              airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
+                                 TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
+       for (i = 0; i < n_weights; i++) {
+               u32 status;
+               int err;
+-              airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
++              airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG,
+                              TWRR_RW_CMD_MASK |
+                              FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
+                              FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
+@@ -2738,12 +2831,12 @@ static int airoha_qdma_set_chan_tx_sched
+               err = read_poll_timeout(airoha_qdma_rr, status,
+                                       status & TWRR_RW_CMD_DONE,
+                                       USEC_PER_MSEC, 10 * USEC_PER_MSEC,
+-                                      true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
++                                      true, qdma, REG_TXWRR_WEIGHT_CFG);
+               if (err)
+                       return err;
+       }
+-      airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
++      airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3),
+                       CHAN_QOS_MODE_MASK(channel),
+                       __field_prep(CHAN_QOS_MODE_MASK(channel), mode));
+@@ -2807,13 +2900,15 @@ static int airoha_qdma_get_tx_ets_stats(
+                                       struct tc_ets_qopt_offload *opt)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_qdma *qdma = dev->qdma;
++      u64 cpu_tx_packets, fwd_tx_packets, tx_packets;
++      struct airoha_qdma *qdma;
+-      u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+-      u64 fwd_tx_packets = airoha_qdma_rr(qdma,
+-                                          REG_CNTR_VAL((channel << 1) + 1));
+-      u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
+-                       (fwd_tx_packets - dev->fwd_tx_packets);
++      qdma = airoha_qdma_deref(dev);
++      cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
++      fwd_tx_packets = airoha_qdma_rr(qdma,
++                                      REG_CNTR_VAL((channel << 1) + 1));
++      tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
++                   (fwd_tx_packets - dev->fwd_tx_packets);
+       _bstats_update(opt->stats.bstats, 0, tx_packets);
+       dev->cpu_tx_packets = cpu_tx_packets;
+@@ -3073,16 +3168,18 @@ static int airoha_qdma_set_tx_rate_limit
+                                        u32 bucket_size)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
++      struct airoha_qdma *qdma;
+       int i, err;
++      qdma = airoha_qdma_deref(dev);
+       for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
+-              err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
++              err = airoha_qdma_set_trtcm_config(qdma, channel,
+                                                  REG_EGRESS_TRTCM_CFG, i,
+                                                  !!rate, TRTCM_METER_MODE);
+               if (err)
+                       return err;
+-              err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
++              err = airoha_qdma_set_trtcm_token_bucket(qdma, channel,
+                                                        REG_EGRESS_TRTCM_CFG,
+                                                        i, rate, bucket_size);
+               if (err)
+@@ -3118,11 +3215,12 @@ static int airoha_tc_htb_alloc_leaf_queu
+       u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
+       int err, num_tx_queues = netdev->real_num_tx_queues;
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_qdma *qdma = dev->qdma;
++      struct airoha_qdma *qdma;
+       /* Here we need to check the requested QDMA channel is not already
+        * in use by another net_device running on the same QDMA block.
+        */
++      qdma = airoha_qdma_deref(dev);
+       if (test_and_set_bit(channel, qdma->qos_channel_map)) {
+               NL_SET_ERR_MSG_MOD(opt->extack,
+                                  "qdma qos channel already in use");
+@@ -3156,7 +3254,7 @@ static int airoha_qdma_set_rx_meter(stru
+                                   u32 rate, u32 bucket_size,
+                                   enum trtcm_unit_type unit_type)
+ {
+-      struct airoha_qdma *qdma = dev->qdma;
++      struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+       int i;
+       for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+@@ -3330,11 +3428,12 @@ static int airoha_dev_setup_tc_block(str
+ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+-      struct airoha_qdma *qdma = dev->qdma;
++      struct airoha_qdma *qdma;
+       netif_set_real_num_tx_queues(netdev, netdev->real_num_tx_queues - 1);
+       airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
++      qdma = airoha_qdma_deref(dev);
+       clear_bit(queue, qdma->qos_channel_map);
+       clear_bit(queue, dev->qos_sq_bmap);
+ }
+@@ -3355,6 +3454,95 @@ static int airoha_tc_htb_delete_leaf_que
+       return 0;
+ }
++static void airoha_disable_qos_for_gdm34(struct net_device *netdev)
++{
++      struct airoha_gdm_dev *dev = netdev_priv(netdev);
++      struct airoha_gdm_port *port = dev->port;
++      int err;
++
++      if (port->id != AIROHA_GDM3_IDX &&
++          port->id != AIROHA_GDM4_IDX)
++              return;
++
++      err = airoha_disable_gdm2_loopback(dev);
++      if (err)
++              netdev_warn(netdev,
++                          "failed disabling GDM2 loopback: %d\n", err);
++
++      dev->flags &= ~AIROHA_PRIV_F_WAN;
++      airoha_dev_set_qdma(dev);
++
++      airoha_set_macaddr(dev, netdev->dev_addr);
++      if (netif_running(netdev))
++              airoha_set_gdm_port_fwd_cfg(dev->eth,
++                                          REG_GDM_FWD_CFG(port->id),
++                                          FE_PSE_PORT_PPE1);
++}
++
++static int airoha_enable_qos_for_gdm34(struct net_device *netdev,
++                                     struct netlink_ext_ack *extack)
++{
++      struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);
++      struct airoha_gdm_port *port = dev->port;
++      struct airoha_eth *eth = dev->eth;
++      int err = -EBUSY;
++
++      if (port->id != AIROHA_GDM3_IDX &&
++          port->id != AIROHA_GDM4_IDX) {
++              /* HW QoS is always supported by GDM1 and GDM2 */
++              return 0;
++      }
++
++      if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */
++              return 0;
++
++      mutex_lock(&flow_offload_mutex);
++
++      wan_dev = airoha_get_wan_gdm_dev(eth);
++      if (wan_dev) {
++              if ((wan_dev->flags & AIROHA_PRIV_F_QOS) ||
++                  wan_dev->port->id == AIROHA_GDM2_IDX) {
++                      NL_SET_ERR_MSG_MOD(extack,
++                                         "QoS configured for WAN device");
++                      goto error_unlock;
++              }
++              airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
++      }
++
++      dev->flags |= AIROHA_PRIV_F_WAN;
++      airoha_dev_set_qdma(dev);
++      err = airoha_enable_gdm2_loopback(dev);
++      if (err)
++              goto error_disable_wan;
++
++      err = airoha_set_macaddr(dev, netdev->dev_addr);
++      if (err)
++              goto error_disable_loopback;
++
++      if (netif_running(netdev)) {
++              u32 pse_port;
++
++              pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
++                                                       : FE_PSE_PORT_PPE1;
++              airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
++                                          pse_port);
++      }
++
++      mutex_unlock(&flow_offload_mutex);
++
++      return 0;
++
++error_disable_loopback:
++      airoha_disable_gdm2_loopback(dev);
++error_disable_wan:
++      dev->flags &= ~AIROHA_PRIV_F_WAN;
++      airoha_dev_set_qdma(dev);
++error_unlock:
++      mutex_unlock(&flow_offload_mutex);
++
++      return err;
++}
++
+ static int airoha_tc_htb_destroy(struct net_device *netdev)
+ {
+       struct airoha_gdm_dev *dev = netdev_priv(netdev);
+@@ -3363,6 +3551,8 @@ static int airoha_tc_htb_destroy(struct
+       for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
+               airoha_tc_remove_htb_queue(netdev, q);
++      dev->flags &= ~AIROHA_PRIV_F_QOS;
++
+       return 0;
+ }
+@@ -3382,24 +3572,33 @@ static int airoha_tc_get_htb_get_leaf_qu
+       return 0;
+ }
+-static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
++static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,
+                                    struct tc_htb_qopt_offload *opt)
+ {
+       switch (opt->command) {
+-      case TC_HTB_CREATE:
++      case TC_HTB_CREATE: {
++              struct airoha_gdm_dev *dev = netdev_priv(netdev);
++              int err;
++
++              err = airoha_enable_qos_for_gdm34(netdev, opt->extack);
++              if (err)
++                      return err;
++
++              dev->flags |= AIROHA_PRIV_F_QOS;
+               break;
++      }
+       case TC_HTB_DESTROY:
+-              return airoha_tc_htb_destroy(dev);
++              return airoha_tc_htb_destroy(netdev);
+       case TC_HTB_NODE_MODIFY:
+-              return airoha_tc_htb_modify_queue(dev, opt);
++              return airoha_tc_htb_modify_queue(netdev, opt);
+       case TC_HTB_LEAF_ALLOC_QUEUE:
+-              return airoha_tc_htb_alloc_leaf_queue(dev, opt);
++              return airoha_tc_htb_alloc_leaf_queue(netdev, opt);
+       case TC_HTB_LEAF_DEL:
+       case TC_HTB_LEAF_DEL_LAST:
+       case TC_HTB_LEAF_DEL_LAST_FORCE:
+-              return airoha_tc_htb_delete_leaf_queue(dev, opt);
++              return airoha_tc_htb_delete_leaf_queue(netdev, opt);
+       case TC_HTB_LEAF_QUERY_QUEUE:
+-              return airoha_tc_get_htb_get_leaf_queue(dev, opt);
++              return airoha_tc_get_htb_get_leaf_queue(netdev, opt);
+       default:
+               return -EOPNOTSUPP;
+       }
+@@ -3509,8 +3708,8 @@ static void airoha_mac_link_up(struct ph
+ {
+       struct airoha_gdm_dev *dev = container_of(config, struct airoha_gdm_dev,
+                                                 phylink_config);
++      struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+       struct airoha_gdm_port *port = dev->port;
+-      struct airoha_qdma *qdma = dev->qdma;
+       struct airoha_eth *eth = qdma->eth;
+       u32 frag_size_tx, frag_size_rx;
+--- a/drivers/net/ethernet/airoha/airoha_regs.h
++++ b/drivers/net/ethernet/airoha/airoha_regs.h
+@@ -402,6 +402,7 @@
+ #define REG_SRC_PORT_FC_MAP6          0x2298
+ #define FC_ID_OF_SRC_PORT_MASK(_n)    GENMASK(4 + ((_n) << 3), ((_n) << 3))
++#define FC_MAP6_DEF_VALUE             0x1b1a1918
+ #define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -552,11 +552,12 @@ struct airoha_qdma {
+ enum airoha_priv_flags {
+       AIROHA_PRIV_F_WAN = BIT(0),
++      AIROHA_PRIV_F_QOS = BIT(1),
+ };
+ struct airoha_gdm_dev {
++      struct airoha_qdma __rcu *qdma;
+       struct airoha_gdm_port *port;
+-      struct airoha_qdma *qdma;
+       struct airoha_eth *eth;
+ #if defined(CONFIG_PCS_AIROHA)
+@@ -705,6 +706,16 @@ int airoha_get_fe_port(struct airoha_gdm
+ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
+                            struct airoha_gdm_dev *dev);
++extern struct mutex flow_offload_mutex;
++
++static inline struct airoha_qdma *
++airoha_qdma_deref(struct airoha_gdm_dev *dev)
++{
++      return rcu_dereference_protected(dev->qdma,
++                                       lockdep_rtnl_is_held() ||
++                                       lockdep_is_held(&flow_offload_mutex));
++}
++
+ void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
+ bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
+ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
+--- a/drivers/net/ethernet/airoha/airoha_ppe.c
++++ b/drivers/net/ethernet/airoha/airoha_ppe.c
+@@ -16,7 +16,10 @@
+ #include "airoha_regs.h"
+ #include "airoha_eth.h"
+-static DEFINE_MUTEX(flow_offload_mutex);
++/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port
++ * configuration.
++ */
++DEFINE_MUTEX(flow_offload_mutex);
+ static DEFINE_SPINLOCK(ppe_lock);
+ static const struct rhashtable_params airoha_flow_table_params = {
+@@ -87,8 +90,8 @@ static u32 airoha_ppe_get_timestamp(stru
+ void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
+ {
+-      struct airoha_qdma *qdma = dev->qdma;
+-      struct airoha_eth *eth = qdma->eth;
++      struct airoha_qdma *qdma = airoha_qdma_deref(dev);
++      struct airoha_eth *eth = dev->eth;
+       u8 qdma_id = qdma - &eth->qdma[0];
+       u32 fe_cpu_port;
index 072b50fe35c665946a63df8127b225bf2b3b0698..d17c537daa21dd60ad9851f3d6e034f772b8ab0f 100644 (file)
@@ -16,7 +16,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -185,10 +185,15 @@ static void airoha_fe_maccr_init(struct
+@@ -188,10 +188,15 @@ static void airoha_fe_maccr_init(struct
  {
        int p;
  
@@ -33,8 +33,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK,
                      FIELD_PREP(CDM_VLAN_MASK, 0x8100));
-@@ -1908,13 +1913,24 @@ static void airoha_update_hw_stats(struc
-       spin_unlock(&port->stats.lock);
+@@ -2078,15 +2083,25 @@ static void airoha_qdma_stop(struct airo
+       }
  }
  
 +static void airoha_dev_set_mtu(struct net_device *netdev)
@@ -54,17 +54,19 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
        struct airoha_gdm_port *port = dev->port;
 -      u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
-       struct airoha_qdma *qdma = dev->qdma;
+       struct airoha_eth *eth = dev->eth;
+       struct airoha_qdma *qdma;
+-      int qdma_id;
 +      u32 pse_port = FE_PSE_PORT_PPE1;
-+      int err;
++      int err, qdma_id;
  
-       if (port->id == AIROHA_GDM2_IDX && airoha_is_lan_gdm_dev(dev)) {
-               /* GDM2 can be used just as wan */
-@@ -1947,19 +1963,7 @@ static int airoha_dev_open(struct net_de
-               airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+       /* HW LRO is configured on the QDMA and it is shared between
+        * all the devices using it. Refuse to open a second device on
+@@ -2128,19 +2143,7 @@ static int airoha_dev_open(struct net_de
+               airoha_fe_clear(eth, REG_GDM_INGRESS_CFG(port->id),
                                GDM_STAG_EN_MASK);
  
--      cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
+-      cur_len = airoha_fe_get(eth, REG_GDM_LEN_CFG(port->id),
 -                              GDM_LONG_LEN_MASK);
 -      if (!port->users || len > cur_len) {
 -              /* Opening a sibling net_device with a larger MTU updates the
@@ -72,7 +74,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -               * multiple net_devices with different MTUs to share the same
 -               * GDM port.
 -               */
--              airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
+-              airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
 -                            GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
 -                            FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
 -                            FIELD_PREP(GDM_LONG_LEN_MASK, len));
@@ -80,8 +82,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +      airoha_dev_set_mtu(netdev);
        port->users++;
  
-       airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
-@@ -1976,30 +1980,6 @@ static int airoha_dev_open(struct net_de
+       airoha_qdma_start(qdma);
+@@ -2161,30 +2164,6 @@ static int airoha_dev_open(struct net_de
        return 0;
  }
  
@@ -98,7 +100,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 -              if (!dev)
 -                      continue;
 -
--              netdev = dev->dev;
+-              netdev = netdev_from_priv(dev);
 -              if (netif_running(netdev))
 -                      len = max_t(u32, len, netdev->mtu);
 -      }
@@ -112,16 +114,16 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  static int airoha_dev_stop(struct net_device *netdev)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
-@@ -2013,7 +1993,7 @@ static int airoha_dev_stop(struct net_de
-               netdev_tx_reset_subqueue(netdev, i);
+@@ -2199,7 +2178,7 @@ static int airoha_dev_stop(struct net_de
  
+       qdma = airoha_qdma_deref(dev);
        if (--port->users)
 -              airoha_set_port_mtu(dev->eth, port);
 +              airoha_ppe_set_mtu(dev);
        else
                airoha_set_gdm_port_fwd_cfg(qdma->eth,
                                            REG_GDM_FWD_CFG(port->id),
-@@ -2083,10 +2063,6 @@ static int airoha_enable_gdm2_loopback(s
+@@ -2258,10 +2237,6 @@ static int airoha_enable_gdm2_loopback(s
                      FIELD_PREP(LPBK_CHAN_MASK, chan) |
                      LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK |
                      LBK_CHAN_MODE_MASK | LPBK_EN_MASK);
@@ -132,7 +134,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        /* Forward the traffic to the proper GDM port */
        pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
                                               : FE_PSE_PORT_GDM4;
-@@ -2270,7 +2246,7 @@ static int airoha_dev_change_mtu(struct
+@@ -2469,7 +2444,7 @@ static int airoha_dev_change_mtu(struct
  
        WRITE_ONCE(netdev->mtu, mtu);
        if (port->users)
@@ -141,14 +143,22 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  
        return 0;
  }
-@@ -2613,6 +2589,7 @@ static int airoha_dev_set_wan_flag(struc
-               default:
-                       return -EOPNOTSUPP;
-               }
-+              airoha_dev_set_mtu(netdev);
-       } else {
-               switch (port->id) {
-               case AIROHA_GDM3_IDX:
+@@ -3471,6 +3446,7 @@ static void airoha_disable_qos_for_gdm34
+       dev->flags &= ~AIROHA_PRIV_F_WAN;
+       airoha_dev_set_qdma(dev);
++      airoha_dev_set_mtu(netdev);
+       airoha_set_macaddr(dev, netdev->dev_addr);
+       if (netif_running(netdev))
+@@ -3519,6 +3495,7 @@ static int airoha_enable_qos_for_gdm34(s
+       if (err)
+               goto error_disable_loopback;
++      airoha_dev_set_mtu(netdev);
+       if (netif_running(netdev)) {
+               u32 pse_port;
 --- a/drivers/net/ethernet/airoha/airoha_eth.h
 +++ b/drivers/net/ethernet/airoha/airoha_eth.h
 @@ -23,6 +23,7 @@
@@ -159,9 +169,9 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  #define AIROHA_MAX_PACKET_SIZE                2048
  #define AIROHA_NUM_QOS_CHANNELS               4
  #define AIROHA_NUM_QOS_QUEUES         8
-@@ -706,6 +707,7 @@ int airoha_get_fe_port(struct airoha_gdm
- bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
-                            struct airoha_gdm_dev *dev);
+@@ -716,6 +717,7 @@ airoha_qdma_deref(struct airoha_gdm_dev
+                                        lockdep_is_held(&flow_offload_mutex));
+ }
  
 +void airoha_ppe_set_mtu(struct airoha_gdm_dev *dev);
  void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
@@ -169,7 +179,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
 --- a/drivers/net/ethernet/airoha/airoha_ppe.c
 +++ b/drivers/net/ethernet/airoha/airoha_ppe.c
-@@ -98,6 +98,35 @@ void airoha_ppe_set_cpu_port(struct airo
+@@ -101,6 +101,39 @@ void airoha_ppe_set_cpu_port(struct airo
                      __field_prep(DFT_CPORT_MASK(fport), fe_cpu_port));
  }
  
@@ -177,20 +187,24 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
 +{
 +      struct airoha_gdm_port *port = dev->port;
 +      struct airoha_eth *eth = dev->eth;
++      struct airoha_qdma *qdma;
 +      int i, ppe_id, index;
 +      u32 mtu = 0;
 +
++      qdma = airoha_qdma_deref(dev);
 +      for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
 +              struct airoha_gdm_dev *d = port->devs[i];
 +              struct net_device *netdev;
++              struct airoha_qdma *q;
 +
 +              if (!d)
 +                      continue;
 +
-+              if (d->qdma != dev->qdma)
++              q = airoha_qdma_deref(d);
++              if (q != qdma)
 +                      continue;
 +
-+              netdev = d->dev;
++              netdev = netdev_from_priv(d);
 +              if (netif_running(netdev))
 +                      mtu = max_t(u32, mtu, netdev->mtu);
 +      }
@@ -205,7 +219,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
  static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
  {
        u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries;
-@@ -116,8 +145,6 @@ static void airoha_ppe_hw_init(struct ai
+@@ -119,8 +152,6 @@ static void airoha_ppe_hw_init(struct ai
                PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries);
  
        for (i = 0; i < eth->soc->num_ppe; i++) {
@@ -214,7 +228,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
                airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
                             ppe->foe_dma + sram_tb_size);
  
-@@ -167,15 +194,6 @@ static void airoha_ppe_hw_init(struct ai
+@@ -170,15 +201,6 @@ static void airoha_ppe_hw_init(struct ai
                airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
                airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
                                PPE_FLOW_CFG_IP6_6RD_MASK);
@@ -230,7 +244,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
        }
  
        for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
-@@ -197,6 +215,7 @@ static void airoha_ppe_hw_init(struct ai
+@@ -200,6 +222,7 @@ static void airoha_ppe_hw_init(struct ai
                                 airoha_ppe_is_enabled(eth, 1);
                        fport = airoha_get_fe_port(dev);
                        airoha_ppe_set_cpu_port(dev, ppe_id, fport);
index 25db5b6661e4b1a155670d0d9dc3974cd444f860..1aec4e91fcf3b722d9741b178a1eefd02a54bd28 100644 (file)
@@ -31,7 +31,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -612,6 +612,14 @@ static int airoha_fe_init(struct airoha_
+@@ -640,6 +640,14 @@ static int airoha_fe_init(struct airoha_
        airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM4_IDX),
                      GDM_PAD_EN_MASK | GDM_STRIP_CRC_MASK);
  
@@ -46,7 +46,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
        airoha_fe_crsn_qsel_init(eth);
  
        airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK);
-@@ -1768,149 +1776,169 @@ static void airoha_qdma_stop_napi(struct
+@@ -1872,149 +1880,169 @@ static void airoha_qdma_stop_napi(struct
        }
  }
  
@@ -269,8 +269,8 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 +      spin_unlock(&port->stats_lock);
  }
  
- static void airoha_dev_set_mtu(struct net_device *netdev)
-@@ -2220,23 +2248,22 @@ static void airoha_dev_get_stats64(struc
+ static void airoha_update_netdev_features(struct airoha_gdm_dev *dev)
+@@ -2418,23 +2446,22 @@ static void airoha_dev_get_stats64(struc
                                   struct rtnl_link_stats64 *storage)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -306,7 +306,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
  }
  
  static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
-@@ -2632,20 +2659,19 @@ static void airoha_ethtool_get_mac_stats
+@@ -2723,20 +2750,19 @@ static void airoha_ethtool_get_mac_stats
                                         struct ethtool_eth_mac_stats *stats)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -336,7 +336,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
  }
  
  static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
-@@ -2665,8 +2691,7 @@ airoha_ethtool_get_rmon_stats(struct net
+@@ -2756,8 +2782,7 @@ airoha_ethtool_get_rmon_stats(struct net
                              const struct ethtool_rmon_hist_range **ranges)
  {
        struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -346,7 +346,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
        unsigned int start;
  
        BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
-@@ -2679,7 +2704,7 @@ airoha_ethtool_get_rmon_stats(struct net
+@@ -2770,7 +2795,7 @@ airoha_ethtool_get_rmon_stats(struct net
        do {
                int i;
  
@@ -355,7 +355,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
                stats->fragments = hw_stats->rx_fragment;
                stats->jabbers = hw_stats->rx_jabber;
                for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
-@@ -2687,7 +2712,7 @@ airoha_ethtool_get_rmon_stats(struct net
+@@ -2778,7 +2803,7 @@ airoha_ethtool_get_rmon_stats(struct net
                        stats->hist[i] = hw_stats->rx_len[i];
                        stats->hist_tx[i] = hw_stats->tx_len[i];
                }
@@ -363,16 +363,16 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 +      } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
  }
  
- static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
-@@ -3695,6 +3720,7 @@ static int airoha_alloc_gdm_device(struc
+ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
+@@ -3849,6 +3874,7 @@ static int airoha_alloc_gdm_device(struc
  
        netdev->dev.of_node = of_node_get(np);
        dev = netdev_priv(netdev);
 +      u64_stats_init(&dev->stats.syncp);
-       dev->dev = netdev;
        dev->port = port;
        dev->eth = eth;
-@@ -3743,9 +3769,8 @@ static int airoha_alloc_gdm_port(struct
+       dev->nbq = nbq;
+@@ -3896,9 +3922,8 @@ static int airoha_alloc_gdm_port(struct
        if (!port)
                return -ENOMEM;
  
index 75a49306c2ca287266fcd34f2f056d1412d8100c..2df1996d19a80fb3d2759a09f86ba5930ca732f5 100644 (file)
@@ -18,7 +18,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 
 --- a/drivers/net/ethernet/airoha/airoha_eth.c
 +++ b/drivers/net/ethernet/airoha/airoha_eth.c
-@@ -2345,17 +2345,9 @@ static u32 airoha_get_dsa_tag(struct sk_
+@@ -2543,17 +2543,9 @@ static u32 airoha_get_dsa_tag(struct sk_
  int airoha_get_fe_port(struct airoha_gdm_dev *dev)
  {
        struct airoha_gdm_port *port = dev->port;
@@ -37,4 +37,4 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
 +                                         : port->id;
  }
  
- static int airoha_dev_set_features(struct net_device *netdev,
+ static netdev_features_t airoha_dev_fix_features(struct net_device *netdev,
diff --git a/target/linux/airoha/patches-6.18/921-net-airoha-move-get_sport-callback-at-the-beginning-.patch b/target/linux/airoha/patches-6.18/921-net-airoha-move-get_sport-callback-at-the-beginning-.patch
new file mode 100644 (file)
index 0000000..9cd99c7
--- /dev/null
@@ -0,0 +1,36 @@
+From 18aa76cd820c412358dc36d115b8e02d24a78f39 Mon Sep 17 00:00:00 2001
+Message-ID: <18aa76cd820c412358dc36d115b8e02d24a78f39.1780942778.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Mon, 8 Jun 2026 20:10:35 +0200
+Subject: [PATCH] net: airoha: move get_sport() callback at the beginning of
+ airoha_enable_gdm2_loopback()
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -2250,6 +2250,10 @@ static int airoha_enable_gdm2_loopback(s
+       u32 val, pse_port, chan;
+       int i, src_port;
++      src_port = eth->soc->ops.get_sport(port, dev->nbq);
++      if (src_port < 0)
++              return src_port;
++
+       airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+                                   FE_PSE_PORT_DROP);
+       airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+@@ -2275,10 +2279,6 @@ static int airoha_enable_gdm2_loopback(s
+       airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+       airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
+-      src_port = eth->soc->ops.get_sport(port, dev->nbq);
+-      if (src_port < 0)
+-              return src_port;
+-
+       airoha_fe_rmw(eth, REG_FE_WAN_PORT,
+                     WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
+                     FIELD_PREP(WAN0_MASK, src_port));
diff --git a/target/linux/airoha/patches-6.18/922-net-airoha-simplify-WAN-device-check-in-airoha_dev_i.patch b/target/linux/airoha/patches-6.18/922-net-airoha-simplify-WAN-device-check-in-airoha_dev_i.patch
new file mode 100644 (file)
index 0000000..c9e788e
--- /dev/null
@@ -0,0 +1,42 @@
+From 3d67e4ba79bd4b40f520d26af098f133b5f551d4 Mon Sep 17 00:00:00 2001
+Message-ID: <3d67e4ba79bd4b40f520d26af098f133b5f551d4.1781097105.git.lorenzo@kernel.org>
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 6 Jun 2026 08:53:03 +0200
+Subject: [PATCH] net: airoha: simplify WAN device check in airoha_dev_init()
+
+airoha_register_gdm_devices() iterates eth->ports[] in order, so GDM2's
+netdev is always registered before GDM3/GDM4. This means the explicit
+check for eth->ports[1] && eth->ports[1]->devs[0] is a redundant
+special-case of what airoha_get_wan_gdm_dev() already covers, since
+GDM2 is always marked as WAN during its own ndo_init.
+Remove the redundant check and rely solely on airoha_get_wan_gdm_dev()
+which handles both the GDM2-present and GDM2-absent cases.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 12 ++----------
+ 1 file changed, 2 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -2407,18 +2407,10 @@ static int airoha_dev_init(struct net_de
+       switch (port->id) {
+       case AIROHA_GDM3_IDX:
+-      case AIROHA_GDM4_IDX: {
+-              struct airoha_eth *eth = dev->eth;
+-
+-              /* GDM2 supports a single net_device */
+-              if (eth->ports[1] && eth->ports[1]->devs[0])
+-                      break;
+-
+-              if (airoha_get_wan_gdm_dev(eth))
++      case AIROHA_GDM4_IDX:
++              if (airoha_get_wan_gdm_dev(dev->eth))
+                       break;
+-
+               fallthrough;
+-      }
+       case AIROHA_GDM2_IDX:
+               /* GDM2 is always used as wan */
+               dev->flags |= AIROHA_PRIV_F_WAN;