]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
airoha: backport upstream patch for Flow Offload support for AN7581 18166/head
authorChristian Marangi <ansuelsmth@gmail.com>
Tue, 4 Mar 2025 22:54:31 +0000 (23:54 +0100)
committerChristian Marangi <ansuelsmth@gmail.com>
Sat, 5 Jul 2025 08:10:35 +0000 (10:10 +0200)
Backpot upstream patch for Flow Offload support for AN7581 and refresh
all affected patch. To correctly work a dedicated firmware is needed to
use the dedicated Network Coprocessor (NPU).

This also introduce good cleanup and moves the driver in a dedicated
Airoha directory. While currently not totally usable (due to lack of
firmware blob) this is needed to backport support for external PHY/SFP
support.

Refresh all affected patch.

Tested-by: Aleksander Jan Bajkowski <olek2@wp.pl> # tested on Quantum W1700k
Tested-by: Andrew LaMarche <andrewjlamarche@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/18166
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
18 files changed:
target/linux/airoha/an7581/config-6.6
target/linux/airoha/patches-6.6/045-v6.14-net-airoha-Fix-wrong-GDM4-register-definition.patch
target/linux/airoha/patches-6.6/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch
target/linux/airoha/patches-6.6/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch
target/linux/airoha/patches-6.6/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch [new file with mode: 0644]
target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch

index 00e09ff83d4ef8f0d6efa78fbd16df60c9738ade..600f1e45b47497d9c46f50e273fcf1417865dd02 100644 (file)
@@ -231,7 +231,8 @@ CONFIG_NET_FLOW_LIMIT=y
 # CONFIG_NET_MEDIATEK_SOC is not set
 CONFIG_NET_SELFTESTS=y
 # CONFIG_NET_VENDOR_3COM is not set
-CONFIG_NET_VENDOR_MEDIATEK=y
+CONFIG_NET_VENDOR_AIROHA=y
+# CONFIG_NET_VENDOR_MEDIATEK is not set
 CONFIG_NLS=y
 CONFIG_NO_HZ_COMMON=y
 CONFIG_NO_HZ_IDLE=y
index e14dd3e6d5fcc36c30c3d8b16e052f20a3d85e42..f17242a6c5c7fd0a271870d4fbc119454100e19c 100644 (file)
@@ -23,8 +23,6 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  drivers/net/ethernet/mediatek/airoha_eth.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/drivers/net/ethernet/mediatek/airoha_eth.c b/drivers/net/ethernet/mediatek/airoha_eth.c
-index 415d784de741..09f448f29124 100644
 --- a/drivers/net/ethernet/mediatek/airoha_eth.c
 +++ b/drivers/net/ethernet/mediatek/airoha_eth.c
 @@ -266,11 +266,11 @@
@@ -41,6 +39,3 @@ index 415d784de741..09f448f29124 100644
  #define GDM4_SPORT_OFF2_MASK          GENMASK(19, 16)
  #define GDM4_SPORT_OFF1_MASK          GENMASK(15, 12)
  #define GDM4_SPORT_OFF0_MASK          GENMASK(11, 8)
--- 
-2.48.1
-
index b381612c39c3d95143d11adbbac67949ffe0603b..9c2443ce69860380af55a1dee2bb6698389be13c 100644 (file)
@@ -22,11 +22,9 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  drivers/net/ethernet/mediatek/airoha_eth.c | 10 +++++-----
  1 file changed, 5 insertions(+), 5 deletions(-)
 
-diff --git a/drivers/net/ethernet/mediatek/airoha_eth.c b/drivers/net/ethernet/mediatek/airoha_eth.c
-index 09f448f29124..aa5f220ddbcf 100644
 --- a/drivers/net/ethernet/mediatek/airoha_eth.c
 +++ b/drivers/net/ethernet/mediatek/airoha_eth.c
-@@ -2556,11 +2556,10 @@ static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
+@@ -2549,11 +2549,10 @@ static u16 airoha_dev_select_queue(struc
  static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
                                   struct net_device *dev)
  {
@@ -39,7 +37,7 @@ index 09f448f29124..aa5f220ddbcf 100644
        struct netdev_queue *txq;
        struct airoha_queue *q;
        void *data = skb->data;
-@@ -2583,8 +2582,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
+@@ -2576,8 +2575,9 @@ static netdev_tx_t airoha_dev_xmit(struc
                if (skb_cow_head(skb, 0))
                        goto error;
  
@@ -51,7 +49,7 @@ index 09f448f29124..aa5f220ddbcf 100644
  
                        tcp_hdr(skb)->check = (__force __sum16)csum;
                        msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1);
-@@ -2613,7 +2613,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
+@@ -2606,7 +2606,7 @@ static netdev_tx_t airoha_dev_xmit(struc
        for (i = 0; i < nr_frags; i++) {
                struct airoha_qdma_desc *desc = &q->desc[index];
                struct airoha_queue_entry *e = &q->entry[index];
@@ -60,6 +58,3 @@ index 09f448f29124..aa5f220ddbcf 100644
                dma_addr_t addr;
                u32 val;
  
--- 
-2.48.1
-
index 4a53e64aa30f6f86855c6003c25f0ea9f8273f00..f20e2034c22c97c11e4447e67d2599ae0208ce61 100644 (file)
@@ -14,11 +14,9 @@ Signed-off-by: Andrew Lunn <andrew@lunn.ch>
  drivers/net/ethernet/mediatek/airoha_eth.c | 10 +++++++++-
  1 file changed, 9 insertions(+), 1 deletion(-)
 
-diff --git a/drivers/net/ethernet/mediatek/airoha_eth.c b/drivers/net/ethernet/mediatek/airoha_eth.c
-index 21d6eed8aece..f463a505f5ba 100644
 --- a/drivers/net/ethernet/mediatek/airoha_eth.c
 +++ b/drivers/net/ethernet/mediatek/airoha_eth.c
-@@ -2342,7 +2342,7 @@ static int airoha_dev_stop(struct net_device *dev)
+@@ -2469,7 +2469,7 @@ static int airoha_dev_stop(struct net_de
  {
        struct airoha_gdm_port *port = netdev_priv(dev);
        struct airoha_qdma *qdma = port->qdma;
@@ -27,7 +25,7 @@ index 21d6eed8aece..f463a505f5ba 100644
  
        netif_tx_disable(dev);
        err = airoha_set_gdm_ports(qdma->eth, false);
-@@ -2353,6 +2353,14 @@ static int airoha_dev_stop(struct net_device *dev)
+@@ -2480,6 +2480,14 @@ static int airoha_dev_stop(struct net_de
                          GLOBAL_CFG_TX_DMA_EN_MASK |
                          GLOBAL_CFG_RX_DMA_EN_MASK);
  
@@ -42,6 +40,3 @@ index 21d6eed8aece..f463a505f5ba 100644
        return 0;
  }
  
--- 
-2.48.1
-
diff --git a/target/linux/airoha/patches-6.6/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch b/target/linux/airoha/patches-6.6/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch
new file mode 100644 (file)
index 0000000..255bbd2
--- /dev/null
@@ -0,0 +1,6825 @@
+From fb3dda82fd38ca42140f29b3082324dcdc128293 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:09 +0100
+Subject: [PATCH 01/15] net: airoha: Move airoha_eth driver in a dedicated
+ folder
+
+The airoha_eth driver has no codebase shared with mtk_eth_soc one.
+Moreover, the upcoming features (flowtable hw offloading, PCS, ..) will
+not reuse any code from MediaTek driver. Move the Airoha driver in a
+dedicated folder.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/Kconfig                   |  2 ++
+ drivers/net/ethernet/Makefile                  |  1 +
+ drivers/net/ethernet/airoha/Kconfig            | 18 ++++++++++++++++++
+ drivers/net/ethernet/airoha/Makefile           |  6 ++++++
+ .../ethernet/{mediatek => airoha}/airoha_eth.c |  0
+ drivers/net/ethernet/mediatek/Kconfig          |  8 --------
+ drivers/net/ethernet/mediatek/Makefile         |  1 -
+ 7 files changed, 27 insertions(+), 9 deletions(-)
+ create mode 100644 drivers/net/ethernet/airoha/Kconfig
+ create mode 100644 drivers/net/ethernet/airoha/Makefile
+ rename drivers/net/ethernet/{mediatek => airoha}/airoha_eth.c (100%)
+
+--- a/drivers/net/ethernet/Kconfig
++++ b/drivers/net/ethernet/Kconfig
+@@ -23,6 +23,8 @@ source "drivers/net/ethernet/actions/Kco
+ source "drivers/net/ethernet/adaptec/Kconfig"
+ source "drivers/net/ethernet/aeroflex/Kconfig"
+ source "drivers/net/ethernet/agere/Kconfig"
++source "drivers/net/ethernet/airoha/Kconfig"
++source "drivers/net/ethernet/mellanox/Kconfig"
+ source "drivers/net/ethernet/alacritech/Kconfig"
+ source "drivers/net/ethernet/allwinner/Kconfig"
+ source "drivers/net/ethernet/alteon/Kconfig"
+--- a/drivers/net/ethernet/Makefile
++++ b/drivers/net/ethernet/Makefile
+@@ -10,6 +10,7 @@ obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adap
+ obj-$(CONFIG_GRETH) += aeroflex/
+ obj-$(CONFIG_NET_VENDOR_ADI) += adi/
+ obj-$(CONFIG_NET_VENDOR_AGERE) += agere/
++obj-$(CONFIG_NET_VENDOR_AIROHA) += airoha/
+ obj-$(CONFIG_NET_VENDOR_ALACRITECH) += alacritech/
+ obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/
+ obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/Kconfig
+@@ -0,0 +1,18 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config NET_VENDOR_AIROHA
++      bool "Airoha devices"
++      depends on ARCH_AIROHA || COMPILE_TEST
++      help
++        If you have a Airoha SoC with ethernet, say Y.
++
++if NET_VENDOR_AIROHA
++
++config NET_AIROHA
++      tristate "Airoha SoC Gigabit Ethernet support"
++      depends on NET_DSA || !NET_DSA
++      select PAGE_POOL
++      help
++        This driver supports the gigabit ethernet MACs in the
++        Airoha SoC family.
++
++endif #NET_VENDOR_AIROHA
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0-only
++#
++# Airoha for the Mediatek SoCs built-in ethernet macs
++#
++
++obj-$(CONFIG_NET_AIROHA) += airoha_eth.o
+--- a/drivers/net/ethernet/mediatek/Kconfig
++++ b/drivers/net/ethernet/mediatek/Kconfig
+@@ -7,14 +7,6 @@ config NET_VENDOR_MEDIATEK
+ if NET_VENDOR_MEDIATEK
+-config NET_AIROHA
+-      tristate "Airoha SoC Gigabit Ethernet support"
+-      depends on NET_DSA || !NET_DSA
+-      select PAGE_POOL
+-      help
+-        This driver supports the gigabit ethernet MACs in the
+-        Airoha SoC family.
+-
+ config NET_MEDIATEK_SOC_WED
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+       def_bool NET_MEDIATEK_SOC != n
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -11,4 +11,3 @@ mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) +
+ endif
+ obj-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_ops.o
+ obj-$(CONFIG_NET_MEDIATEK_STAR_EMAC) += mtk_star_emac.o
+-obj-$(CONFIG_NET_AIROHA) += airoha_eth.o
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -0,0 +1,3359 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++#include <linux/etherdevice.h>
++#include <linux/iopoll.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/of.h>
++#include <linux/of_net.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/tcp.h>
++#include <linux/u64_stats_sync.h>
++#include <net/dsa.h>
++#include <net/page_pool/helpers.h>
++#include <net/pkt_cls.h>
++#include <uapi/linux/ppp_defs.h>
++
++#define AIROHA_MAX_NUM_GDM_PORTS      1
++#define AIROHA_MAX_NUM_QDMA           2
++#define AIROHA_MAX_NUM_RSTS           3
++#define AIROHA_MAX_NUM_XSI_RSTS               5
++#define AIROHA_MAX_MTU                        2000
++#define AIROHA_MAX_PACKET_SIZE                2048
++#define AIROHA_NUM_QOS_CHANNELS               4
++#define AIROHA_NUM_QOS_QUEUES         8
++#define AIROHA_NUM_TX_RING            32
++#define AIROHA_NUM_RX_RING            32
++#define AIROHA_NUM_NETDEV_TX_RINGS    (AIROHA_NUM_TX_RING + \
++                                       AIROHA_NUM_QOS_CHANNELS)
++#define AIROHA_FE_MC_MAX_VLAN_TABLE   64
++#define AIROHA_FE_MC_MAX_VLAN_PORT    16
++#define AIROHA_NUM_TX_IRQ             2
++#define HW_DSCP_NUM                   2048
++#define IRQ_QUEUE_LEN(_n)             ((_n) ? 1024 : 2048)
++#define TX_DSCP_NUM                   1024
++#define RX_DSCP_NUM(_n)                       \
++      ((_n) ==  2 ? 128 :             \
++       (_n) == 11 ? 128 :             \
++       (_n) == 15 ? 128 :             \
++       (_n) ==  0 ? 1024 : 16)
++
++#define PSE_RSV_PAGES                 128
++#define PSE_QUEUE_RSV_PAGES           64
++
++#define QDMA_METER_IDX(_n)            ((_n) & 0xff)
++#define QDMA_METER_GROUP(_n)          (((_n) >> 8) & 0x3)
++
++/* FE */
++#define PSE_BASE                      0x0100
++#define CSR_IFC_BASE                  0x0200
++#define CDM1_BASE                     0x0400
++#define GDM1_BASE                     0x0500
++#define PPE1_BASE                     0x0c00
++
++#define CDM2_BASE                     0x1400
++#define GDM2_BASE                     0x1500
++
++#define GDM3_BASE                     0x1100
++#define GDM4_BASE                     0x2500
++
++#define GDM_BASE(_n)                  \
++      ((_n) == 4 ? GDM4_BASE :        \
++       (_n) == 3 ? GDM3_BASE :        \
++       (_n) == 2 ? GDM2_BASE : GDM1_BASE)
++
++#define REG_FE_DMA_GLO_CFG            0x0000
++#define FE_DMA_GLO_L2_SPACE_MASK      GENMASK(7, 4)
++#define FE_DMA_GLO_PG_SZ_MASK         BIT(3)
++
++#define REG_FE_RST_GLO_CFG            0x0004
++#define FE_RST_GDM4_MBI_ARB_MASK      BIT(3)
++#define FE_RST_GDM3_MBI_ARB_MASK      BIT(2)
++#define FE_RST_CORE_MASK              BIT(0)
++
++#define REG_FE_WAN_MAC_H              0x0030
++#define REG_FE_LAN_MAC_H              0x0040
++
++#define REG_FE_MAC_LMIN(_n)           ((_n) + 0x04)
++#define REG_FE_MAC_LMAX(_n)           ((_n) + 0x08)
++
++#define REG_FE_CDM1_OQ_MAP0           0x0050
++#define REG_FE_CDM1_OQ_MAP1           0x0054
++#define REG_FE_CDM1_OQ_MAP2           0x0058
++#define REG_FE_CDM1_OQ_MAP3           0x005c
++
++#define REG_FE_PCE_CFG                        0x0070
++#define PCE_DPI_EN_MASK                       BIT(2)
++#define PCE_KA_EN_MASK                        BIT(1)
++#define PCE_MC_EN_MASK                        BIT(0)
++
++#define REG_FE_PSE_QUEUE_CFG_WR               0x0080
++#define PSE_CFG_PORT_ID_MASK          GENMASK(27, 24)
++#define PSE_CFG_QUEUE_ID_MASK         GENMASK(20, 16)
++#define PSE_CFG_WR_EN_MASK            BIT(8)
++#define PSE_CFG_OQRSV_SEL_MASK                BIT(0)
++
++#define REG_FE_PSE_QUEUE_CFG_VAL      0x0084
++#define PSE_CFG_OQ_RSV_MASK           GENMASK(13, 0)
++
++#define PSE_FQ_CFG                    0x008c
++#define PSE_FQ_LIMIT_MASK             GENMASK(14, 0)
++
++#define REG_FE_PSE_BUF_SET            0x0090
++#define PSE_SHARE_USED_LTHD_MASK      GENMASK(31, 16)
++#define PSE_ALLRSV_MASK                       GENMASK(14, 0)
++
++#define REG_PSE_SHARE_USED_THD                0x0094
++#define PSE_SHARE_USED_MTHD_MASK      GENMASK(31, 16)
++#define PSE_SHARE_USED_HTHD_MASK      GENMASK(15, 0)
++
++#define REG_GDM_MISC_CFG              0x0148
++#define GDM2_RDM_ACK_WAIT_PREF_MASK   BIT(9)
++#define GDM2_CHN_VLD_MODE_MASK                BIT(5)
++
++#define REG_FE_CSR_IFC_CFG            CSR_IFC_BASE
++#define FE_IFC_EN_MASK                        BIT(0)
++
++#define REG_FE_VIP_PORT_EN            0x01f0
++#define REG_FE_IFC_PORT_EN            0x01f4
++
++#define REG_PSE_IQ_REV1                       (PSE_BASE + 0x08)
++#define PSE_IQ_RES1_P2_MASK           GENMASK(23, 16)
++
++#define REG_PSE_IQ_REV2                       (PSE_BASE + 0x0c)
++#define PSE_IQ_RES2_P5_MASK           GENMASK(15, 8)
++#define PSE_IQ_RES2_P4_MASK           GENMASK(7, 0)
++
++#define REG_FE_VIP_EN(_n)             (0x0300 + ((_n) << 3))
++#define PATN_FCPU_EN_MASK             BIT(7)
++#define PATN_SWP_EN_MASK              BIT(6)
++#define PATN_DP_EN_MASK                       BIT(5)
++#define PATN_SP_EN_MASK                       BIT(4)
++#define PATN_TYPE_MASK                        GENMASK(3, 1)
++#define PATN_EN_MASK                  BIT(0)
++
++#define REG_FE_VIP_PATN(_n)           (0x0304 + ((_n) << 3))
++#define PATN_DP_MASK                  GENMASK(31, 16)
++#define PATN_SP_MASK                  GENMASK(15, 0)
++
++#define REG_CDM1_VLAN_CTRL            CDM1_BASE
++#define CDM1_VLAN_MASK                        GENMASK(31, 16)
++
++#define REG_CDM1_FWD_CFG              (CDM1_BASE + 0x08)
++#define CDM1_VIP_QSEL_MASK            GENMASK(24, 20)
++
++#define REG_CDM1_CRSN_QSEL(_n)                (CDM1_BASE + 0x10 + ((_n) << 2))
++#define CDM1_CRSN_QSEL_REASON_MASK(_n)        \
++      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
++
++#define REG_CDM2_FWD_CFG              (CDM2_BASE + 0x08)
++#define CDM2_OAM_QSEL_MASK            GENMASK(31, 27)
++#define CDM2_VIP_QSEL_MASK            GENMASK(24, 20)
++
++#define REG_CDM2_CRSN_QSEL(_n)                (CDM2_BASE + 0x10 + ((_n) << 2))
++#define CDM2_CRSN_QSEL_REASON_MASK(_n)        \
++      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
++
++#define REG_GDM_FWD_CFG(_n)           GDM_BASE(_n)
++#define GDM_DROP_CRC_ERR              BIT(23)
++#define GDM_IP4_CKSUM                 BIT(22)
++#define GDM_TCP_CKSUM                 BIT(21)
++#define GDM_UDP_CKSUM                 BIT(20)
++#define GDM_UCFQ_MASK                 GENMASK(15, 12)
++#define GDM_BCFQ_MASK                 GENMASK(11, 8)
++#define GDM_MCFQ_MASK                 GENMASK(7, 4)
++#define GDM_OCFQ_MASK                 GENMASK(3, 0)
++
++#define REG_GDM_INGRESS_CFG(_n)               (GDM_BASE(_n) + 0x10)
++#define GDM_INGRESS_FC_EN_MASK                BIT(1)
++#define GDM_STAG_EN_MASK              BIT(0)
++
++#define REG_GDM_LEN_CFG(_n)           (GDM_BASE(_n) + 0x14)
++#define GDM_SHORT_LEN_MASK            GENMASK(13, 0)
++#define GDM_LONG_LEN_MASK             GENMASK(29, 16)
++
++#define REG_FE_CPORT_CFG              (GDM1_BASE + 0x40)
++#define FE_CPORT_PAD                  BIT(26)
++#define FE_CPORT_PORT_XFC_MASK                BIT(25)
++#define FE_CPORT_QUEUE_XFC_MASK               BIT(24)
++
++#define REG_FE_GDM_MIB_CLEAR(_n)      (GDM_BASE(_n) + 0xf0)
++#define FE_GDM_MIB_RX_CLEAR_MASK      BIT(1)
++#define FE_GDM_MIB_TX_CLEAR_MASK      BIT(0)
++
++#define REG_FE_GDM1_MIB_CFG           (GDM1_BASE + 0xf4)
++#define FE_STRICT_RFC2819_MODE_MASK   BIT(31)
++#define FE_GDM1_TX_MIB_SPLIT_EN_MASK  BIT(17)
++#define FE_GDM1_RX_MIB_SPLIT_EN_MASK  BIT(16)
++#define FE_TX_MIB_ID_MASK             GENMASK(15, 8)
++#define FE_RX_MIB_ID_MASK             GENMASK(7, 0)
++
++#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x104)
++#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x10c)
++#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x110)
++#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x114)
++#define REG_FE_GDM_TX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x118)
++#define REG_FE_GDM_TX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x11c)
++#define REG_FE_GDM_TX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x120)
++#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x124)
++#define REG_FE_GDM_TX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x128)
++#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x12c)
++#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x130)
++#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x134)
++#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x138)
++#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x13c)
++#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x140)
++
++#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x148)
++#define REG_FE_GDM_RX_FC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x14c)
++#define REG_FE_GDM_RX_RC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x150)
++#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n)   (GDM_BASE(_n) + 0x154)
++#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n)      (GDM_BASE(_n) + 0x158)
++#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x15c)
++#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x160)
++#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x164)
++#define REG_FE_GDM_RX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x168)
++#define REG_FE_GDM_RX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x16c)
++#define REG_FE_GDM_RX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x170)
++#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n)     (GDM_BASE(_n) + 0x174)
++#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n)                (GDM_BASE(_n) + 0x178)
++#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n)      (GDM_BASE(_n) + 0x17c)
++#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x180)
++#define REG_FE_GDM_RX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x184)
++#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x188)
++#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x18c)
++#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x190)
++#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x194)
++#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x198)
++#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x19c)
++
++#define REG_PPE1_TB_HASH_CFG          (PPE1_BASE + 0x250)
++#define PPE1_SRAM_TABLE_EN_MASK               BIT(0)
++#define PPE1_SRAM_HASH1_EN_MASK               BIT(8)
++#define PPE1_DRAM_TABLE_EN_MASK               BIT(16)
++#define PPE1_DRAM_HASH1_EN_MASK               BIT(24)
++
++#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x280)
++#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x284)
++#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x288)
++#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x28c)
++
++#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x290)
++#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x294)
++#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x298)
++#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x29c)
++#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2b8)
++#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2bc)
++#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2c0)
++#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2c4)
++#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2c8)
++#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2cc)
++#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2e8)
++#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2ec)
++#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2f0)
++#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2f4)
++#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2f8)
++#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2fc)
++
++#define REG_GDM2_CHN_RLS              (GDM2_BASE + 0x20)
++#define MBI_RX_AGE_SEL_MASK           GENMASK(26, 25)
++#define MBI_TX_AGE_SEL_MASK           GENMASK(18, 17)
++
++#define REG_GDM3_FWD_CFG              GDM3_BASE
++#define GDM3_PAD_EN_MASK              BIT(28)
++
++#define REG_GDM4_FWD_CFG              GDM4_BASE
++#define GDM4_PAD_EN_MASK              BIT(28)
++#define GDM4_SPORT_OFFSET0_MASK               GENMASK(11, 8)
++
++#define REG_GDM4_SRC_PORT_SET         (GDM4_BASE + 0x23c)
++#define GDM4_SPORT_OFF2_MASK          GENMASK(19, 16)
++#define GDM4_SPORT_OFF1_MASK          GENMASK(15, 12)
++#define GDM4_SPORT_OFF0_MASK          GENMASK(11, 8)
++
++#define REG_IP_FRAG_FP                        0x2010
++#define IP_ASSEMBLE_PORT_MASK         GENMASK(24, 21)
++#define IP_ASSEMBLE_NBQ_MASK          GENMASK(20, 16)
++#define IP_FRAGMENT_PORT_MASK         GENMASK(8, 5)
++#define IP_FRAGMENT_NBQ_MASK          GENMASK(4, 0)
++
++#define REG_MC_VLAN_EN                        0x2100
++#define MC_VLAN_EN_MASK                       BIT(0)
++
++#define REG_MC_VLAN_CFG                       0x2104
++#define MC_VLAN_CFG_CMD_DONE_MASK     BIT(31)
++#define MC_VLAN_CFG_TABLE_ID_MASK     GENMASK(21, 16)
++#define MC_VLAN_CFG_PORT_ID_MASK      GENMASK(11, 8)
++#define MC_VLAN_CFG_TABLE_SEL_MASK    BIT(4)
++#define MC_VLAN_CFG_RW_MASK           BIT(0)
++
++#define REG_MC_VLAN_DATA              0x2108
++
++#define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
++
++/* QDMA */
++#define REG_QDMA_GLOBAL_CFG                   0x0004
++#define GLOBAL_CFG_RX_2B_OFFSET_MASK          BIT(31)
++#define GLOBAL_CFG_DMA_PREFERENCE_MASK                GENMASK(30, 29)
++#define GLOBAL_CFG_CPU_TXR_RR_MASK            BIT(28)
++#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK                BIT(27)
++#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK     BIT(26)
++#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK   BIT(25)
++#define GLOBAL_CFG_OAM_MODIFY_MASK            BIT(24)
++#define GLOBAL_CFG_RESET_MASK                 BIT(23)
++#define GLOBAL_CFG_RESET_DONE_MASK            BIT(22)
++#define GLOBAL_CFG_MULTICAST_EN_MASK          BIT(21)
++#define GLOBAL_CFG_IRQ1_EN_MASK                       BIT(20)
++#define GLOBAL_CFG_IRQ0_EN_MASK                       BIT(19)
++#define GLOBAL_CFG_LOOPCNT_EN_MASK            BIT(18)
++#define GLOBAL_CFG_RD_BYPASS_WR_MASK          BIT(17)
++#define GLOBAL_CFG_QDMA_LOOPBACK_MASK         BIT(16)
++#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK          GENMASK(13, 8)
++#define GLOBAL_CFG_CHECK_DONE_MASK            BIT(7)
++#define GLOBAL_CFG_TX_WB_DONE_MASK            BIT(6)
++#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK         GENMASK(5, 4)
++#define GLOBAL_CFG_RX_DMA_BUSY_MASK           BIT(3)
++#define GLOBAL_CFG_RX_DMA_EN_MASK             BIT(2)
++#define GLOBAL_CFG_TX_DMA_BUSY_MASK           BIT(1)
++#define GLOBAL_CFG_TX_DMA_EN_MASK             BIT(0)
++
++#define REG_FWD_DSCP_BASE                     0x0010
++#define REG_FWD_BUF_BASE                      0x0014
++
++#define REG_HW_FWD_DSCP_CFG                   0x0018
++#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK         GENMASK(29, 28)
++#define HW_FWD_DSCP_SCATTER_LEN_MASK          GENMASK(17, 16)
++#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK      GENMASK(15, 0)
++
++#define REG_INT_STATUS(_n)            \
++      (((_n) == 4) ? 0x0730 :         \
++       ((_n) == 3) ? 0x0724 :         \
++       ((_n) == 2) ? 0x0720 :         \
++       ((_n) == 1) ? 0x0024 : 0x0020)
++
++#define REG_INT_ENABLE(_n)            \
++      (((_n) == 4) ? 0x0750 :         \
++       ((_n) == 3) ? 0x0744 :         \
++       ((_n) == 2) ? 0x0740 :         \
++       ((_n) == 1) ? 0x002c : 0x0028)
++
++/* QDMA_CSR_INT_ENABLE1 */
++#define RX15_COHERENT_INT_MASK                BIT(31)
++#define RX14_COHERENT_INT_MASK                BIT(30)
++#define RX13_COHERENT_INT_MASK                BIT(29)
++#define RX12_COHERENT_INT_MASK                BIT(28)
++#define RX11_COHERENT_INT_MASK                BIT(27)
++#define RX10_COHERENT_INT_MASK                BIT(26)
++#define RX9_COHERENT_INT_MASK         BIT(25)
++#define RX8_COHERENT_INT_MASK         BIT(24)
++#define RX7_COHERENT_INT_MASK         BIT(23)
++#define RX6_COHERENT_INT_MASK         BIT(22)
++#define RX5_COHERENT_INT_MASK         BIT(21)
++#define RX4_COHERENT_INT_MASK         BIT(20)
++#define RX3_COHERENT_INT_MASK         BIT(19)
++#define RX2_COHERENT_INT_MASK         BIT(18)
++#define RX1_COHERENT_INT_MASK         BIT(17)
++#define RX0_COHERENT_INT_MASK         BIT(16)
++#define TX7_COHERENT_INT_MASK         BIT(15)
++#define TX6_COHERENT_INT_MASK         BIT(14)
++#define TX5_COHERENT_INT_MASK         BIT(13)
++#define TX4_COHERENT_INT_MASK         BIT(12)
++#define TX3_COHERENT_INT_MASK         BIT(11)
++#define TX2_COHERENT_INT_MASK         BIT(10)
++#define TX1_COHERENT_INT_MASK         BIT(9)
++#define TX0_COHERENT_INT_MASK         BIT(8)
++#define CNT_OVER_FLOW_INT_MASK                BIT(7)
++#define IRQ1_FULL_INT_MASK            BIT(5)
++#define IRQ1_INT_MASK                 BIT(4)
++#define HWFWD_DSCP_LOW_INT_MASK               BIT(3)
++#define HWFWD_DSCP_EMPTY_INT_MASK     BIT(2)
++#define IRQ0_FULL_INT_MASK            BIT(1)
++#define IRQ0_INT_MASK                 BIT(0)
++
++#define TX_DONE_INT_MASK(_n)                                  \
++      ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK              \
++            : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
++
++#define INT_TX_MASK                                           \
++      (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK |                   \
++       IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
++
++#define INT_IDX0_MASK                                         \
++      (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK |        \
++       TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK |        \
++       TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK |        \
++       TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK |        \
++       RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK |        \
++       RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK |        \
++       RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK |        \
++       RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK |        \
++       RX15_COHERENT_INT_MASK | INT_TX_MASK)
++
++/* QDMA_CSR_INT_ENABLE2 */
++#define RX15_NO_CPU_DSCP_INT_MASK     BIT(31)
++#define RX14_NO_CPU_DSCP_INT_MASK     BIT(30)
++#define RX13_NO_CPU_DSCP_INT_MASK     BIT(29)
++#define RX12_NO_CPU_DSCP_INT_MASK     BIT(28)
++#define RX11_NO_CPU_DSCP_INT_MASK     BIT(27)
++#define RX10_NO_CPU_DSCP_INT_MASK     BIT(26)
++#define RX9_NO_CPU_DSCP_INT_MASK      BIT(25)
++#define RX8_NO_CPU_DSCP_INT_MASK      BIT(24)
++#define RX7_NO_CPU_DSCP_INT_MASK      BIT(23)
++#define RX6_NO_CPU_DSCP_INT_MASK      BIT(22)
++#define RX5_NO_CPU_DSCP_INT_MASK      BIT(21)
++#define RX4_NO_CPU_DSCP_INT_MASK      BIT(20)
++#define RX3_NO_CPU_DSCP_INT_MASK      BIT(19)
++#define RX2_NO_CPU_DSCP_INT_MASK      BIT(18)
++#define RX1_NO_CPU_DSCP_INT_MASK      BIT(17)
++#define RX0_NO_CPU_DSCP_INT_MASK      BIT(16)
++#define RX15_DONE_INT_MASK            BIT(15)
++#define RX14_DONE_INT_MASK            BIT(14)
++#define RX13_DONE_INT_MASK            BIT(13)
++#define RX12_DONE_INT_MASK            BIT(12)
++#define RX11_DONE_INT_MASK            BIT(11)
++#define RX10_DONE_INT_MASK            BIT(10)
++#define RX9_DONE_INT_MASK             BIT(9)
++#define RX8_DONE_INT_MASK             BIT(8)
++#define RX7_DONE_INT_MASK             BIT(7)
++#define RX6_DONE_INT_MASK             BIT(6)
++#define RX5_DONE_INT_MASK             BIT(5)
++#define RX4_DONE_INT_MASK             BIT(4)
++#define RX3_DONE_INT_MASK             BIT(3)
++#define RX2_DONE_INT_MASK             BIT(2)
++#define RX1_DONE_INT_MASK             BIT(1)
++#define RX0_DONE_INT_MASK             BIT(0)
++
++#define RX_DONE_INT_MASK                                      \
++      (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK |                \
++       RX2_DONE_INT_MASK | RX3_DONE_INT_MASK |                \
++       RX4_DONE_INT_MASK | RX7_DONE_INT_MASK |                \
++       RX8_DONE_INT_MASK | RX9_DONE_INT_MASK |                \
++       RX15_DONE_INT_MASK)
++#define INT_IDX1_MASK                                         \
++      (RX_DONE_INT_MASK |                                     \
++       RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK |  \
++       RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK |  \
++       RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK |  \
++       RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK |  \
++       RX15_NO_CPU_DSCP_INT_MASK)
++
++/* QDMA_CSR_INT_ENABLE5 */
++#define TX31_COHERENT_INT_MASK                BIT(31)
++#define TX30_COHERENT_INT_MASK                BIT(30)
++#define TX29_COHERENT_INT_MASK                BIT(29)
++#define TX28_COHERENT_INT_MASK                BIT(28)
++#define TX27_COHERENT_INT_MASK                BIT(27)
++#define TX26_COHERENT_INT_MASK                BIT(26)
++#define TX25_COHERENT_INT_MASK                BIT(25)
++#define TX24_COHERENT_INT_MASK                BIT(24)
++#define TX23_COHERENT_INT_MASK                BIT(23)
++#define TX22_COHERENT_INT_MASK                BIT(22)
++#define TX21_COHERENT_INT_MASK                BIT(21)
++#define TX20_COHERENT_INT_MASK                BIT(20)
++#define TX19_COHERENT_INT_MASK                BIT(19)
++#define TX18_COHERENT_INT_MASK                BIT(18)
++#define TX17_COHERENT_INT_MASK                BIT(17)
++#define TX16_COHERENT_INT_MASK                BIT(16)
++#define TX15_COHERENT_INT_MASK                BIT(15)
++#define TX14_COHERENT_INT_MASK                BIT(14)
++#define TX13_COHERENT_INT_MASK                BIT(13)
++#define TX12_COHERENT_INT_MASK                BIT(12)
++#define TX11_COHERENT_INT_MASK                BIT(11)
++#define TX10_COHERENT_INT_MASK                BIT(10)
++#define TX9_COHERENT_INT_MASK         BIT(9)
++#define TX8_COHERENT_INT_MASK         BIT(8)
++
++#define INT_IDX4_MASK                                         \
++      (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK |        \
++       TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK |      \
++       TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK |      \
++       TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK |      \
++       TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK |      \
++       TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK |      \
++       TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK |      \
++       TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK |      \
++       TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK |      \
++       TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK |      \
++       TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK |      \
++       TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK)
++
++#define REG_TX_IRQ_BASE(_n)           ((_n) ? 0x0048 : 0x0050)
++
++#define REG_TX_IRQ_CFG(_n)            ((_n) ? 0x004c : 0x0054)
++#define TX_IRQ_THR_MASK                       GENMASK(27, 16)
++#define TX_IRQ_DEPTH_MASK             GENMASK(11, 0)
++
++#define REG_IRQ_CLEAR_LEN(_n)         ((_n) ? 0x0064 : 0x0058)
++#define IRQ_CLEAR_LEN_MASK            GENMASK(7, 0)
++
++#define REG_IRQ_STATUS(_n)            ((_n) ? 0x0068 : 0x005c)
++#define IRQ_ENTRY_LEN_MASK            GENMASK(27, 16)
++#define IRQ_HEAD_IDX_MASK             GENMASK(11, 0)
++
++#define REG_TX_RING_BASE(_n)  \
++      (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5))
++
++#define REG_TX_RING_BLOCKING(_n)      \
++      (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5))
++
++#define TX_RING_IRQ_BLOCKING_MAP_MASK                 BIT(6)
++#define TX_RING_IRQ_BLOCKING_CFG_MASK                 BIT(4)
++#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK          BIT(2)
++#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK    BIT(1)
++#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK    BIT(0)
++
++#define REG_TX_CPU_IDX(_n)    \
++      (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5))
++
++#define TX_RING_CPU_IDX_MASK          GENMASK(15, 0)
++
++#define REG_TX_DMA_IDX(_n)    \
++      (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5))
++
++#define TX_RING_DMA_IDX_MASK          GENMASK(15, 0)
++
++#define IRQ_RING_IDX_MASK             GENMASK(20, 16)
++#define IRQ_DESC_IDX_MASK             GENMASK(15, 0)
++
++#define REG_RX_RING_BASE(_n)  \
++      (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5))
++
++#define REG_RX_RING_SIZE(_n)  \
++      (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5))
++
++#define RX_RING_THR_MASK              GENMASK(31, 16)
++#define RX_RING_SIZE_MASK             GENMASK(15, 0)
++
++#define REG_RX_CPU_IDX(_n)    \
++      (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5))
++
++#define RX_RING_CPU_IDX_MASK          GENMASK(15, 0)
++
++#define REG_RX_DMA_IDX(_n)    \
++      (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5))
++
++#define REG_RX_DELAY_INT_IDX(_n)      \
++      (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5))
++
++#define RX_DELAY_INT_MASK             GENMASK(15, 0)
++
++#define RX_RING_DMA_IDX_MASK          GENMASK(15, 0)
++
++#define REG_INGRESS_TRTCM_CFG         0x0070
++#define INGRESS_TRTCM_EN_MASK         BIT(31)
++#define INGRESS_TRTCM_MODE_MASK               BIT(30)
++#define INGRESS_SLOW_TICK_RATIO_MASK  GENMASK(29, 16)
++#define INGRESS_FAST_TICK_MASK                GENMASK(15, 0)
++
++#define REG_QUEUE_CLOSE_CFG(_n)               (0x00a0 + ((_n) & 0xfc))
++#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m)   BIT((_m) + (((_n) & 0x3) << 3))
++
++#define REG_TXQ_DIS_CFG_BASE(_n)      ((_n) ? 0x20a0 : 0x00a0)
++#define REG_TXQ_DIS_CFG(_n, _m)               (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2)
++
++#define REG_CNTR_CFG(_n)              (0x0400 + ((_n) << 3))
++#define CNTR_EN_MASK                  BIT(31)
++#define CNTR_ALL_CHAN_EN_MASK         BIT(30)
++#define CNTR_ALL_QUEUE_EN_MASK                BIT(29)
++#define CNTR_ALL_DSCP_RING_EN_MASK    BIT(28)
++#define CNTR_SRC_MASK                 GENMASK(27, 24)
++#define CNTR_DSCP_RING_MASK           GENMASK(20, 16)
++#define CNTR_CHAN_MASK                        GENMASK(7, 3)
++#define CNTR_QUEUE_MASK                       GENMASK(2, 0)
++
++#define REG_CNTR_VAL(_n)              (0x0404 + ((_n) << 3))
++
++#define REG_LMGR_INIT_CFG             0x1000
++#define LMGR_INIT_START                       BIT(31)
++#define LMGR_SRAM_MODE_MASK           BIT(30)
++#define HW_FWD_PKTSIZE_OVERHEAD_MASK  GENMASK(27, 20)
++#define HW_FWD_DESC_NUM_MASK          GENMASK(16, 0)
++
++#define REG_FWD_DSCP_LOW_THR          0x1004
++#define FWD_DSCP_LOW_THR_MASK         GENMASK(17, 0)
++
++#define REG_EGRESS_RATE_METER_CFG             0x100c
++#define EGRESS_RATE_METER_EN_MASK             BIT(31)
++#define EGRESS_RATE_METER_EQ_RATE_EN_MASK     BIT(17)
++#define EGRESS_RATE_METER_WINDOW_SZ_MASK      GENMASK(16, 12)
++#define EGRESS_RATE_METER_TIMESLICE_MASK      GENMASK(10, 0)
++
++#define REG_EGRESS_TRTCM_CFG          0x1010
++#define EGRESS_TRTCM_EN_MASK          BIT(31)
++#define EGRESS_TRTCM_MODE_MASK                BIT(30)
++#define EGRESS_SLOW_TICK_RATIO_MASK   GENMASK(29, 16)
++#define EGRESS_FAST_TICK_MASK         GENMASK(15, 0)
++
++#define TRTCM_PARAM_RW_MASK           BIT(31)
++#define TRTCM_PARAM_RW_DONE_MASK      BIT(30)
++#define TRTCM_PARAM_TYPE_MASK         GENMASK(29, 28)
++#define TRTCM_METER_GROUP_MASK                GENMASK(27, 26)
++#define TRTCM_PARAM_INDEX_MASK                GENMASK(23, 17)
++#define TRTCM_PARAM_RATE_TYPE_MASK    BIT(16)
++
++#define REG_TRTCM_CFG_PARAM(_n)               ((_n) + 0x4)
++#define REG_TRTCM_DATA_LOW(_n)                ((_n) + 0x8)
++#define REG_TRTCM_DATA_HIGH(_n)               ((_n) + 0xc)
++
++#define REG_TXWRR_MODE_CFG            0x1020
++#define TWRR_WEIGHT_SCALE_MASK                BIT(31)
++#define TWRR_WEIGHT_BASE_MASK         BIT(3)
++
++#define REG_TXWRR_WEIGHT_CFG          0x1024
++#define TWRR_RW_CMD_MASK              BIT(31)
++#define TWRR_RW_CMD_DONE              BIT(30)
++#define TWRR_CHAN_IDX_MASK            GENMASK(23, 19)
++#define TWRR_QUEUE_IDX_MASK           GENMASK(18, 16)
++#define TWRR_VALUE_MASK                       GENMASK(15, 0)
++
++#define REG_PSE_BUF_USAGE_CFG         0x1028
++#define PSE_BUF_ESTIMATE_EN_MASK      BIT(29)
++
++#define REG_CHAN_QOS_MODE(_n)         (0x1040 + ((_n) << 2))
++#define CHAN_QOS_MODE_MASK(_n)                GENMASK(2 + ((_n) << 2), (_n) << 2)
++
++#define REG_GLB_TRTCM_CFG             0x1080
++#define GLB_TRTCM_EN_MASK             BIT(31)
++#define GLB_TRTCM_MODE_MASK           BIT(30)
++#define GLB_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
++#define GLB_FAST_TICK_MASK            GENMASK(15, 0)
++
++#define REG_TXQ_CNGST_CFG             0x10a0
++#define TXQ_CNGST_DROP_EN             BIT(31)
++#define TXQ_CNGST_DEI_DROP_EN         BIT(30)
++
++#define REG_SLA_TRTCM_CFG             0x1150
++#define SLA_TRTCM_EN_MASK             BIT(31)
++#define SLA_TRTCM_MODE_MASK           BIT(30)
++#define SLA_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
++#define SLA_FAST_TICK_MASK            GENMASK(15, 0)
++
++/* CTRL */
++#define QDMA_DESC_DONE_MASK           BIT(31)
++#define QDMA_DESC_DROP_MASK           BIT(30) /* tx: drop - rx: overflow */
++#define QDMA_DESC_MORE_MASK           BIT(29) /* more SG elements */
++#define QDMA_DESC_DEI_MASK            BIT(25)
++#define QDMA_DESC_NO_DROP_MASK                BIT(24)
++#define QDMA_DESC_LEN_MASK            GENMASK(15, 0)
++/* DATA */
++#define QDMA_DESC_NEXT_ID_MASK                GENMASK(15, 0)
++/* TX MSG0 */
++#define QDMA_ETH_TXMSG_MIC_IDX_MASK   BIT(30)
++#define QDMA_ETH_TXMSG_SP_TAG_MASK    GENMASK(29, 14)
++#define QDMA_ETH_TXMSG_ICO_MASK               BIT(13)
++#define QDMA_ETH_TXMSG_UCO_MASK               BIT(12)
++#define QDMA_ETH_TXMSG_TCO_MASK               BIT(11)
++#define QDMA_ETH_TXMSG_TSO_MASK               BIT(10)
++#define QDMA_ETH_TXMSG_FAST_MASK      BIT(9)
++#define QDMA_ETH_TXMSG_OAM_MASK               BIT(8)
++#define QDMA_ETH_TXMSG_CHAN_MASK      GENMASK(7, 3)
++#define QDMA_ETH_TXMSG_QUEUE_MASK     GENMASK(2, 0)
++/* TX MSG1 */
++#define QDMA_ETH_TXMSG_NO_DROP                BIT(31)
++#define QDMA_ETH_TXMSG_METER_MASK     GENMASK(30, 24) /* 0x7f no meters */
++#define QDMA_ETH_TXMSG_FPORT_MASK     GENMASK(23, 20)
++#define QDMA_ETH_TXMSG_NBOQ_MASK      GENMASK(19, 15)
++#define QDMA_ETH_TXMSG_HWF_MASK               BIT(14)
++#define QDMA_ETH_TXMSG_HOP_MASK               BIT(13)
++#define QDMA_ETH_TXMSG_PTP_MASK               BIT(12)
++#define QDMA_ETH_TXMSG_ACNT_G1_MASK   GENMASK(10, 6)  /* 0x1f do not count */
++#define QDMA_ETH_TXMSG_ACNT_G0_MASK   GENMASK(5, 0)   /* 0x3f do not count */
++
++/* RX MSG1 */
++#define QDMA_ETH_RXMSG_DEI_MASK               BIT(31)
++#define QDMA_ETH_RXMSG_IP6_MASK               BIT(30)
++#define QDMA_ETH_RXMSG_IP4_MASK               BIT(29)
++#define QDMA_ETH_RXMSG_IP4F_MASK      BIT(28)
++#define QDMA_ETH_RXMSG_L4_VALID_MASK  BIT(27)
++#define QDMA_ETH_RXMSG_L4F_MASK               BIT(26)
++#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)
++
++struct airoha_qdma_desc {
++      __le32 rsv;
++      __le32 ctrl;
++      __le32 addr;
++      __le32 data;
++      __le32 msg0;
++      __le32 msg1;
++      __le32 msg2;
++      __le32 msg3;
++};
++
++/* CTRL0 */
++#define QDMA_FWD_DESC_CTX_MASK                BIT(31)
++#define QDMA_FWD_DESC_RING_MASK               GENMASK(30, 28)
++#define QDMA_FWD_DESC_IDX_MASK                GENMASK(27, 16)
++#define QDMA_FWD_DESC_LEN_MASK                GENMASK(15, 0)
++/* CTRL1 */
++#define QDMA_FWD_DESC_FIRST_IDX_MASK  GENMASK(15, 0)
++/* CTRL2 */
++#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK       GENMASK(2, 0)
++
++struct airoha_qdma_fwd_desc {
++      __le32 addr;
++      __le32 ctrl0;
++      __le32 ctrl1;
++      __le32 ctrl2;
++      __le32 msg0;
++      __le32 msg1;
++      __le32 rsv0;
++      __le32 rsv1;
++};
++
++enum {
++      QDMA_INT_REG_IDX0,
++      QDMA_INT_REG_IDX1,
++      QDMA_INT_REG_IDX2,
++      QDMA_INT_REG_IDX3,
++      QDMA_INT_REG_IDX4,
++      QDMA_INT_REG_MAX
++};
++
++enum {
++      XSI_PCIE0_PORT,
++      XSI_PCIE1_PORT,
++      XSI_USB_PORT,
++      XSI_AE_PORT,
++      XSI_ETH_PORT,
++};
++
++enum {
++      XSI_PCIE0_VIP_PORT_MASK = BIT(22),
++      XSI_PCIE1_VIP_PORT_MASK = BIT(23),
++      XSI_USB_VIP_PORT_MASK   = BIT(25),
++      XSI_ETH_VIP_PORT_MASK   = BIT(24),
++};
++
++enum {
++      DEV_STATE_INITIALIZED,
++};
++
++enum {
++      CDM_CRSN_QSEL_Q1 = 1,
++      CDM_CRSN_QSEL_Q5 = 5,
++      CDM_CRSN_QSEL_Q6 = 6,
++      CDM_CRSN_QSEL_Q15 = 15,
++};
++
++enum {
++      CRSN_08 = 0x8,
++      CRSN_21 = 0x15, /* KA */
++      CRSN_22 = 0x16, /* hit bind and force route to CPU */
++      CRSN_24 = 0x18,
++      CRSN_25 = 0x19,
++};
++
++enum {
++      FE_PSE_PORT_CDM1,
++      FE_PSE_PORT_GDM1,
++      FE_PSE_PORT_GDM2,
++      FE_PSE_PORT_GDM3,
++      FE_PSE_PORT_PPE1,
++      FE_PSE_PORT_CDM2,
++      FE_PSE_PORT_CDM3,
++      FE_PSE_PORT_CDM4,
++      FE_PSE_PORT_PPE2,
++      FE_PSE_PORT_GDM4,
++      FE_PSE_PORT_CDM5,
++      FE_PSE_PORT_DROP = 0xf,
++};
++
++enum tx_sched_mode {
++      TC_SCH_WRR8,
++      TC_SCH_SP,
++      TC_SCH_WRR7,
++      TC_SCH_WRR6,
++      TC_SCH_WRR5,
++      TC_SCH_WRR4,
++      TC_SCH_WRR3,
++      TC_SCH_WRR2,
++};
++
++enum trtcm_param_type {
++      TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */
++      TRTCM_TOKEN_RATE_MODE,
++      TRTCM_BUCKETSIZE_SHIFT_MODE,
++      TRTCM_BUCKET_COUNTER_MODE,
++};
++
++enum trtcm_mode_type {
++      TRTCM_COMMIT_MODE,
++      TRTCM_PEAK_MODE,
++};
++
++enum trtcm_param {
++      TRTCM_TICK_SEL = BIT(0),
++      TRTCM_PKT_MODE = BIT(1),
++      TRTCM_METER_MODE = BIT(2),
++};
++
++#define MIN_TOKEN_SIZE                                4096
++#define MAX_TOKEN_SIZE_OFFSET                 17
++#define TRTCM_TOKEN_RATE_MASK                 GENMASK(23, 6)
++#define TRTCM_TOKEN_RATE_FRACTION_MASK                GENMASK(5, 0)
++
++struct airoha_queue_entry {
++      union {
++              void *buf;
++              struct sk_buff *skb;
++      };
++      dma_addr_t dma_addr;
++      u16 dma_len;
++};
++
++struct airoha_queue {
++      struct airoha_qdma *qdma;
++
++      /* protect concurrent queue accesses */
++      spinlock_t lock;
++      struct airoha_queue_entry *entry;
++      struct airoha_qdma_desc *desc;
++      u16 head;
++      u16 tail;
++
++      int queued;
++      int ndesc;
++      int free_thr;
++      int buf_size;
++
++      struct napi_struct napi;
++      struct page_pool *page_pool;
++};
++
++struct airoha_tx_irq_queue {
++      struct airoha_qdma *qdma;
++
++      struct napi_struct napi;
++
++      int size;
++      u32 *q;
++};
++
++struct airoha_hw_stats {
++      /* protect concurrent hw_stats accesses */
++      spinlock_t lock;
++      struct u64_stats_sync syncp;
++
++      /* get_stats64 */
++      u64 rx_ok_pkts;
++      u64 tx_ok_pkts;
++      u64 rx_ok_bytes;
++      u64 tx_ok_bytes;
++      u64 rx_multicast;
++      u64 rx_errors;
++      u64 rx_drops;
++      u64 tx_drops;
++      u64 rx_crc_error;
++      u64 rx_over_errors;
++      /* ethtool stats */
++      u64 tx_broadcast;
++      u64 tx_multicast;
++      u64 tx_len[7];
++      u64 rx_broadcast;
++      u64 rx_fragment;
++      u64 rx_jabber;
++      u64 rx_len[7];
++};
++
++struct airoha_qdma {
++      struct airoha_eth *eth;
++      void __iomem *regs;
++
++      /* protect concurrent irqmask accesses */
++      spinlock_t irq_lock;
++      u32 irqmask[QDMA_INT_REG_MAX];
++      int irq;
++
++      struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ];
++
++      struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
++      struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
++
++      /* descriptor and packet buffers for qdma hw forward */
++      struct {
++              void *desc;
++              void *q;
++      } hfwd;
++};
++
++struct airoha_gdm_port {
++      struct airoha_qdma *qdma;
++      struct net_device *dev;
++      int id;
++
++      struct airoha_hw_stats stats;
++
++      DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
++
++      /* qos stats counters */
++      u64 cpu_tx_packets;
++      u64 fwd_tx_packets;
++};
++
++struct airoha_eth {
++      struct device *dev;
++
++      unsigned long state;
++      void __iomem *fe_regs;
++
++      struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
++      struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
++
++      struct net_device *napi_dev;
++
++      struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA];
++      struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
++};
++
++static u32 airoha_rr(void __iomem *base, u32 offset)
++{
++      return readl(base + offset);
++}
++
++static void airoha_wr(void __iomem *base, u32 offset, u32 val)
++{
++      writel(val, base + offset);
++}
++
++static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val)
++{
++      val |= (airoha_rr(base, offset) & ~mask);
++      airoha_wr(base, offset, val);
++
++      return val;
++}
++
++#define airoha_fe_rr(eth, offset)                             \
++      airoha_rr((eth)->fe_regs, (offset))
++#define airoha_fe_wr(eth, offset, val)                                \
++      airoha_wr((eth)->fe_regs, (offset), (val))
++#define airoha_fe_rmw(eth, offset, mask, val)                 \
++      airoha_rmw((eth)->fe_regs, (offset), (mask), (val))
++#define airoha_fe_set(eth, offset, val)                               \
++      airoha_rmw((eth)->fe_regs, (offset), 0, (val))
++#define airoha_fe_clear(eth, offset, val)                     \
++      airoha_rmw((eth)->fe_regs, (offset), (val), 0)
++
++#define airoha_qdma_rr(qdma, offset)                          \
++      airoha_rr((qdma)->regs, (offset))
++#define airoha_qdma_wr(qdma, offset, val)                     \
++      airoha_wr((qdma)->regs, (offset), (val))
++#define airoha_qdma_rmw(qdma, offset, mask, val)              \
++      airoha_rmw((qdma)->regs, (offset), (mask), (val))
++#define airoha_qdma_set(qdma, offset, val)                    \
++      airoha_rmw((qdma)->regs, (offset), 0, (val))
++#define airoha_qdma_clear(qdma, offset, val)                  \
++      airoha_rmw((qdma)->regs, (offset), (val), 0)
++
++static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index,
++                                  u32 clear, u32 set)
++{
++      unsigned long flags;
++
++      if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask)))
++              return;
++
++      spin_lock_irqsave(&qdma->irq_lock, flags);
++
++      qdma->irqmask[index] &= ~clear;
++      qdma->irqmask[index] |= set;
++      airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]);
++      /* Read irq_enable register in order to guarantee the update above
++       * completes in the spinlock critical section.
++       */
++      airoha_qdma_rr(qdma, REG_INT_ENABLE(index));
++
++      spin_unlock_irqrestore(&qdma->irq_lock, flags);
++}
++
++static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index,
++                                 u32 mask)
++{
++      airoha_qdma_set_irqmask(qdma, index, 0, mask);
++}
++
++static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index,
++                                  u32 mask)
++{
++      airoha_qdma_set_irqmask(qdma, index, mask, 0);
++}
++
++static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port)
++{
++      /* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
++       * GDM{2,3,4} can be used as wan port connected to an external
++       * phy module.
++       */
++      return port->id == 1;
++}
++
++static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
++{
++      struct airoha_eth *eth = port->qdma->eth;
++      u32 val, reg;
++
++      reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
++                                         : REG_FE_WAN_MAC_H;
++      val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
++      airoha_fe_wr(eth, reg, val);
++
++      val = (addr[3] << 16) | (addr[4] << 8) | addr[5];
++      airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
++      airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
++}
++
++static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
++                                      u32 val)
++{
++      airoha_fe_rmw(eth, addr, GDM_OCFQ_MASK,
++                    FIELD_PREP(GDM_OCFQ_MASK, val));
++      airoha_fe_rmw(eth, addr, GDM_MCFQ_MASK,
++                    FIELD_PREP(GDM_MCFQ_MASK, val));
++      airoha_fe_rmw(eth, addr, GDM_BCFQ_MASK,
++                    FIELD_PREP(GDM_BCFQ_MASK, val));
++      airoha_fe_rmw(eth, addr, GDM_UCFQ_MASK,
++                    FIELD_PREP(GDM_UCFQ_MASK, val));
++}
++
++static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable)
++{
++      u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP;
++      u32 vip_port, cfg_addr;
++
++      switch (port) {
++      case XSI_PCIE0_PORT:
++              vip_port = XSI_PCIE0_VIP_PORT_MASK;
++              cfg_addr = REG_GDM_FWD_CFG(3);
++              break;
++      case XSI_PCIE1_PORT:
++              vip_port = XSI_PCIE1_VIP_PORT_MASK;
++              cfg_addr = REG_GDM_FWD_CFG(3);
++              break;
++      case XSI_USB_PORT:
++              vip_port = XSI_USB_VIP_PORT_MASK;
++              cfg_addr = REG_GDM_FWD_CFG(4);
++              break;
++      case XSI_ETH_PORT:
++              vip_port = XSI_ETH_VIP_PORT_MASK;
++              cfg_addr = REG_GDM_FWD_CFG(4);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      if (enable) {
++              airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
++              airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
++      } else {
++              airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, vip_port);
++              airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port);
++      }
++
++      airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val);
++
++      return 0;
++}
++
++static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable)
++{
++      const int port_list[] = {
++              XSI_PCIE0_PORT,
++              XSI_PCIE1_PORT,
++              XSI_USB_PORT,
++              XSI_ETH_PORT
++      };
++      int i, err;
++
++      for (i = 0; i < ARRAY_SIZE(port_list); i++) {
++              err = airoha_set_gdm_port(eth, port_list[i], enable);
++              if (err)
++                      goto error;
++      }
++
++      return 0;
++
++error:
++      for (i--; i >= 0; i--)
++              airoha_set_gdm_port(eth, port_list[i], false);
++
++      return err;
++}
++
++static void airoha_fe_maccr_init(struct airoha_eth *eth)
++{
++      int p;
++
++      for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
++              airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
++                            GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM |
++                            GDM_DROP_CRC_ERR);
++              airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p),
++                                          FE_PSE_PORT_CDM1);
++              airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
++                            GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
++                            FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
++                            FIELD_PREP(GDM_LONG_LEN_MASK, 4004));
++      }
++
++      airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK,
++                    FIELD_PREP(CDM1_VLAN_MASK, 0x8100));
++
++      airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD);
++}
++
++static void airoha_fe_vip_setup(struct airoha_eth *eth)
++{
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(3), ETH_P_PPP_DISC);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(3), PATN_FCPU_EN_MASK | PATN_EN_MASK);
++
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(4), PPP_LCP);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(4),
++                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
++                   PATN_EN_MASK);
++
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(6), PPP_IPCP);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(6),
++                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
++                   PATN_EN_MASK);
++
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(7), PPP_CHAP);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(7),
++                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
++                   PATN_EN_MASK);
++
++      /* BOOTP (0x43) */
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(8), 0x43);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(8),
++                   PATN_FCPU_EN_MASK | PATN_SP_EN_MASK |
++                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
++
++      /* BOOTP (0x44) */
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(9), 0x44);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(9),
++                   PATN_FCPU_EN_MASK | PATN_SP_EN_MASK |
++                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
++
++      /* ISAKMP */
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(10), 0x1f401f4);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(10),
++                   PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK |
++                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
++
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(11), PPP_IPV6CP);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(11),
++                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
++                   PATN_EN_MASK);
++
++      /* DHCPv6 */
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(12), 0x2220223);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(12),
++                   PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK |
++                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
++
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(19), PPP_PAP);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(19),
++                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
++                   PATN_EN_MASK);
++
++      /* ETH->ETH_P_1905 (0x893a) */
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(20), 0x893a);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(20),
++                   PATN_FCPU_EN_MASK | PATN_EN_MASK);
++
++      airoha_fe_wr(eth, REG_FE_VIP_PATN(21), ETH_P_LLDP);
++      airoha_fe_wr(eth, REG_FE_VIP_EN(21),
++                   PATN_FCPU_EN_MASK | PATN_EN_MASK);
++}
++
++static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth,
++                                           u32 port, u32 queue)
++{
++      u32 val;
++
++      airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
++                    PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK,
++                    FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
++                    FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue));
++      val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL);
++
++      return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val);
++}
++
++static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
++                                            u32 port, u32 queue, u32 val)
++{
++      airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_VAL, PSE_CFG_OQ_RSV_MASK,
++                    FIELD_PREP(PSE_CFG_OQ_RSV_MASK, val));
++      airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
++                    PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK |
++                    PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK,
++                    FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
++                    FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue) |
++                    PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK);
++}
++
++static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth)
++{
++      u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET);
++
++      return FIELD_GET(PSE_ALLRSV_MASK, val);
++}
++
++static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
++                                  u32 port, u32 queue, u32 val)
++{
++      u32 orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue);
++      u32 tmp, all_rsv, fq_limit;
++
++      airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val);
++
++      /* modify all rsv */
++      all_rsv = airoha_fe_get_pse_all_rsv(eth);
++      all_rsv += (val - orig_val);
++      airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK,
++                    FIELD_PREP(PSE_ALLRSV_MASK, all_rsv));
++
++      /* modify hthd */
++      tmp = airoha_fe_rr(eth, PSE_FQ_CFG);
++      fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp);
++      tmp = fq_limit - all_rsv - 0x20;
++      airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
++                    PSE_SHARE_USED_HTHD_MASK,
++                    FIELD_PREP(PSE_SHARE_USED_HTHD_MASK, tmp));
++
++      tmp = fq_limit - all_rsv - 0x100;
++      airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
++                    PSE_SHARE_USED_MTHD_MASK,
++                    FIELD_PREP(PSE_SHARE_USED_MTHD_MASK, tmp));
++      tmp = (3 * tmp) >> 2;
++      airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET,
++                    PSE_SHARE_USED_LTHD_MASK,
++                    FIELD_PREP(PSE_SHARE_USED_LTHD_MASK, tmp));
++
++      return 0;
++}
++
++static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
++{
++      const u32 pse_port_num_queues[] = {
++              [FE_PSE_PORT_CDM1] = 6,
++              [FE_PSE_PORT_GDM1] = 6,
++              [FE_PSE_PORT_GDM2] = 32,
++              [FE_PSE_PORT_GDM3] = 6,
++              [FE_PSE_PORT_PPE1] = 4,
++              [FE_PSE_PORT_CDM2] = 6,
++              [FE_PSE_PORT_CDM3] = 8,
++              [FE_PSE_PORT_CDM4] = 10,
++              [FE_PSE_PORT_PPE2] = 4,
++              [FE_PSE_PORT_GDM4] = 2,
++              [FE_PSE_PORT_CDM5] = 2,
++      };
++      u32 all_rsv;
++      int q;
++
++      all_rsv = airoha_fe_get_pse_all_rsv(eth);
++      /* hw misses PPE2 oq rsv */
++      all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2];
++      airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv);
++
++      /* CMD1 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q,
++                                       PSE_QUEUE_RSV_PAGES);
++      /* GMD1 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q,
++                                       PSE_QUEUE_RSV_PAGES);
++      /* GMD2 */
++      for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0);
++      /* GMD3 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q,
++                                       PSE_QUEUE_RSV_PAGES);
++      /* PPE1 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) {
++              if (q < pse_port_num_queues[FE_PSE_PORT_PPE1])
++                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q,
++                                               PSE_QUEUE_RSV_PAGES);
++              else
++                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 0);
++      }
++      /* CDM2 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM2]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM2, q,
++                                       PSE_QUEUE_RSV_PAGES);
++      /* CDM3 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM3] - 1; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM3, q, 0);
++      /* CDM4 */
++      for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q,
++                                       PSE_QUEUE_RSV_PAGES);
++      /* PPE2 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) {
++              if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2)
++                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q,
++                                               PSE_QUEUE_RSV_PAGES);
++              else
++                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0);
++      }
++      /* GMD4 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q,
++                                       PSE_QUEUE_RSV_PAGES);
++      /* CDM5 */
++      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM5]; q++)
++              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM5, q,
++                                       PSE_QUEUE_RSV_PAGES);
++}
++
++static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth)
++{
++      int i;
++
++      for (i = 0; i < AIROHA_FE_MC_MAX_VLAN_TABLE; i++) {
++              int err, j;
++              u32 val;
++
++              airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0);
++
++              val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) |
++                    MC_VLAN_CFG_TABLE_SEL_MASK | MC_VLAN_CFG_RW_MASK;
++              airoha_fe_wr(eth, REG_MC_VLAN_CFG, val);
++              err = read_poll_timeout(airoha_fe_rr, val,
++                                      val & MC_VLAN_CFG_CMD_DONE_MASK,
++                                      USEC_PER_MSEC, 5 * USEC_PER_MSEC,
++                                      false, eth, REG_MC_VLAN_CFG);
++              if (err)
++                      return err;
++
++              for (j = 0; j < AIROHA_FE_MC_MAX_VLAN_PORT; j++) {
++                      airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0);
++
++                      val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) |
++                            FIELD_PREP(MC_VLAN_CFG_PORT_ID_MASK, j) |
++                            MC_VLAN_CFG_RW_MASK;
++                      airoha_fe_wr(eth, REG_MC_VLAN_CFG, val);
++                      err = read_poll_timeout(airoha_fe_rr, val,
++                                              val & MC_VLAN_CFG_CMD_DONE_MASK,
++                                              USEC_PER_MSEC,
++                                              5 * USEC_PER_MSEC, false, eth,
++                                              REG_MC_VLAN_CFG);
++                      if (err)
++                              return err;
++              }
++      }
++
++      return 0;
++}
++
++static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth)
++{
++      /* CDM1_CRSN_QSEL */
++      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2),
++                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_22),
++                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22),
++                               CDM_CRSN_QSEL_Q1));
++      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2),
++                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_08),
++                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08),
++                               CDM_CRSN_QSEL_Q1));
++      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2),
++                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_21),
++                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21),
++                               CDM_CRSN_QSEL_Q1));
++      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2),
++                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_24),
++                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24),
++                               CDM_CRSN_QSEL_Q6));
++      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2),
++                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_25),
++                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25),
++                               CDM_CRSN_QSEL_Q1));
++      /* CDM2_CRSN_QSEL */
++      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2),
++                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_08),
++                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08),
++                               CDM_CRSN_QSEL_Q1));
++      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2),
++                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_21),
++                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21),
++                               CDM_CRSN_QSEL_Q1));
++      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2),
++                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_22),
++                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22),
++                               CDM_CRSN_QSEL_Q1));
++      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2),
++                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_24),
++                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24),
++                               CDM_CRSN_QSEL_Q6));
++      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2),
++                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_25),
++                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25),
++                               CDM_CRSN_QSEL_Q1));
++}
++
++static int airoha_fe_init(struct airoha_eth *eth)
++{
++      airoha_fe_maccr_init(eth);
++
++      /* PSE IQ reserve */
++      airoha_fe_rmw(eth, REG_PSE_IQ_REV1, PSE_IQ_RES1_P2_MASK,
++                    FIELD_PREP(PSE_IQ_RES1_P2_MASK, 0x10));
++      airoha_fe_rmw(eth, REG_PSE_IQ_REV2,
++                    PSE_IQ_RES2_P5_MASK | PSE_IQ_RES2_P4_MASK,
++                    FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) |
++                    FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34));
++
++      /* enable FE copy engine for MC/KA/DPI */
++      airoha_fe_wr(eth, REG_FE_PCE_CFG,
++                   PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK);
++      /* set vip queue selection to ring 1 */
++      airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK,
++                    FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4));
++      airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK,
++                    FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4));
++      /* set GDM4 source interface offset to 8 */
++      airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET,
++                    GDM4_SPORT_OFF2_MASK |
++                    GDM4_SPORT_OFF1_MASK |
++                    GDM4_SPORT_OFF0_MASK,
++                    FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) |
++                    FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) |
++                    FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8));
++
++      /* set PSE Page as 128B */
++      airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG,
++                    FE_DMA_GLO_L2_SPACE_MASK | FE_DMA_GLO_PG_SZ_MASK,
++                    FIELD_PREP(FE_DMA_GLO_L2_SPACE_MASK, 2) |
++                    FE_DMA_GLO_PG_SZ_MASK);
++      airoha_fe_wr(eth, REG_FE_RST_GLO_CFG,
++                   FE_RST_CORE_MASK | FE_RST_GDM3_MBI_ARB_MASK |
++                   FE_RST_GDM4_MBI_ARB_MASK);
++      usleep_range(1000, 2000);
++
++      /* connect RxRing1 and RxRing15 to PSE Port0 OQ-1
++       * connect other rings to PSE Port0 OQ-0
++       */
++      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP0, BIT(4));
++      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP1, BIT(28));
++      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP2, BIT(4));
++      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP3, BIT(28));
++
++      airoha_fe_vip_setup(eth);
++      airoha_fe_pse_ports_init(eth);
++
++      airoha_fe_set(eth, REG_GDM_MISC_CFG,
++                    GDM2_RDM_ACK_WAIT_PREF_MASK |
++                    GDM2_CHN_VLD_MODE_MASK);
++      airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK,
++                    FIELD_PREP(CDM2_OAM_QSEL_MASK, 15));
++
++      /* init fragment and assemble Force Port */
++      /* NPU Core-3, NPU Bridge Channel-3 */
++      airoha_fe_rmw(eth, REG_IP_FRAG_FP,
++                    IP_FRAGMENT_PORT_MASK | IP_FRAGMENT_NBQ_MASK,
++                    FIELD_PREP(IP_FRAGMENT_PORT_MASK, 6) |
++                    FIELD_PREP(IP_FRAGMENT_NBQ_MASK, 3));
++      /* QDMA LAN, RX Ring-22 */
++      airoha_fe_rmw(eth, REG_IP_FRAG_FP,
++                    IP_ASSEMBLE_PORT_MASK | IP_ASSEMBLE_NBQ_MASK,
++                    FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) |
++                    FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22));
++
++      airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK);
++      airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK);
++
++      airoha_fe_crsn_qsel_init(eth);
++
++      airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK);
++      airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK);
++
++      /* default aging mode for mbi unlock issue */
++      airoha_fe_rmw(eth, REG_GDM2_CHN_RLS,
++                    MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK,
++                    FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) |
++                    FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3));
++
++      /* disable IFC by default */
++      airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK);
++
++      /* enable 1:N vlan action, init vlan table */
++      airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
++
++      return airoha_fe_mc_vlan_clear(eth);
++}
++
++static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
++{
++      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];
++      int nframes = 0;
++
++      while (q->queued < q->ndesc - 1) {
++              struct airoha_queue_entry *e = &q->entry[q->head];
++              struct airoha_qdma_desc *desc = &q->desc[q->head];
++              struct page *page;
++              int offset;
++              u32 val;
++
++              page = page_pool_dev_alloc_frag(q->page_pool, &offset,
++                                              q->buf_size);
++              if (!page)
++                      break;
++
++              q->head = (q->head + 1) % q->ndesc;
++              q->queued++;
++              nframes++;
++
++              e->buf = page_address(page) + offset;
++              e->dma_addr = page_pool_get_dma_addr(page) + offset;
++              e->dma_len = SKB_WITH_OVERHEAD(q->buf_size);
++
++              dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len,
++                                         dir);
++
++              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));
++              val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, q->head);
++              WRITE_ONCE(desc->data, cpu_to_le32(val));
++              WRITE_ONCE(desc->msg0, 0);
++              WRITE_ONCE(desc->msg1, 0);
++              WRITE_ONCE(desc->msg2, 0);
++              WRITE_ONCE(desc->msg3, 0);
++
++              airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid),
++                              RX_RING_CPU_IDX_MASK,
++                              FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
++      }
++
++      return nframes;
++}
++
++static int airoha_qdma_get_gdm_port(struct airoha_eth *eth,
++                                  struct airoha_qdma_desc *desc)
++{
++      u32 port, sport, msg1 = le32_to_cpu(desc->msg1);
++
++      sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
++      switch (sport) {
++      case 0x10 ... 0x13:
++              port = 0;
++              break;
++      case 0x2 ... 0x4:
++              port = sport - 1;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
++}
++
++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];
++      int done = 0;
++
++      while (done < budget) {
++              struct airoha_queue_entry *e = &q->entry[q->tail];
++              struct airoha_qdma_desc *desc = &q->desc[q->tail];
++              dma_addr_t dma_addr = le32_to_cpu(desc->addr);
++              u32 desc_ctrl = le32_to_cpu(desc->ctrl);
++              struct sk_buff *skb;
++              int len, p;
++
++              if (!(desc_ctrl & QDMA_DESC_DONE_MASK))
++                      break;
++
++              if (!dma_addr)
++                      break;
++
++              len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
++              if (!len)
++                      break;
++
++              q->tail = (q->tail + 1) % q->ndesc;
++              q->queued--;
++
++              dma_sync_single_for_cpu(eth->dev, dma_addr,
++                                      SKB_WITH_OVERHEAD(q->buf_size), dir);
++
++              p = airoha_qdma_get_gdm_port(eth, desc);
++              if (p < 0 || !eth->ports[p]) {
++                      page_pool_put_full_page(q->page_pool,
++                                              virt_to_head_page(e->buf),
++                                              true);
++                      continue;
++              }
++
++              skb = napi_build_skb(e->buf, q->buf_size);
++              if (!skb) {
++                      page_pool_put_full_page(q->page_pool,
++                                              virt_to_head_page(e->buf),
++                                              true);
++                      break;
++              }
++
++              skb_reserve(skb, 2);
++              __skb_put(skb, len);
++              skb_mark_for_recycle(skb);
++              skb->dev = eth->ports[p]->dev;
++              skb->protocol = eth_type_trans(skb, skb->dev);
++              skb->ip_summed = CHECKSUM_UNNECESSARY;
++              skb_record_rx_queue(skb, qid);
++              napi_gro_receive(&q->napi, skb);
++
++              done++;
++      }
++      airoha_qdma_fill_rx_queue(q);
++
++      return done;
++}
++
++static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget)
++{
++      struct airoha_queue *q = container_of(napi, struct airoha_queue, napi);
++      int cur, done = 0;
++
++      do {
++              cur = airoha_qdma_rx_process(q, budget - done);
++              done += cur;
++      } while (cur && done < budget);
++
++      if (done < budget && napi_complete(napi))
++              airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1,
++                                     RX_DONE_INT_MASK);
++
++      return done;
++}
++
++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,
++              .pool_size = 256,
++              .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV |
++                       PP_FLAG_PAGE_FRAG,
++              .dma_dir = DMA_FROM_DEVICE,
++              .max_len = PAGE_SIZE,
++              .nid = NUMA_NO_NODE,
++              .dev = qdma->eth->dev,
++              .napi = &q->napi,
++      };
++      struct airoha_eth *eth = qdma->eth;
++      int qid = q - &qdma->q_rx[0], thr;
++      dma_addr_t dma_addr;
++
++      q->buf_size = PAGE_SIZE / 2;
++      q->ndesc = ndesc;
++      q->qdma = qdma;
++
++      q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
++                              GFP_KERNEL);
++      if (!q->entry)
++              return -ENOMEM;
++
++      q->page_pool = page_pool_create(&pp_params);
++      if (IS_ERR(q->page_pool)) {
++              int err = PTR_ERR(q->page_pool);
++
++              q->page_pool = NULL;
++              return err;
++      }
++
++      q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
++                                    &dma_addr, GFP_KERNEL);
++      if (!q->desc)
++              return -ENOMEM;
++
++      netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
++
++      airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr);
++      airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid),
++                      RX_RING_SIZE_MASK,
++                      FIELD_PREP(RX_RING_SIZE_MASK, ndesc));
++
++      thr = clamp(ndesc >> 3, 1, 32);
++      airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK,
++                      FIELD_PREP(RX_RING_THR_MASK, thr));
++      airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
++                      FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
++
++      airoha_qdma_fill_rx_queue(q);
++
++      return 0;
++}
++
++static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q)
++{
++      struct airoha_eth *eth = q->qdma->eth;
++
++      while (q->queued) {
++              struct airoha_queue_entry *e = &q->entry[q->tail];
++              struct page *page = virt_to_head_page(e->buf);
++
++              dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len,
++                                      page_pool_get_dma_dir(q->page_pool));
++              page_pool_put_full_page(q->page_pool, page, false);
++              q->tail = (q->tail + 1) % q->ndesc;
++              q->queued--;
++      }
++}
++
++static int airoha_qdma_init_rx(struct airoha_qdma *qdma)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++              int err;
++
++              if (!(RX_DONE_INT_MASK & BIT(i))) {
++                      /* rx-queue not binded to irq */
++                      continue;
++              }
++
++              err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma,
++                                              RX_DSCP_NUM(i));
++              if (err)
++                      return err;
++      }
++
++      return 0;
++}
++
++static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
++{
++      struct airoha_tx_irq_queue *irq_q;
++      int id, done = 0, irq_queued;
++      struct airoha_qdma *qdma;
++      struct airoha_eth *eth;
++      u32 status, head;
++
++      irq_q = container_of(napi, struct airoha_tx_irq_queue, napi);
++      qdma = irq_q->qdma;
++      id = irq_q - &qdma->q_tx_irq[0];
++      eth = qdma->eth;
++
++      status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(id));
++      head = FIELD_GET(IRQ_HEAD_IDX_MASK, status);
++      head = head % irq_q->size;
++      irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status);
++
++      while (irq_queued > 0 && done < budget) {
++              u32 qid, val = irq_q->q[head];
++              struct airoha_qdma_desc *desc;
++              struct airoha_queue_entry *e;
++              struct airoha_queue *q;
++              u32 index, desc_ctrl;
++              struct sk_buff *skb;
++
++              if (val == 0xff)
++                      break;
++
++              irq_q->q[head] = 0xff; /* mark as done */
++              head = (head + 1) % irq_q->size;
++              irq_queued--;
++              done++;
++
++              qid = FIELD_GET(IRQ_RING_IDX_MASK, val);
++              if (qid >= ARRAY_SIZE(qdma->q_tx))
++                      continue;
++
++              q = &qdma->q_tx[qid];
++              if (!q->ndesc)
++                      continue;
++
++              index = FIELD_GET(IRQ_DESC_IDX_MASK, val);
++              if (index >= q->ndesc)
++                      continue;
++
++              spin_lock_bh(&q->lock);
++
++              if (!q->queued)
++                      goto unlock;
++
++              desc = &q->desc[index];
++              desc_ctrl = le32_to_cpu(desc->ctrl);
++
++              if (!(desc_ctrl & QDMA_DESC_DONE_MASK) &&
++                  !(desc_ctrl & QDMA_DESC_DROP_MASK))
++                      goto unlock;
++
++              e = &q->entry[index];
++              skb = e->skb;
++
++              dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
++                               DMA_TO_DEVICE);
++              memset(e, 0, sizeof(*e));
++              WRITE_ONCE(desc->msg0, 0);
++              WRITE_ONCE(desc->msg1, 0);
++              q->queued--;
++
++              /* completion ring can report out-of-order indexes if hw QoS
++               * is enabled and packets with different priority are queued
++               * to same DMA ring. Take into account possible out-of-order
++               * reports incrementing DMA ring tail pointer
++               */
++              while (q->tail != q->head && !q->entry[q->tail].dma_addr)
++                      q->tail = (q->tail + 1) % q->ndesc;
++
++              if (skb) {
++                      u16 queue = skb_get_queue_mapping(skb);
++                      struct netdev_queue *txq;
++
++                      txq = netdev_get_tx_queue(skb->dev, queue);
++                      netdev_tx_completed_queue(txq, 1, skb->len);
++                      if (netif_tx_queue_stopped(txq) &&
++                          q->ndesc - q->queued >= q->free_thr)
++                              netif_tx_wake_queue(txq);
++
++                      dev_kfree_skb_any(skb);
++              }
++unlock:
++              spin_unlock_bh(&q->lock);
++      }
++
++      if (done) {
++              int i, len = done >> 7;
++
++              for (i = 0; i < len; i++)
++                      airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id),
++                                      IRQ_CLEAR_LEN_MASK, 0x80);
++              airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id),
++                              IRQ_CLEAR_LEN_MASK, (done & 0x7f));
++      }
++
++      if (done < budget && napi_complete(napi))
++              airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0,
++                                     TX_DONE_INT_MASK(id));
++
++      return done;
++}
++
++static int airoha_qdma_init_tx_queue(struct airoha_queue *q,
++                                   struct airoha_qdma *qdma, int size)
++{
++      struct airoha_eth *eth = qdma->eth;
++      int i, qid = q - &qdma->q_tx[0];
++      dma_addr_t dma_addr;
++
++      spin_lock_init(&q->lock);
++      q->ndesc = size;
++      q->qdma = qdma;
++      q->free_thr = 1 + MAX_SKB_FRAGS;
++
++      q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
++                              GFP_KERNEL);
++      if (!q->entry)
++              return -ENOMEM;
++
++      q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
++                                    &dma_addr, GFP_KERNEL);
++      if (!q->desc)
++              return -ENOMEM;
++
++      for (i = 0; i < q->ndesc; i++) {
++              u32 val;
++
++              val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1);
++              WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val));
++      }
++
++      /* xmit ring drop default setting */
++      airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid),
++                      TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK);
++
++      airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr);
++      airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK,
++                      FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
++      airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK,
++                      FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head));
++
++      return 0;
++}
++
++static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q,
++                                 struct airoha_qdma *qdma, int size)
++{
++      int id = irq_q - &qdma->q_tx_irq[0];
++      struct airoha_eth *eth = qdma->eth;
++      dma_addr_t dma_addr;
++
++      netif_napi_add_tx(eth->napi_dev, &irq_q->napi,
++                        airoha_qdma_tx_napi_poll);
++      irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32),
++                                     &dma_addr, GFP_KERNEL);
++      if (!irq_q->q)
++              return -ENOMEM;
++
++      memset(irq_q->q, 0xff, size * sizeof(u32));
++      irq_q->size = size;
++      irq_q->qdma = qdma;
++
++      airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr);
++      airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK,
++                      FIELD_PREP(TX_IRQ_DEPTH_MASK, size));
++      airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK,
++                      FIELD_PREP(TX_IRQ_THR_MASK, 1));
++
++      return 0;
++}
++
++static int airoha_qdma_init_tx(struct airoha_qdma *qdma)
++{
++      int i, err;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) {
++              err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma,
++                                            IRQ_QUEUE_LEN(i));
++              if (err)
++                      return err;
++      }
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
++              err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma,
++                                              TX_DSCP_NUM);
++              if (err)
++                      return err;
++      }
++
++      return 0;
++}
++
++static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
++{
++      struct airoha_eth *eth = q->qdma->eth;
++
++      spin_lock_bh(&q->lock);
++      while (q->queued) {
++              struct airoha_queue_entry *e = &q->entry[q->tail];
++
++              dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
++                               DMA_TO_DEVICE);
++              dev_kfree_skb_any(e->skb);
++              e->skb = NULL;
++
++              q->tail = (q->tail + 1) % q->ndesc;
++              q->queued--;
++      }
++      spin_unlock_bh(&q->lock);
++}
++
++static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
++{
++      struct airoha_eth *eth = qdma->eth;
++      dma_addr_t dma_addr;
++      u32 status;
++      int size;
++
++      size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc);
++      qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr,
++                                            GFP_KERNEL);
++      if (!qdma->hfwd.desc)
++              return -ENOMEM;
++
++      airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr);
++
++      size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM;
++      qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr,
++                                         GFP_KERNEL);
++      if (!qdma->hfwd.q)
++              return -ENOMEM;
++
++      airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr);
++
++      airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG,
++                      HW_FWD_DSCP_PAYLOAD_SIZE_MASK,
++                      FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0));
++      airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK,
++                      FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128));
++      airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG,
++                      LMGR_INIT_START | LMGR_SRAM_MODE_MASK |
++                      HW_FWD_DESC_NUM_MASK,
++                      FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) |
++                      LMGR_INIT_START);
++
++      return read_poll_timeout(airoha_qdma_rr, status,
++                               !(status & LMGR_INIT_START), USEC_PER_MSEC,
++                               30 * USEC_PER_MSEC, true, qdma,
++                               REG_LMGR_INIT_CFG);
++}
++
++static void airoha_qdma_init_qos(struct airoha_qdma *qdma)
++{
++      airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK);
++      airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK);
++
++      airoha_qdma_clear(qdma, REG_PSE_BUF_USAGE_CFG,
++                        PSE_BUF_ESTIMATE_EN_MASK);
++
++      airoha_qdma_set(qdma, REG_EGRESS_RATE_METER_CFG,
++                      EGRESS_RATE_METER_EN_MASK |
++                      EGRESS_RATE_METER_EQ_RATE_EN_MASK);
++      /* 2047us x 31 = 63.457ms */
++      airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG,
++                      EGRESS_RATE_METER_WINDOW_SZ_MASK,
++                      FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f));
++      airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG,
++                      EGRESS_RATE_METER_TIMESLICE_MASK,
++                      FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff));
++
++      /* ratelimit init */
++      airoha_qdma_set(qdma, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK);
++      /* fast-tick 25us */
++      airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK,
++                      FIELD_PREP(GLB_FAST_TICK_MASK, 25));
++      airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK,
++                      FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40));
++
++      airoha_qdma_set(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK);
++      airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK,
++                      FIELD_PREP(EGRESS_FAST_TICK_MASK, 25));
++      airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG,
++                      EGRESS_SLOW_TICK_RATIO_MASK,
++                      FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40));
++
++      airoha_qdma_set(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK);
++      airoha_qdma_clear(qdma, REG_INGRESS_TRTCM_CFG,
++                        INGRESS_TRTCM_MODE_MASK);
++      airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK,
++                      FIELD_PREP(INGRESS_FAST_TICK_MASK, 125));
++      airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG,
++                      INGRESS_SLOW_TICK_RATIO_MASK,
++                      FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8));
++
++      airoha_qdma_set(qdma, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK);
++      airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK,
++                      FIELD_PREP(SLA_FAST_TICK_MASK, 25));
++      airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK,
++                      FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40));
++}
++
++static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma)
++{
++      int i;
++
++      for (i = 0; i < AIROHA_NUM_QOS_CHANNELS; i++) {
++              /* Tx-cpu transferred count */
++              airoha_qdma_wr(qdma, REG_CNTR_VAL(i << 1), 0);
++              airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1),
++                             CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK |
++                             CNTR_ALL_DSCP_RING_EN_MASK |
++                             FIELD_PREP(CNTR_CHAN_MASK, i));
++              /* Tx-fwd transferred count */
++              airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0);
++              airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1),
++                             CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK |
++                             CNTR_ALL_DSCP_RING_EN_MASK |
++                             FIELD_PREP(CNTR_SRC_MASK, 1) |
++                             FIELD_PREP(CNTR_CHAN_MASK, i));
++      }
++}
++
++static int airoha_qdma_hw_init(struct airoha_qdma *qdma)
++{
++      int i;
++
++      /* clear pending irqs */
++      for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++)
++              airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff);
++
++      /* setup irqs */
++      airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK);
++      airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK);
++      airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK);
++
++      /* setup irq binding */
++      for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
++              if (!qdma->q_tx[i].ndesc)
++                      continue;
++
++              if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i))
++                      airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(i),
++                                      TX_RING_IRQ_BLOCKING_CFG_MASK);
++              else
++                      airoha_qdma_clear(qdma, REG_TX_RING_BLOCKING(i),
++                                        TX_RING_IRQ_BLOCKING_CFG_MASK);
++      }
++
++      airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG,
++                     GLOBAL_CFG_RX_2B_OFFSET_MASK |
++                     FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) |
++                     GLOBAL_CFG_CPU_TXR_RR_MASK |
++                     GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK |
++                     GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK |
++                     GLOBAL_CFG_MULTICAST_EN_MASK |
++                     GLOBAL_CFG_IRQ0_EN_MASK | GLOBAL_CFG_IRQ1_EN_MASK |
++                     GLOBAL_CFG_TX_WB_DONE_MASK |
++                     FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2));
++
++      airoha_qdma_init_qos(qdma);
++
++      /* disable qdma rx delay interrupt */
++      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++              if (!qdma->q_rx[i].ndesc)
++                      continue;
++
++              airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i),
++                                RX_DELAY_INT_MASK);
++      }
++
++      airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG,
++                      TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN);
++      airoha_qdma_init_qos_stats(qdma);
++
++      return 0;
++}
++
++static irqreturn_t airoha_irq_handler(int irq, void *dev_instance)
++{
++      struct airoha_qdma *qdma = dev_instance;
++      u32 intr[ARRAY_SIZE(qdma->irqmask)];
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) {
++              intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i));
++              intr[i] &= qdma->irqmask[i];
++              airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]);
++      }
++
++      if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state))
++              return IRQ_NONE;
++
++      if (intr[1] & RX_DONE_INT_MASK) {
++              airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1,
++                                      RX_DONE_INT_MASK);
++
++              for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++                      if (!qdma->q_rx[i].ndesc)
++                              continue;
++
++                      if (intr[1] & BIT(i))
++                              napi_schedule(&qdma->q_rx[i].napi);
++              }
++      }
++
++      if (intr[0] & INT_TX_MASK) {
++              for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) {
++                      if (!(intr[0] & TX_DONE_INT_MASK(i)))
++                              continue;
++
++                      airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0,
++                                              TX_DONE_INT_MASK(i));
++                      napi_schedule(&qdma->q_tx_irq[i].napi);
++              }
++      }
++
++      return IRQ_HANDLED;
++}
++
++static int airoha_qdma_init(struct platform_device *pdev,
++                          struct airoha_eth *eth,
++                          struct airoha_qdma *qdma)
++{
++      int err, id = qdma - &eth->qdma[0];
++      const char *res;
++
++      spin_lock_init(&qdma->irq_lock);
++      qdma->eth = eth;
++
++      res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id);
++      if (!res)
++              return -ENOMEM;
++
++      qdma->regs = devm_platform_ioremap_resource_byname(pdev, res);
++      if (IS_ERR(qdma->regs))
++              return dev_err_probe(eth->dev, PTR_ERR(qdma->regs),
++                                   "failed to iomap qdma%d regs\n", id);
++
++      qdma->irq = platform_get_irq(pdev, 4 * id);
++      if (qdma->irq < 0)
++              return qdma->irq;
++
++      err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler,
++                             IRQF_SHARED, KBUILD_MODNAME, qdma);
++      if (err)
++              return err;
++
++      err = airoha_qdma_init_rx(qdma);
++      if (err)
++              return err;
++
++      err = airoha_qdma_init_tx(qdma);
++      if (err)
++              return err;
++
++      err = airoha_qdma_init_hfwd_queues(qdma);
++      if (err)
++              return err;
++
++      return airoha_qdma_hw_init(qdma);
++}
++
++static int airoha_hw_init(struct platform_device *pdev,
++                        struct airoha_eth *eth)
++{
++      int err, i;
++
++      /* disable xsi */
++      err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts),
++                                      eth->xsi_rsts);
++      if (err)
++              return err;
++
++      err = reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts);
++      if (err)
++              return err;
++
++      msleep(20);
++      err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts);
++      if (err)
++              return err;
++
++      msleep(20);
++      err = airoha_fe_init(eth);
++      if (err)
++              return err;
++
++      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
++              err = airoha_qdma_init(pdev, eth, &eth->qdma[i]);
++              if (err)
++                      return err;
++      }
++
++      set_bit(DEV_STATE_INITIALIZED, &eth->state);
++
++      return 0;
++}
++
++static void airoha_hw_cleanup(struct airoha_qdma *qdma)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++              if (!qdma->q_rx[i].ndesc)
++                      continue;
++
++              netif_napi_del(&qdma->q_rx[i].napi);
++              airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]);
++              if (qdma->q_rx[i].page_pool)
++                      page_pool_destroy(qdma->q_rx[i].page_pool);
++      }
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
++              netif_napi_del(&qdma->q_tx_irq[i].napi);
++
++      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]);
++      }
++}
++
++static void airoha_qdma_start_napi(struct airoha_qdma *qdma)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
++              napi_enable(&qdma->q_tx_irq[i].napi);
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++              if (!qdma->q_rx[i].ndesc)
++                      continue;
++
++              napi_enable(&qdma->q_rx[i].napi);
++      }
++}
++
++static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
++              napi_disable(&qdma->q_tx_irq[i].napi);
++
++      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
++              if (!qdma->q_rx[i].ndesc)
++                      continue;
++
++              napi_disable(&qdma->q_rx[i].napi);
++      }
++}
++
++static void airoha_update_hw_stats(struct airoha_gdm_port *port)
++{
++      struct airoha_eth *eth = port->qdma->eth;
++      u32 val, i = 0;
++
++      spin_lock(&port->stats.lock);
++      u64_stats_update_begin(&port->stats.syncp);
++
++      /* TX */
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
++      port->stats.tx_ok_pkts += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
++      port->stats.tx_ok_pkts += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
++      port->stats.tx_ok_bytes += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
++      port->stats.tx_ok_bytes += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
++      port->stats.tx_drops += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
++      port->stats.tx_broadcast += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
++      port->stats.tx_multicast += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
++      port->stats.tx_len[i] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
++      port->stats.tx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
++      port->stats.tx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
++      port->stats.tx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
++      port->stats.tx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
++      port->stats.tx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
++      port->stats.tx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
++      port->stats.tx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
++      port->stats.tx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
++      port->stats.tx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
++      port->stats.tx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
++      port->stats.tx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
++      port->stats.tx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
++      port->stats.tx_len[i++] += val;
++
++      /* RX */
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
++      port->stats.rx_ok_pkts += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
++      port->stats.rx_ok_pkts += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
++      port->stats.rx_ok_bytes += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
++      port->stats.rx_ok_bytes += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
++      port->stats.rx_drops += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
++      port->stats.rx_broadcast += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
++      port->stats.rx_multicast += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
++      port->stats.rx_errors += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
++      port->stats.rx_crc_error += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
++      port->stats.rx_over_errors += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
++      port->stats.rx_fragment += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
++      port->stats.rx_jabber += val;
++
++      i = 0;
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
++      port->stats.rx_len[i] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
++      port->stats.rx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
++      port->stats.rx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
++      port->stats.rx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
++      port->stats.rx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
++      port->stats.rx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
++      port->stats.rx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
++      port->stats.rx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
++      port->stats.rx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
++      port->stats.rx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
++      port->stats.rx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
++      port->stats.rx_len[i] += ((u64)val << 32);
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
++      port->stats.rx_len[i++] += val;
++
++      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
++      port->stats.rx_len[i++] += val;
++
++      /* reset mib counters */
++      airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
++                    FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
++
++      u64_stats_update_end(&port->stats.syncp);
++      spin_unlock(&port->stats.lock);
++}
++
++static int airoha_dev_open(struct net_device *dev)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_qdma *qdma = port->qdma;
++      int err;
++
++      netif_tx_start_all_queues(dev);
++      err = airoha_set_gdm_ports(qdma->eth, true);
++      if (err)
++              return err;
++
++      if (netdev_uses_dsa(dev))
++              airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
++                            GDM_STAG_EN_MASK);
++      else
++              airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
++                              GDM_STAG_EN_MASK);
++
++      airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
++                      GLOBAL_CFG_TX_DMA_EN_MASK |
++                      GLOBAL_CFG_RX_DMA_EN_MASK);
++
++      return 0;
++}
++
++static int airoha_dev_stop(struct net_device *dev)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_qdma *qdma = port->qdma;
++      int i, err;
++
++      netif_tx_disable(dev);
++      err = airoha_set_gdm_ports(qdma->eth, false);
++      if (err)
++              return err;
++
++      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]);
++              netdev_tx_reset_subqueue(dev, i);
++      }
++
++      return 0;
++}
++
++static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      int err;
++
++      err = eth_mac_addr(dev, p);
++      if (err)
++              return err;
++
++      airoha_set_macaddr(port, dev->dev_addr);
++
++      return 0;
++}
++
++static int airoha_dev_init(struct net_device *dev)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++
++      airoha_set_macaddr(port, dev->dev_addr);
++
++      return 0;
++}
++
++static void airoha_dev_get_stats64(struct net_device *dev,
++                                 struct rtnl_link_stats64 *storage)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      unsigned int start;
++
++      airoha_update_hw_stats(port);
++      do {
++              start = u64_stats_fetch_begin(&port->stats.syncp);
++              storage->rx_packets = port->stats.rx_ok_pkts;
++              storage->tx_packets = port->stats.tx_ok_pkts;
++              storage->rx_bytes = port->stats.rx_ok_bytes;
++              storage->tx_bytes = port->stats.tx_ok_bytes;
++              storage->multicast = port->stats.rx_multicast;
++              storage->rx_errors = port->stats.rx_errors;
++              storage->rx_dropped = port->stats.rx_drops;
++              storage->tx_dropped = port->stats.tx_drops;
++              storage->rx_crc_errors = port->stats.rx_crc_error;
++              storage->rx_over_errors = port->stats.rx_over_errors;
++      } while (u64_stats_fetch_retry(&port->stats.syncp, start));
++}
++
++static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
++                                 struct net_device *sb_dev)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      int queue, channel;
++
++      /* For dsa device select QoS channel according to the dsa user port
++       * index, rely on port id otherwise. Select QoS queue based on the
++       * skb priority.
++       */
++      channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
++      channel = channel % AIROHA_NUM_QOS_CHANNELS;
++      queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
++      queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
++
++      return queue < dev->num_tx_queues ? queue : 0;
++}
++
++static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
++                                 struct net_device *dev)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags;
++      u32 msg0, msg1, len = skb_headlen(skb);
++      struct airoha_qdma *qdma = port->qdma;
++      struct netdev_queue *txq;
++      struct airoha_queue *q;
++      void *data = skb->data;
++      int i, qid;
++      u16 index;
++      u8 fport;
++
++      qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx);
++      msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
++                        qid / AIROHA_NUM_QOS_QUEUES) |
++             FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK,
++                        qid % AIROHA_NUM_QOS_QUEUES);
++      if (skb->ip_summed == CHECKSUM_PARTIAL)
++              msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) |
++                      FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) |
++                      FIELD_PREP(QDMA_ETH_TXMSG_ICO_MASK, 1);
++
++      /* TSO: fill MSS info in tcp checksum field */
++      if (skb_is_gso(skb)) {
++              if (skb_cow_head(skb, 0))
++                      goto error;
++
++              if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 |
++                                               SKB_GSO_TCPV6)) {
++                      __be16 csum = cpu_to_be16(skb_shinfo(skb)->gso_size);
++
++                      tcp_hdr(skb)->check = (__force __sum16)csum;
++                      msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1);
++              }
++      }
++
++      fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
++      msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
++             FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
++
++      q = &qdma->q_tx[qid];
++      if (WARN_ON_ONCE(!q->ndesc))
++              goto error;
++
++      spin_lock_bh(&q->lock);
++
++      txq = netdev_get_tx_queue(dev, qid);
++      if (q->queued + nr_frags > q->ndesc) {
++              /* not enough space in the queue */
++              netif_tx_stop_queue(txq);
++              spin_unlock_bh(&q->lock);
++              return NETDEV_TX_BUSY;
++      }
++
++      index = q->head;
++      for (i = 0; i < nr_frags; i++) {
++              struct airoha_qdma_desc *desc = &q->desc[index];
++              struct airoha_queue_entry *e = &q->entry[index];
++              skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
++              dma_addr_t addr;
++              u32 val;
++
++              addr = dma_map_single(dev->dev.parent, data, len,
++                                    DMA_TO_DEVICE);
++              if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
++                      goto error_unmap;
++
++              index = (index + 1) % q->ndesc;
++
++              val = FIELD_PREP(QDMA_DESC_LEN_MASK, len);
++              if (i < nr_frags - 1)
++                      val |= FIELD_PREP(QDMA_DESC_MORE_MASK, 1);
++              WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
++              WRITE_ONCE(desc->addr, cpu_to_le32(addr));
++              val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index);
++              WRITE_ONCE(desc->data, cpu_to_le32(val));
++              WRITE_ONCE(desc->msg0, cpu_to_le32(msg0));
++              WRITE_ONCE(desc->msg1, cpu_to_le32(msg1));
++              WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff));
++
++              e->skb = i ? NULL : skb;
++              e->dma_addr = addr;
++              e->dma_len = len;
++
++              data = skb_frag_address(frag);
++              len = skb_frag_size(frag);
++      }
++
++      q->head = index;
++      q->queued += i;
++
++      skb_tx_timestamp(skb);
++      netdev_tx_sent_queue(txq, skb->len);
++
++      if (netif_xmit_stopped(txq) || !netdev_xmit_more())
++              airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid),
++                              TX_RING_CPU_IDX_MASK,
++                              FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
++
++      if (q->ndesc - q->queued < q->free_thr)
++              netif_tx_stop_queue(txq);
++
++      spin_unlock_bh(&q->lock);
++
++      return NETDEV_TX_OK;
++
++error_unmap:
++      for (i--; i >= 0; i--) {
++              index = (q->head + i) % q->ndesc;
++              dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr,
++                               q->entry[index].dma_len, DMA_TO_DEVICE);
++      }
++
++      spin_unlock_bh(&q->lock);
++error:
++      dev_kfree_skb_any(skb);
++      dev->stats.tx_dropped++;
++
++      return NETDEV_TX_OK;
++}
++
++static void airoha_ethtool_get_drvinfo(struct net_device *dev,
++                                     struct ethtool_drvinfo *info)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_eth *eth = port->qdma->eth;
++
++      strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
++      strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
++}
++
++static void airoha_ethtool_get_mac_stats(struct net_device *dev,
++                                       struct ethtool_eth_mac_stats *stats)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      unsigned int start;
++
++      airoha_update_hw_stats(port);
++      do {
++              start = u64_stats_fetch_begin(&port->stats.syncp);
++              stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
++              stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
++              stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
++      } while (u64_stats_fetch_retry(&port->stats.syncp, start));
++}
++
++static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
++      {    0,    64 },
++      {   65,   127 },
++      {  128,   255 },
++      {  256,   511 },
++      {  512,  1023 },
++      { 1024,  1518 },
++      { 1519, 10239 },
++      {},
++};
++
++static void
++airoha_ethtool_get_rmon_stats(struct net_device *dev,
++                            struct ethtool_rmon_stats *stats,
++                            const struct ethtool_rmon_hist_range **ranges)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_hw_stats *hw_stats = &port->stats;
++      unsigned int start;
++
++      BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
++                   ARRAY_SIZE(hw_stats->tx_len) + 1);
++      BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
++                   ARRAY_SIZE(hw_stats->rx_len) + 1);
++
++      *ranges = airoha_ethtool_rmon_ranges;
++      airoha_update_hw_stats(port);
++      do {
++              int i;
++
++              start = u64_stats_fetch_begin(&port->stats.syncp);
++              stats->fragments = hw_stats->rx_fragment;
++              stats->jabbers = hw_stats->rx_jabber;
++              for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
++                   i++) {
++                      stats->hist[i] = hw_stats->rx_len[i];
++                      stats->hist_tx[i] = hw_stats->tx_len[i];
++              }
++      } while (u64_stats_fetch_retry(&port->stats.syncp, start));
++}
++
++static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port,
++                                       int channel, enum tx_sched_mode mode,
++                                       const u16 *weights, u8 n_weights)
++{
++      int i;
++
++      for (i = 0; i < AIROHA_NUM_TX_RING; i++)
++              airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
++                                TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
++
++      for (i = 0; i < n_weights; i++) {
++              u32 status;
++              int err;
++
++              airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG,
++                             TWRR_RW_CMD_MASK |
++                             FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
++                             FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
++                             FIELD_PREP(TWRR_VALUE_MASK, weights[i]));
++              err = read_poll_timeout(airoha_qdma_rr, status,
++                                      status & TWRR_RW_CMD_DONE,
++                                      USEC_PER_MSEC, 10 * USEC_PER_MSEC,
++                                      true, port->qdma,
++                                      REG_TXWRR_WEIGHT_CFG);
++              if (err)
++                      return err;
++      }
++
++      airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
++                      CHAN_QOS_MODE_MASK(channel),
++                      mode << __ffs(CHAN_QOS_MODE_MASK(channel)));
++
++      return 0;
++}
++
++static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port,
++                                       int channel)
++{
++      static const u16 w[AIROHA_NUM_QOS_QUEUES] = {};
++
++      return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w,
++                                           ARRAY_SIZE(w));
++}
++
++static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port,
++                                      int channel,
++                                      struct tc_ets_qopt_offload *opt)
++{
++      struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params;
++      enum tx_sched_mode mode = TC_SCH_SP;
++      u16 w[AIROHA_NUM_QOS_QUEUES] = {};
++      int i, nstrict = 0, nwrr, qidx;
++
++      if (p->bands > AIROHA_NUM_QOS_QUEUES)
++              return -EINVAL;
++
++      for (i = 0; i < p->bands; i++) {
++              if (!p->quanta[i])
++                      nstrict++;
++      }
++
++      /* this configuration is not supported by the hw */
++      if (nstrict == AIROHA_NUM_QOS_QUEUES - 1)
++              return -EINVAL;
++
++      /* EN7581 SoC supports fixed QoS band priority where WRR queues have
++       * lowest priorities with respect to SP ones.
++       * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn
++       */
++      nwrr = p->bands - nstrict;
++      qidx = nstrict && nwrr ? nstrict : 0;
++      for (i = 1; i <= p->bands; i++) {
++              if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx)
++                      return -EINVAL;
++
++              qidx = i == nwrr ? 0 : qidx + 1;
++      }
++
++      for (i = 0; i < nwrr; i++)
++              w[i] = p->weights[nstrict + i];
++
++      if (!nstrict)
++              mode = TC_SCH_WRR8;
++      else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1)
++              mode = nstrict + 1;
++
++      return airoha_qdma_set_chan_tx_sched(port, channel, mode, w,
++                                           ARRAY_SIZE(w));
++}
++
++static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port,
++                                      int channel,
++                                      struct tc_ets_qopt_offload *opt)
++{
++      u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
++                                          REG_CNTR_VAL(channel << 1));
++      u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
++                                          REG_CNTR_VAL((channel << 1) + 1));
++      u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
++                       (fwd_tx_packets - port->fwd_tx_packets);
++      _bstats_update(opt->stats.bstats, 0, tx_packets);
++
++      port->cpu_tx_packets = cpu_tx_packets;
++      port->fwd_tx_packets = fwd_tx_packets;
++
++      return 0;
++}
++
++static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port,
++                                   struct tc_ets_qopt_offload *opt)
++{
++      int channel = TC_H_MAJ(opt->handle) >> 16;
++
++      if (opt->parent == TC_H_ROOT)
++              return -EINVAL;
++
++      switch (opt->command) {
++      case TC_ETS_REPLACE:
++              return airoha_qdma_set_tx_ets_sched(port, channel, opt);
++      case TC_ETS_DESTROY:
++              /* PRIO is default qdisc scheduler */
++              return airoha_qdma_set_tx_prio_sched(port, channel);
++      case TC_ETS_STATS:
++              return airoha_qdma_get_tx_ets_stats(port, channel, opt);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel,
++                                     u32 addr, enum trtcm_param_type param,
++                                     enum trtcm_mode_type mode,
++                                     u32 *val_low, u32 *val_high)
++{
++      u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel);
++      u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) |
++                        FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
++                        FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
++                        FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
++
++      airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
++      if (read_poll_timeout(airoha_qdma_rr, val,
++                            val & TRTCM_PARAM_RW_DONE_MASK,
++                            USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
++                            qdma, REG_TRTCM_CFG_PARAM(addr)))
++              return -ETIMEDOUT;
++
++      *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr));
++      if (val_high)
++              *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr));
++
++      return 0;
++}
++
++static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,
++                                     u32 addr, enum trtcm_param_type param,
++                                     enum trtcm_mode_type mode, u32 val)
++{
++      u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel);
++      u32 config = TRTCM_PARAM_RW_MASK |
++                   FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) |
++                   FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
++                   FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
++                   FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
++
++      airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
++      airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
++
++      return read_poll_timeout(airoha_qdma_rr, val,
++                               val & TRTCM_PARAM_RW_DONE_MASK,
++                               USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
++                               qdma, REG_TRTCM_CFG_PARAM(addr));
++}
++
++static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
++                                      u32 addr, enum trtcm_mode_type mode,
++                                      bool enable, u32 enable_mask)
++{
++      u32 val;
++
++      if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
++                                      mode, &val, NULL))
++              return -EINVAL;
++
++      val = enable ? val | enable_mask : val & ~enable_mask;
++
++      return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
++                                         mode, val);
++}
++
++static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma,
++                                            int channel, u32 addr,
++                                            enum trtcm_mode_type mode,
++                                            u32 rate_val, u32 bucket_size)
++{
++      u32 val, config, tick, unit, rate, rate_frac;
++      int err;
++
++      if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
++                                      mode, &config, NULL))
++              return -EINVAL;
++
++      val = airoha_qdma_rr(qdma, addr);
++      tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val);
++      if (config & TRTCM_TICK_SEL)
++              tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val);
++      if (!tick)
++              return -EINVAL;
++
++      unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick;
++      if (!unit)
++              return -EINVAL;
++
++      rate = rate_val / unit;
++      rate_frac = rate_val % unit;
++      rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit;
++      rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) |
++             FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac);
++
++      err = airoha_qdma_set_trtcm_param(qdma, channel, addr,
++                                        TRTCM_TOKEN_RATE_MODE, mode, rate);
++      if (err)
++              return err;
++
++      val = max_t(u32, bucket_size, MIN_TOKEN_SIZE);
++      val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET);
++
++      return airoha_qdma_set_trtcm_param(qdma, channel, addr,
++                                         TRTCM_BUCKETSIZE_SHIFT_MODE,
++                                         mode, val);
++}
++
++static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port,
++                                       int channel, u32 rate,
++                                       u32 bucket_size)
++{
++      int i, err;
++
++      for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
++              err = airoha_qdma_set_trtcm_config(port->qdma, channel,
++                                                 REG_EGRESS_TRTCM_CFG, i,
++                                                 !!rate, TRTCM_METER_MODE);
++              if (err)
++                      return err;
++
++              err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel,
++                                                       REG_EGRESS_TRTCM_CFG,
++                                                       i, rate, bucket_size);
++              if (err)
++                      return err;
++      }
++
++      return 0;
++}
++
++static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port,
++                                        struct tc_htb_qopt_offload *opt)
++{
++      u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
++      u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
++      struct net_device *dev = port->dev;
++      int num_tx_queues = dev->real_num_tx_queues;
++      int err;
++
++      if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
++              NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
++              return -EINVAL;
++      }
++
++      err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum);
++      if (err) {
++              NL_SET_ERR_MSG_MOD(opt->extack,
++                                 "failed configuring htb offload");
++              return err;
++      }
++
++      if (opt->command == TC_HTB_NODE_MODIFY)
++              return 0;
++
++      err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1);
++      if (err) {
++              airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum);
++              NL_SET_ERR_MSG_MOD(opt->extack,
++                                 "failed setting real_num_tx_queues");
++              return err;
++      }
++
++      set_bit(channel, port->qos_sq_bmap);
++      opt->qid = AIROHA_NUM_TX_RING + channel;
++
++      return 0;
++}
++
++static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue)
++{
++      struct net_device *dev = port->dev;
++
++      netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
++      airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0);
++      clear_bit(queue, port->qos_sq_bmap);
++}
++
++static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port,
++                                         struct tc_htb_qopt_offload *opt)
++{
++      u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
++
++      if (!test_bit(channel, port->qos_sq_bmap)) {
++              NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
++              return -EINVAL;
++      }
++
++      airoha_tc_remove_htb_queue(port, channel);
++
++      return 0;
++}
++
++static int airoha_tc_htb_destroy(struct airoha_gdm_port *port)
++{
++      int q;
++
++      for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
++              airoha_tc_remove_htb_queue(port, q);
++
++      return 0;
++}
++
++static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port,
++                                          struct tc_htb_qopt_offload *opt)
++{
++      u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
++
++      if (!test_bit(channel, port->qos_sq_bmap)) {
++              NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
++              return -EINVAL;
++      }
++
++      opt->qid = channel;
++
++      return 0;
++}
++
++static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port,
++                                   struct tc_htb_qopt_offload *opt)
++{
++      switch (opt->command) {
++      case TC_HTB_CREATE:
++              break;
++      case TC_HTB_DESTROY:
++              return airoha_tc_htb_destroy(port);
++      case TC_HTB_NODE_MODIFY:
++      case TC_HTB_LEAF_ALLOC_QUEUE:
++              return airoha_tc_htb_alloc_leaf_queue(port, 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(port, opt);
++      case TC_HTB_LEAF_QUERY_QUEUE:
++              return airoha_tc_get_htb_get_leaf_queue(port, opt);
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      return 0;
++}
++
++static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
++                             void *type_data)
++{
++      struct airoha_gdm_port *port = netdev_priv(dev);
++
++      switch (type) {
++      case TC_SETUP_QDISC_ETS:
++              return airoha_tc_setup_qdisc_ets(port, type_data);
++      case TC_SETUP_QDISC_HTB:
++              return airoha_tc_setup_qdisc_htb(port, type_data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++static const struct net_device_ops airoha_netdev_ops = {
++      .ndo_init               = airoha_dev_init,
++      .ndo_open               = airoha_dev_open,
++      .ndo_stop               = airoha_dev_stop,
++      .ndo_select_queue       = airoha_dev_select_queue,
++      .ndo_start_xmit         = airoha_dev_xmit,
++      .ndo_get_stats64        = airoha_dev_get_stats64,
++      .ndo_set_mac_address    = airoha_dev_set_macaddr,
++      .ndo_setup_tc           = airoha_dev_tc_setup,
++};
++
++static const struct ethtool_ops airoha_ethtool_ops = {
++      .get_drvinfo            = airoha_ethtool_get_drvinfo,
++      .get_eth_mac_stats      = airoha_ethtool_get_mac_stats,
++      .get_rmon_stats         = airoha_ethtool_get_rmon_stats,
++};
++
++static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
++{
++      const __be32 *id_ptr = of_get_property(np, "reg", NULL);
++      struct airoha_gdm_port *port;
++      struct airoha_qdma *qdma;
++      struct net_device *dev;
++      int err, index;
++      u32 id;
++
++      if (!id_ptr) {
++              dev_err(eth->dev, "missing gdm port id\n");
++              return -EINVAL;
++      }
++
++      id = be32_to_cpup(id_ptr);
++      index = id - 1;
++
++      if (!id || id > ARRAY_SIZE(eth->ports)) {
++              dev_err(eth->dev, "invalid gdm port id: %d\n", id);
++              return -EINVAL;
++      }
++
++      if (eth->ports[index]) {
++              dev_err(eth->dev, "duplicate gdm port id: %d\n", id);
++              return -EINVAL;
++      }
++
++      dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
++                                    AIROHA_NUM_NETDEV_TX_RINGS,
++                                    AIROHA_NUM_RX_RING);
++      if (!dev) {
++              dev_err(eth->dev, "alloc_etherdev failed\n");
++              return -ENOMEM;
++      }
++
++      qdma = &eth->qdma[index % AIROHA_MAX_NUM_QDMA];
++      dev->netdev_ops = &airoha_netdev_ops;
++      dev->ethtool_ops = &airoha_ethtool_ops;
++      dev->max_mtu = AIROHA_MAX_MTU;
++      dev->watchdog_timeo = 5 * HZ;
++      dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
++                         NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
++                         NETIF_F_SG | NETIF_F_TSO |
++                         NETIF_F_HW_TC;
++      dev->features |= dev->hw_features;
++      dev->dev.of_node = np;
++      dev->irq = qdma->irq;
++      SET_NETDEV_DEV(dev, eth->dev);
++
++      /* reserve hw queues for HTB offloading */
++      err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
++      if (err)
++              return err;
++
++      err = of_get_ethdev_address(np, dev);
++      if (err) {
++              if (err == -EPROBE_DEFER)
++                      return err;
++
++              eth_hw_addr_random(dev);
++              dev_info(eth->dev, "generated random MAC address %pM\n",
++                       dev->dev_addr);
++      }
++
++      port = netdev_priv(dev);
++      u64_stats_init(&port->stats.syncp);
++      spin_lock_init(&port->stats.lock);
++      port->qdma = qdma;
++      port->dev = dev;
++      port->id = id;
++      eth->ports[index] = port;
++
++      return register_netdev(dev);
++}
++
++static int airoha_probe(struct platform_device *pdev)
++{
++      struct device_node *np;
++      struct airoha_eth *eth;
++      int i, err;
++
++      eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
++      if (!eth)
++              return -ENOMEM;
++
++      eth->dev = &pdev->dev;
++
++      err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32));
++      if (err) {
++              dev_err(eth->dev, "failed configuring DMA mask\n");
++              return err;
++      }
++
++      eth->fe_regs = devm_platform_ioremap_resource_byname(pdev, "fe");
++      if (IS_ERR(eth->fe_regs))
++              return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs),
++                                   "failed to iomap fe regs\n");
++
++      eth->rsts[0].id = "fe";
++      eth->rsts[1].id = "pdma";
++      eth->rsts[2].id = "qdma";
++      err = devm_reset_control_bulk_get_exclusive(eth->dev,
++                                                  ARRAY_SIZE(eth->rsts),
++                                                  eth->rsts);
++      if (err) {
++              dev_err(eth->dev, "failed to get bulk reset lines\n");
++              return err;
++      }
++
++      eth->xsi_rsts[0].id = "xsi-mac";
++      eth->xsi_rsts[1].id = "hsi0-mac";
++      eth->xsi_rsts[2].id = "hsi1-mac";
++      eth->xsi_rsts[3].id = "hsi-mac";
++      eth->xsi_rsts[4].id = "xfp-mac";
++      err = devm_reset_control_bulk_get_exclusive(eth->dev,
++                                                  ARRAY_SIZE(eth->xsi_rsts),
++                                                  eth->xsi_rsts);
++      if (err) {
++              dev_err(eth->dev, "failed to get bulk xsi reset lines\n");
++              return err;
++      }
++
++      eth->napi_dev = alloc_netdev_dummy(0);
++      if (!eth->napi_dev)
++              return -ENOMEM;
++
++      /* Enable threaded NAPI by default */
++      eth->napi_dev->threaded = true;
++      strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name));
++      platform_set_drvdata(pdev, eth);
++
++      err = airoha_hw_init(pdev, eth);
++      if (err)
++              goto error_hw_cleanup;
++
++      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
++              airoha_qdma_start_napi(&eth->qdma[i]);
++
++      for_each_child_of_node(pdev->dev.of_node, np) {
++              if (!of_device_is_compatible(np, "airoha,eth-mac"))
++                      continue;
++
++              if (!of_device_is_available(np))
++                      continue;
++
++              err = airoha_alloc_gdm_port(eth, np);
++              if (err) {
++                      of_node_put(np);
++                      goto error_napi_stop;
++              }
++      }
++
++      return 0;
++
++error_napi_stop:
++      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
++              airoha_qdma_stop_napi(&eth->qdma[i]);
++error_hw_cleanup:
++      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
++              airoha_hw_cleanup(&eth->qdma[i]);
++
++      for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
++              struct airoha_gdm_port *port = eth->ports[i];
++
++              if (port && port->dev->reg_state == NETREG_REGISTERED)
++                      unregister_netdev(port->dev);
++      }
++      free_netdev(eth->napi_dev);
++      platform_set_drvdata(pdev, NULL);
++
++      return err;
++}
++
++static void airoha_remove(struct platform_device *pdev)
++{
++      struct airoha_eth *eth = platform_get_drvdata(pdev);
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
++              airoha_qdma_stop_napi(&eth->qdma[i]);
++              airoha_hw_cleanup(&eth->qdma[i]);
++      }
++
++      for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
++              struct airoha_gdm_port *port = eth->ports[i];
++
++              if (!port)
++                      continue;
++
++              airoha_dev_stop(port->dev);
++              unregister_netdev(port->dev);
++      }
++      free_netdev(eth->napi_dev);
++
++      platform_set_drvdata(pdev, NULL);
++}
++
++static const struct of_device_id of_airoha_match[] = {
++      { .compatible = "airoha,en7581-eth" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, of_airoha_match);
++
++static struct platform_driver airoha_driver = {
++      .probe = airoha_probe,
++      .remove_new = airoha_remove,
++      .driver = {
++              .name = KBUILD_MODNAME,
++              .of_match_table = of_airoha_match,
++      },
++};
++module_platform_driver(airoha_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
++MODULE_DESCRIPTION("Ethernet driver for Airoha SoC");
+--- a/drivers/net/ethernet/mediatek/airoha_eth.c
++++ /dev/null
+@@ -1,3359 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-only
+-/*
+- * Copyright (c) 2024 AIROHA Inc
+- * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+- */
+-#include <linux/etherdevice.h>
+-#include <linux/iopoll.h>
+-#include <linux/kernel.h>
+-#include <linux/netdevice.h>
+-#include <linux/of.h>
+-#include <linux/of_net.h>
+-#include <linux/platform_device.h>
+-#include <linux/reset.h>
+-#include <linux/tcp.h>
+-#include <linux/u64_stats_sync.h>
+-#include <net/dsa.h>
+-#include <net/page_pool/helpers.h>
+-#include <net/pkt_cls.h>
+-#include <uapi/linux/ppp_defs.h>
+-
+-#define AIROHA_MAX_NUM_GDM_PORTS      1
+-#define AIROHA_MAX_NUM_QDMA           2
+-#define AIROHA_MAX_NUM_RSTS           3
+-#define AIROHA_MAX_NUM_XSI_RSTS               5
+-#define AIROHA_MAX_MTU                        2000
+-#define AIROHA_MAX_PACKET_SIZE                2048
+-#define AIROHA_NUM_QOS_CHANNELS               4
+-#define AIROHA_NUM_QOS_QUEUES         8
+-#define AIROHA_NUM_TX_RING            32
+-#define AIROHA_NUM_RX_RING            32
+-#define AIROHA_NUM_NETDEV_TX_RINGS    (AIROHA_NUM_TX_RING + \
+-                                       AIROHA_NUM_QOS_CHANNELS)
+-#define AIROHA_FE_MC_MAX_VLAN_TABLE   64
+-#define AIROHA_FE_MC_MAX_VLAN_PORT    16
+-#define AIROHA_NUM_TX_IRQ             2
+-#define HW_DSCP_NUM                   2048
+-#define IRQ_QUEUE_LEN(_n)             ((_n) ? 1024 : 2048)
+-#define TX_DSCP_NUM                   1024
+-#define RX_DSCP_NUM(_n)                       \
+-      ((_n) ==  2 ? 128 :             \
+-       (_n) == 11 ? 128 :             \
+-       (_n) == 15 ? 128 :             \
+-       (_n) ==  0 ? 1024 : 16)
+-
+-#define PSE_RSV_PAGES                 128
+-#define PSE_QUEUE_RSV_PAGES           64
+-
+-#define QDMA_METER_IDX(_n)            ((_n) & 0xff)
+-#define QDMA_METER_GROUP(_n)          (((_n) >> 8) & 0x3)
+-
+-/* FE */
+-#define PSE_BASE                      0x0100
+-#define CSR_IFC_BASE                  0x0200
+-#define CDM1_BASE                     0x0400
+-#define GDM1_BASE                     0x0500
+-#define PPE1_BASE                     0x0c00
+-
+-#define CDM2_BASE                     0x1400
+-#define GDM2_BASE                     0x1500
+-
+-#define GDM3_BASE                     0x1100
+-#define GDM4_BASE                     0x2500
+-
+-#define GDM_BASE(_n)                  \
+-      ((_n) == 4 ? GDM4_BASE :        \
+-       (_n) == 3 ? GDM3_BASE :        \
+-       (_n) == 2 ? GDM2_BASE : GDM1_BASE)
+-
+-#define REG_FE_DMA_GLO_CFG            0x0000
+-#define FE_DMA_GLO_L2_SPACE_MASK      GENMASK(7, 4)
+-#define FE_DMA_GLO_PG_SZ_MASK         BIT(3)
+-
+-#define REG_FE_RST_GLO_CFG            0x0004
+-#define FE_RST_GDM4_MBI_ARB_MASK      BIT(3)
+-#define FE_RST_GDM3_MBI_ARB_MASK      BIT(2)
+-#define FE_RST_CORE_MASK              BIT(0)
+-
+-#define REG_FE_WAN_MAC_H              0x0030
+-#define REG_FE_LAN_MAC_H              0x0040
+-
+-#define REG_FE_MAC_LMIN(_n)           ((_n) + 0x04)
+-#define REG_FE_MAC_LMAX(_n)           ((_n) + 0x08)
+-
+-#define REG_FE_CDM1_OQ_MAP0           0x0050
+-#define REG_FE_CDM1_OQ_MAP1           0x0054
+-#define REG_FE_CDM1_OQ_MAP2           0x0058
+-#define REG_FE_CDM1_OQ_MAP3           0x005c
+-
+-#define REG_FE_PCE_CFG                        0x0070
+-#define PCE_DPI_EN_MASK                       BIT(2)
+-#define PCE_KA_EN_MASK                        BIT(1)
+-#define PCE_MC_EN_MASK                        BIT(0)
+-
+-#define REG_FE_PSE_QUEUE_CFG_WR               0x0080
+-#define PSE_CFG_PORT_ID_MASK          GENMASK(27, 24)
+-#define PSE_CFG_QUEUE_ID_MASK         GENMASK(20, 16)
+-#define PSE_CFG_WR_EN_MASK            BIT(8)
+-#define PSE_CFG_OQRSV_SEL_MASK                BIT(0)
+-
+-#define REG_FE_PSE_QUEUE_CFG_VAL      0x0084
+-#define PSE_CFG_OQ_RSV_MASK           GENMASK(13, 0)
+-
+-#define PSE_FQ_CFG                    0x008c
+-#define PSE_FQ_LIMIT_MASK             GENMASK(14, 0)
+-
+-#define REG_FE_PSE_BUF_SET            0x0090
+-#define PSE_SHARE_USED_LTHD_MASK      GENMASK(31, 16)
+-#define PSE_ALLRSV_MASK                       GENMASK(14, 0)
+-
+-#define REG_PSE_SHARE_USED_THD                0x0094
+-#define PSE_SHARE_USED_MTHD_MASK      GENMASK(31, 16)
+-#define PSE_SHARE_USED_HTHD_MASK      GENMASK(15, 0)
+-
+-#define REG_GDM_MISC_CFG              0x0148
+-#define GDM2_RDM_ACK_WAIT_PREF_MASK   BIT(9)
+-#define GDM2_CHN_VLD_MODE_MASK                BIT(5)
+-
+-#define REG_FE_CSR_IFC_CFG            CSR_IFC_BASE
+-#define FE_IFC_EN_MASK                        BIT(0)
+-
+-#define REG_FE_VIP_PORT_EN            0x01f0
+-#define REG_FE_IFC_PORT_EN            0x01f4
+-
+-#define REG_PSE_IQ_REV1                       (PSE_BASE + 0x08)
+-#define PSE_IQ_RES1_P2_MASK           GENMASK(23, 16)
+-
+-#define REG_PSE_IQ_REV2                       (PSE_BASE + 0x0c)
+-#define PSE_IQ_RES2_P5_MASK           GENMASK(15, 8)
+-#define PSE_IQ_RES2_P4_MASK           GENMASK(7, 0)
+-
+-#define REG_FE_VIP_EN(_n)             (0x0300 + ((_n) << 3))
+-#define PATN_FCPU_EN_MASK             BIT(7)
+-#define PATN_SWP_EN_MASK              BIT(6)
+-#define PATN_DP_EN_MASK                       BIT(5)
+-#define PATN_SP_EN_MASK                       BIT(4)
+-#define PATN_TYPE_MASK                        GENMASK(3, 1)
+-#define PATN_EN_MASK                  BIT(0)
+-
+-#define REG_FE_VIP_PATN(_n)           (0x0304 + ((_n) << 3))
+-#define PATN_DP_MASK                  GENMASK(31, 16)
+-#define PATN_SP_MASK                  GENMASK(15, 0)
+-
+-#define REG_CDM1_VLAN_CTRL            CDM1_BASE
+-#define CDM1_VLAN_MASK                        GENMASK(31, 16)
+-
+-#define REG_CDM1_FWD_CFG              (CDM1_BASE + 0x08)
+-#define CDM1_VIP_QSEL_MASK            GENMASK(24, 20)
+-
+-#define REG_CDM1_CRSN_QSEL(_n)                (CDM1_BASE + 0x10 + ((_n) << 2))
+-#define CDM1_CRSN_QSEL_REASON_MASK(_n)        \
+-      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
+-
+-#define REG_CDM2_FWD_CFG              (CDM2_BASE + 0x08)
+-#define CDM2_OAM_QSEL_MASK            GENMASK(31, 27)
+-#define CDM2_VIP_QSEL_MASK            GENMASK(24, 20)
+-
+-#define REG_CDM2_CRSN_QSEL(_n)                (CDM2_BASE + 0x10 + ((_n) << 2))
+-#define CDM2_CRSN_QSEL_REASON_MASK(_n)        \
+-      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
+-
+-#define REG_GDM_FWD_CFG(_n)           GDM_BASE(_n)
+-#define GDM_DROP_CRC_ERR              BIT(23)
+-#define GDM_IP4_CKSUM                 BIT(22)
+-#define GDM_TCP_CKSUM                 BIT(21)
+-#define GDM_UDP_CKSUM                 BIT(20)
+-#define GDM_UCFQ_MASK                 GENMASK(15, 12)
+-#define GDM_BCFQ_MASK                 GENMASK(11, 8)
+-#define GDM_MCFQ_MASK                 GENMASK(7, 4)
+-#define GDM_OCFQ_MASK                 GENMASK(3, 0)
+-
+-#define REG_GDM_INGRESS_CFG(_n)               (GDM_BASE(_n) + 0x10)
+-#define GDM_INGRESS_FC_EN_MASK                BIT(1)
+-#define GDM_STAG_EN_MASK              BIT(0)
+-
+-#define REG_GDM_LEN_CFG(_n)           (GDM_BASE(_n) + 0x14)
+-#define GDM_SHORT_LEN_MASK            GENMASK(13, 0)
+-#define GDM_LONG_LEN_MASK             GENMASK(29, 16)
+-
+-#define REG_FE_CPORT_CFG              (GDM1_BASE + 0x40)
+-#define FE_CPORT_PAD                  BIT(26)
+-#define FE_CPORT_PORT_XFC_MASK                BIT(25)
+-#define FE_CPORT_QUEUE_XFC_MASK               BIT(24)
+-
+-#define REG_FE_GDM_MIB_CLEAR(_n)      (GDM_BASE(_n) + 0xf0)
+-#define FE_GDM_MIB_RX_CLEAR_MASK      BIT(1)
+-#define FE_GDM_MIB_TX_CLEAR_MASK      BIT(0)
+-
+-#define REG_FE_GDM1_MIB_CFG           (GDM1_BASE + 0xf4)
+-#define FE_STRICT_RFC2819_MODE_MASK   BIT(31)
+-#define FE_GDM1_TX_MIB_SPLIT_EN_MASK  BIT(17)
+-#define FE_GDM1_RX_MIB_SPLIT_EN_MASK  BIT(16)
+-#define FE_TX_MIB_ID_MASK             GENMASK(15, 8)
+-#define FE_RX_MIB_ID_MASK             GENMASK(7, 0)
+-
+-#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x104)
+-#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x10c)
+-#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x110)
+-#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x114)
+-#define REG_FE_GDM_TX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x118)
+-#define REG_FE_GDM_TX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x11c)
+-#define REG_FE_GDM_TX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x120)
+-#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x124)
+-#define REG_FE_GDM_TX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x128)
+-#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x12c)
+-#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x130)
+-#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x134)
+-#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x138)
+-#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x13c)
+-#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x140)
+-
+-#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x148)
+-#define REG_FE_GDM_RX_FC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x14c)
+-#define REG_FE_GDM_RX_RC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x150)
+-#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n)   (GDM_BASE(_n) + 0x154)
+-#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n)      (GDM_BASE(_n) + 0x158)
+-#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x15c)
+-#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x160)
+-#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x164)
+-#define REG_FE_GDM_RX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x168)
+-#define REG_FE_GDM_RX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x16c)
+-#define REG_FE_GDM_RX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x170)
+-#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n)     (GDM_BASE(_n) + 0x174)
+-#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n)                (GDM_BASE(_n) + 0x178)
+-#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n)      (GDM_BASE(_n) + 0x17c)
+-#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x180)
+-#define REG_FE_GDM_RX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x184)
+-#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x188)
+-#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x18c)
+-#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x190)
+-#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x194)
+-#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x198)
+-#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x19c)
+-
+-#define REG_PPE1_TB_HASH_CFG          (PPE1_BASE + 0x250)
+-#define PPE1_SRAM_TABLE_EN_MASK               BIT(0)
+-#define PPE1_SRAM_HASH1_EN_MASK               BIT(8)
+-#define PPE1_DRAM_TABLE_EN_MASK               BIT(16)
+-#define PPE1_DRAM_HASH1_EN_MASK               BIT(24)
+-
+-#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x280)
+-#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x284)
+-#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x288)
+-#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x28c)
+-
+-#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x290)
+-#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x294)
+-#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x298)
+-#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x29c)
+-#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2b8)
+-#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2bc)
+-#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2c0)
+-#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2c4)
+-#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2c8)
+-#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2cc)
+-#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2e8)
+-#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2ec)
+-#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2f0)
+-#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2f4)
+-#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2f8)
+-#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2fc)
+-
+-#define REG_GDM2_CHN_RLS              (GDM2_BASE + 0x20)
+-#define MBI_RX_AGE_SEL_MASK           GENMASK(26, 25)
+-#define MBI_TX_AGE_SEL_MASK           GENMASK(18, 17)
+-
+-#define REG_GDM3_FWD_CFG              GDM3_BASE
+-#define GDM3_PAD_EN_MASK              BIT(28)
+-
+-#define REG_GDM4_FWD_CFG              GDM4_BASE
+-#define GDM4_PAD_EN_MASK              BIT(28)
+-#define GDM4_SPORT_OFFSET0_MASK               GENMASK(11, 8)
+-
+-#define REG_GDM4_SRC_PORT_SET         (GDM4_BASE + 0x23c)
+-#define GDM4_SPORT_OFF2_MASK          GENMASK(19, 16)
+-#define GDM4_SPORT_OFF1_MASK          GENMASK(15, 12)
+-#define GDM4_SPORT_OFF0_MASK          GENMASK(11, 8)
+-
+-#define REG_IP_FRAG_FP                        0x2010
+-#define IP_ASSEMBLE_PORT_MASK         GENMASK(24, 21)
+-#define IP_ASSEMBLE_NBQ_MASK          GENMASK(20, 16)
+-#define IP_FRAGMENT_PORT_MASK         GENMASK(8, 5)
+-#define IP_FRAGMENT_NBQ_MASK          GENMASK(4, 0)
+-
+-#define REG_MC_VLAN_EN                        0x2100
+-#define MC_VLAN_EN_MASK                       BIT(0)
+-
+-#define REG_MC_VLAN_CFG                       0x2104
+-#define MC_VLAN_CFG_CMD_DONE_MASK     BIT(31)
+-#define MC_VLAN_CFG_TABLE_ID_MASK     GENMASK(21, 16)
+-#define MC_VLAN_CFG_PORT_ID_MASK      GENMASK(11, 8)
+-#define MC_VLAN_CFG_TABLE_SEL_MASK    BIT(4)
+-#define MC_VLAN_CFG_RW_MASK           BIT(0)
+-
+-#define REG_MC_VLAN_DATA              0x2108
+-
+-#define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
+-
+-/* QDMA */
+-#define REG_QDMA_GLOBAL_CFG                   0x0004
+-#define GLOBAL_CFG_RX_2B_OFFSET_MASK          BIT(31)
+-#define GLOBAL_CFG_DMA_PREFERENCE_MASK                GENMASK(30, 29)
+-#define GLOBAL_CFG_CPU_TXR_RR_MASK            BIT(28)
+-#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK                BIT(27)
+-#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK     BIT(26)
+-#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK   BIT(25)
+-#define GLOBAL_CFG_OAM_MODIFY_MASK            BIT(24)
+-#define GLOBAL_CFG_RESET_MASK                 BIT(23)
+-#define GLOBAL_CFG_RESET_DONE_MASK            BIT(22)
+-#define GLOBAL_CFG_MULTICAST_EN_MASK          BIT(21)
+-#define GLOBAL_CFG_IRQ1_EN_MASK                       BIT(20)
+-#define GLOBAL_CFG_IRQ0_EN_MASK                       BIT(19)
+-#define GLOBAL_CFG_LOOPCNT_EN_MASK            BIT(18)
+-#define GLOBAL_CFG_RD_BYPASS_WR_MASK          BIT(17)
+-#define GLOBAL_CFG_QDMA_LOOPBACK_MASK         BIT(16)
+-#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK          GENMASK(13, 8)
+-#define GLOBAL_CFG_CHECK_DONE_MASK            BIT(7)
+-#define GLOBAL_CFG_TX_WB_DONE_MASK            BIT(6)
+-#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK         GENMASK(5, 4)
+-#define GLOBAL_CFG_RX_DMA_BUSY_MASK           BIT(3)
+-#define GLOBAL_CFG_RX_DMA_EN_MASK             BIT(2)
+-#define GLOBAL_CFG_TX_DMA_BUSY_MASK           BIT(1)
+-#define GLOBAL_CFG_TX_DMA_EN_MASK             BIT(0)
+-
+-#define REG_FWD_DSCP_BASE                     0x0010
+-#define REG_FWD_BUF_BASE                      0x0014
+-
+-#define REG_HW_FWD_DSCP_CFG                   0x0018
+-#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK         GENMASK(29, 28)
+-#define HW_FWD_DSCP_SCATTER_LEN_MASK          GENMASK(17, 16)
+-#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK      GENMASK(15, 0)
+-
+-#define REG_INT_STATUS(_n)            \
+-      (((_n) == 4) ? 0x0730 :         \
+-       ((_n) == 3) ? 0x0724 :         \
+-       ((_n) == 2) ? 0x0720 :         \
+-       ((_n) == 1) ? 0x0024 : 0x0020)
+-
+-#define REG_INT_ENABLE(_n)            \
+-      (((_n) == 4) ? 0x0750 :         \
+-       ((_n) == 3) ? 0x0744 :         \
+-       ((_n) == 2) ? 0x0740 :         \
+-       ((_n) == 1) ? 0x002c : 0x0028)
+-
+-/* QDMA_CSR_INT_ENABLE1 */
+-#define RX15_COHERENT_INT_MASK                BIT(31)
+-#define RX14_COHERENT_INT_MASK                BIT(30)
+-#define RX13_COHERENT_INT_MASK                BIT(29)
+-#define RX12_COHERENT_INT_MASK                BIT(28)
+-#define RX11_COHERENT_INT_MASK                BIT(27)
+-#define RX10_COHERENT_INT_MASK                BIT(26)
+-#define RX9_COHERENT_INT_MASK         BIT(25)
+-#define RX8_COHERENT_INT_MASK         BIT(24)
+-#define RX7_COHERENT_INT_MASK         BIT(23)
+-#define RX6_COHERENT_INT_MASK         BIT(22)
+-#define RX5_COHERENT_INT_MASK         BIT(21)
+-#define RX4_COHERENT_INT_MASK         BIT(20)
+-#define RX3_COHERENT_INT_MASK         BIT(19)
+-#define RX2_COHERENT_INT_MASK         BIT(18)
+-#define RX1_COHERENT_INT_MASK         BIT(17)
+-#define RX0_COHERENT_INT_MASK         BIT(16)
+-#define TX7_COHERENT_INT_MASK         BIT(15)
+-#define TX6_COHERENT_INT_MASK         BIT(14)
+-#define TX5_COHERENT_INT_MASK         BIT(13)
+-#define TX4_COHERENT_INT_MASK         BIT(12)
+-#define TX3_COHERENT_INT_MASK         BIT(11)
+-#define TX2_COHERENT_INT_MASK         BIT(10)
+-#define TX1_COHERENT_INT_MASK         BIT(9)
+-#define TX0_COHERENT_INT_MASK         BIT(8)
+-#define CNT_OVER_FLOW_INT_MASK                BIT(7)
+-#define IRQ1_FULL_INT_MASK            BIT(5)
+-#define IRQ1_INT_MASK                 BIT(4)
+-#define HWFWD_DSCP_LOW_INT_MASK               BIT(3)
+-#define HWFWD_DSCP_EMPTY_INT_MASK     BIT(2)
+-#define IRQ0_FULL_INT_MASK            BIT(1)
+-#define IRQ0_INT_MASK                 BIT(0)
+-
+-#define TX_DONE_INT_MASK(_n)                                  \
+-      ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK              \
+-            : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
+-
+-#define INT_TX_MASK                                           \
+-      (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK |                   \
+-       IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
+-
+-#define INT_IDX0_MASK                                         \
+-      (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK |        \
+-       TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK |        \
+-       TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK |        \
+-       TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK |        \
+-       RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK |        \
+-       RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK |        \
+-       RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK |        \
+-       RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK |        \
+-       RX15_COHERENT_INT_MASK | INT_TX_MASK)
+-
+-/* QDMA_CSR_INT_ENABLE2 */
+-#define RX15_NO_CPU_DSCP_INT_MASK     BIT(31)
+-#define RX14_NO_CPU_DSCP_INT_MASK     BIT(30)
+-#define RX13_NO_CPU_DSCP_INT_MASK     BIT(29)
+-#define RX12_NO_CPU_DSCP_INT_MASK     BIT(28)
+-#define RX11_NO_CPU_DSCP_INT_MASK     BIT(27)
+-#define RX10_NO_CPU_DSCP_INT_MASK     BIT(26)
+-#define RX9_NO_CPU_DSCP_INT_MASK      BIT(25)
+-#define RX8_NO_CPU_DSCP_INT_MASK      BIT(24)
+-#define RX7_NO_CPU_DSCP_INT_MASK      BIT(23)
+-#define RX6_NO_CPU_DSCP_INT_MASK      BIT(22)
+-#define RX5_NO_CPU_DSCP_INT_MASK      BIT(21)
+-#define RX4_NO_CPU_DSCP_INT_MASK      BIT(20)
+-#define RX3_NO_CPU_DSCP_INT_MASK      BIT(19)
+-#define RX2_NO_CPU_DSCP_INT_MASK      BIT(18)
+-#define RX1_NO_CPU_DSCP_INT_MASK      BIT(17)
+-#define RX0_NO_CPU_DSCP_INT_MASK      BIT(16)
+-#define RX15_DONE_INT_MASK            BIT(15)
+-#define RX14_DONE_INT_MASK            BIT(14)
+-#define RX13_DONE_INT_MASK            BIT(13)
+-#define RX12_DONE_INT_MASK            BIT(12)
+-#define RX11_DONE_INT_MASK            BIT(11)
+-#define RX10_DONE_INT_MASK            BIT(10)
+-#define RX9_DONE_INT_MASK             BIT(9)
+-#define RX8_DONE_INT_MASK             BIT(8)
+-#define RX7_DONE_INT_MASK             BIT(7)
+-#define RX6_DONE_INT_MASK             BIT(6)
+-#define RX5_DONE_INT_MASK             BIT(5)
+-#define RX4_DONE_INT_MASK             BIT(4)
+-#define RX3_DONE_INT_MASK             BIT(3)
+-#define RX2_DONE_INT_MASK             BIT(2)
+-#define RX1_DONE_INT_MASK             BIT(1)
+-#define RX0_DONE_INT_MASK             BIT(0)
+-
+-#define RX_DONE_INT_MASK                                      \
+-      (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK |                \
+-       RX2_DONE_INT_MASK | RX3_DONE_INT_MASK |                \
+-       RX4_DONE_INT_MASK | RX7_DONE_INT_MASK |                \
+-       RX8_DONE_INT_MASK | RX9_DONE_INT_MASK |                \
+-       RX15_DONE_INT_MASK)
+-#define INT_IDX1_MASK                                         \
+-      (RX_DONE_INT_MASK |                                     \
+-       RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK |  \
+-       RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK |  \
+-       RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK |  \
+-       RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK |  \
+-       RX15_NO_CPU_DSCP_INT_MASK)
+-
+-/* QDMA_CSR_INT_ENABLE5 */
+-#define TX31_COHERENT_INT_MASK                BIT(31)
+-#define TX30_COHERENT_INT_MASK                BIT(30)
+-#define TX29_COHERENT_INT_MASK                BIT(29)
+-#define TX28_COHERENT_INT_MASK                BIT(28)
+-#define TX27_COHERENT_INT_MASK                BIT(27)
+-#define TX26_COHERENT_INT_MASK                BIT(26)
+-#define TX25_COHERENT_INT_MASK                BIT(25)
+-#define TX24_COHERENT_INT_MASK                BIT(24)
+-#define TX23_COHERENT_INT_MASK                BIT(23)
+-#define TX22_COHERENT_INT_MASK                BIT(22)
+-#define TX21_COHERENT_INT_MASK                BIT(21)
+-#define TX20_COHERENT_INT_MASK                BIT(20)
+-#define TX19_COHERENT_INT_MASK                BIT(19)
+-#define TX18_COHERENT_INT_MASK                BIT(18)
+-#define TX17_COHERENT_INT_MASK                BIT(17)
+-#define TX16_COHERENT_INT_MASK                BIT(16)
+-#define TX15_COHERENT_INT_MASK                BIT(15)
+-#define TX14_COHERENT_INT_MASK                BIT(14)
+-#define TX13_COHERENT_INT_MASK                BIT(13)
+-#define TX12_COHERENT_INT_MASK                BIT(12)
+-#define TX11_COHERENT_INT_MASK                BIT(11)
+-#define TX10_COHERENT_INT_MASK                BIT(10)
+-#define TX9_COHERENT_INT_MASK         BIT(9)
+-#define TX8_COHERENT_INT_MASK         BIT(8)
+-
+-#define INT_IDX4_MASK                                         \
+-      (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK |        \
+-       TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK |      \
+-       TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK |      \
+-       TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK |      \
+-       TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK |      \
+-       TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK |      \
+-       TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK |      \
+-       TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK |      \
+-       TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK |      \
+-       TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK |      \
+-       TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK |      \
+-       TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK)
+-
+-#define REG_TX_IRQ_BASE(_n)           ((_n) ? 0x0048 : 0x0050)
+-
+-#define REG_TX_IRQ_CFG(_n)            ((_n) ? 0x004c : 0x0054)
+-#define TX_IRQ_THR_MASK                       GENMASK(27, 16)
+-#define TX_IRQ_DEPTH_MASK             GENMASK(11, 0)
+-
+-#define REG_IRQ_CLEAR_LEN(_n)         ((_n) ? 0x0064 : 0x0058)
+-#define IRQ_CLEAR_LEN_MASK            GENMASK(7, 0)
+-
+-#define REG_IRQ_STATUS(_n)            ((_n) ? 0x0068 : 0x005c)
+-#define IRQ_ENTRY_LEN_MASK            GENMASK(27, 16)
+-#define IRQ_HEAD_IDX_MASK             GENMASK(11, 0)
+-
+-#define REG_TX_RING_BASE(_n)  \
+-      (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5))
+-
+-#define REG_TX_RING_BLOCKING(_n)      \
+-      (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5))
+-
+-#define TX_RING_IRQ_BLOCKING_MAP_MASK                 BIT(6)
+-#define TX_RING_IRQ_BLOCKING_CFG_MASK                 BIT(4)
+-#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK          BIT(2)
+-#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK    BIT(1)
+-#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK    BIT(0)
+-
+-#define REG_TX_CPU_IDX(_n)    \
+-      (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5))
+-
+-#define TX_RING_CPU_IDX_MASK          GENMASK(15, 0)
+-
+-#define REG_TX_DMA_IDX(_n)    \
+-      (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5))
+-
+-#define TX_RING_DMA_IDX_MASK          GENMASK(15, 0)
+-
+-#define IRQ_RING_IDX_MASK             GENMASK(20, 16)
+-#define IRQ_DESC_IDX_MASK             GENMASK(15, 0)
+-
+-#define REG_RX_RING_BASE(_n)  \
+-      (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5))
+-
+-#define REG_RX_RING_SIZE(_n)  \
+-      (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5))
+-
+-#define RX_RING_THR_MASK              GENMASK(31, 16)
+-#define RX_RING_SIZE_MASK             GENMASK(15, 0)
+-
+-#define REG_RX_CPU_IDX(_n)    \
+-      (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5))
+-
+-#define RX_RING_CPU_IDX_MASK          GENMASK(15, 0)
+-
+-#define REG_RX_DMA_IDX(_n)    \
+-      (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5))
+-
+-#define REG_RX_DELAY_INT_IDX(_n)      \
+-      (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5))
+-
+-#define RX_DELAY_INT_MASK             GENMASK(15, 0)
+-
+-#define RX_RING_DMA_IDX_MASK          GENMASK(15, 0)
+-
+-#define REG_INGRESS_TRTCM_CFG         0x0070
+-#define INGRESS_TRTCM_EN_MASK         BIT(31)
+-#define INGRESS_TRTCM_MODE_MASK               BIT(30)
+-#define INGRESS_SLOW_TICK_RATIO_MASK  GENMASK(29, 16)
+-#define INGRESS_FAST_TICK_MASK                GENMASK(15, 0)
+-
+-#define REG_QUEUE_CLOSE_CFG(_n)               (0x00a0 + ((_n) & 0xfc))
+-#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m)   BIT((_m) + (((_n) & 0x3) << 3))
+-
+-#define REG_TXQ_DIS_CFG_BASE(_n)      ((_n) ? 0x20a0 : 0x00a0)
+-#define REG_TXQ_DIS_CFG(_n, _m)               (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2)
+-
+-#define REG_CNTR_CFG(_n)              (0x0400 + ((_n) << 3))
+-#define CNTR_EN_MASK                  BIT(31)
+-#define CNTR_ALL_CHAN_EN_MASK         BIT(30)
+-#define CNTR_ALL_QUEUE_EN_MASK                BIT(29)
+-#define CNTR_ALL_DSCP_RING_EN_MASK    BIT(28)
+-#define CNTR_SRC_MASK                 GENMASK(27, 24)
+-#define CNTR_DSCP_RING_MASK           GENMASK(20, 16)
+-#define CNTR_CHAN_MASK                        GENMASK(7, 3)
+-#define CNTR_QUEUE_MASK                       GENMASK(2, 0)
+-
+-#define REG_CNTR_VAL(_n)              (0x0404 + ((_n) << 3))
+-
+-#define REG_LMGR_INIT_CFG             0x1000
+-#define LMGR_INIT_START                       BIT(31)
+-#define LMGR_SRAM_MODE_MASK           BIT(30)
+-#define HW_FWD_PKTSIZE_OVERHEAD_MASK  GENMASK(27, 20)
+-#define HW_FWD_DESC_NUM_MASK          GENMASK(16, 0)
+-
+-#define REG_FWD_DSCP_LOW_THR          0x1004
+-#define FWD_DSCP_LOW_THR_MASK         GENMASK(17, 0)
+-
+-#define REG_EGRESS_RATE_METER_CFG             0x100c
+-#define EGRESS_RATE_METER_EN_MASK             BIT(31)
+-#define EGRESS_RATE_METER_EQ_RATE_EN_MASK     BIT(17)
+-#define EGRESS_RATE_METER_WINDOW_SZ_MASK      GENMASK(16, 12)
+-#define EGRESS_RATE_METER_TIMESLICE_MASK      GENMASK(10, 0)
+-
+-#define REG_EGRESS_TRTCM_CFG          0x1010
+-#define EGRESS_TRTCM_EN_MASK          BIT(31)
+-#define EGRESS_TRTCM_MODE_MASK                BIT(30)
+-#define EGRESS_SLOW_TICK_RATIO_MASK   GENMASK(29, 16)
+-#define EGRESS_FAST_TICK_MASK         GENMASK(15, 0)
+-
+-#define TRTCM_PARAM_RW_MASK           BIT(31)
+-#define TRTCM_PARAM_RW_DONE_MASK      BIT(30)
+-#define TRTCM_PARAM_TYPE_MASK         GENMASK(29, 28)
+-#define TRTCM_METER_GROUP_MASK                GENMASK(27, 26)
+-#define TRTCM_PARAM_INDEX_MASK                GENMASK(23, 17)
+-#define TRTCM_PARAM_RATE_TYPE_MASK    BIT(16)
+-
+-#define REG_TRTCM_CFG_PARAM(_n)               ((_n) + 0x4)
+-#define REG_TRTCM_DATA_LOW(_n)                ((_n) + 0x8)
+-#define REG_TRTCM_DATA_HIGH(_n)               ((_n) + 0xc)
+-
+-#define REG_TXWRR_MODE_CFG            0x1020
+-#define TWRR_WEIGHT_SCALE_MASK                BIT(31)
+-#define TWRR_WEIGHT_BASE_MASK         BIT(3)
+-
+-#define REG_TXWRR_WEIGHT_CFG          0x1024
+-#define TWRR_RW_CMD_MASK              BIT(31)
+-#define TWRR_RW_CMD_DONE              BIT(30)
+-#define TWRR_CHAN_IDX_MASK            GENMASK(23, 19)
+-#define TWRR_QUEUE_IDX_MASK           GENMASK(18, 16)
+-#define TWRR_VALUE_MASK                       GENMASK(15, 0)
+-
+-#define REG_PSE_BUF_USAGE_CFG         0x1028
+-#define PSE_BUF_ESTIMATE_EN_MASK      BIT(29)
+-
+-#define REG_CHAN_QOS_MODE(_n)         (0x1040 + ((_n) << 2))
+-#define CHAN_QOS_MODE_MASK(_n)                GENMASK(2 + ((_n) << 2), (_n) << 2)
+-
+-#define REG_GLB_TRTCM_CFG             0x1080
+-#define GLB_TRTCM_EN_MASK             BIT(31)
+-#define GLB_TRTCM_MODE_MASK           BIT(30)
+-#define GLB_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
+-#define GLB_FAST_TICK_MASK            GENMASK(15, 0)
+-
+-#define REG_TXQ_CNGST_CFG             0x10a0
+-#define TXQ_CNGST_DROP_EN             BIT(31)
+-#define TXQ_CNGST_DEI_DROP_EN         BIT(30)
+-
+-#define REG_SLA_TRTCM_CFG             0x1150
+-#define SLA_TRTCM_EN_MASK             BIT(31)
+-#define SLA_TRTCM_MODE_MASK           BIT(30)
+-#define SLA_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
+-#define SLA_FAST_TICK_MASK            GENMASK(15, 0)
+-
+-/* CTRL */
+-#define QDMA_DESC_DONE_MASK           BIT(31)
+-#define QDMA_DESC_DROP_MASK           BIT(30) /* tx: drop - rx: overflow */
+-#define QDMA_DESC_MORE_MASK           BIT(29) /* more SG elements */
+-#define QDMA_DESC_DEI_MASK            BIT(25)
+-#define QDMA_DESC_NO_DROP_MASK                BIT(24)
+-#define QDMA_DESC_LEN_MASK            GENMASK(15, 0)
+-/* DATA */
+-#define QDMA_DESC_NEXT_ID_MASK                GENMASK(15, 0)
+-/* TX MSG0 */
+-#define QDMA_ETH_TXMSG_MIC_IDX_MASK   BIT(30)
+-#define QDMA_ETH_TXMSG_SP_TAG_MASK    GENMASK(29, 14)
+-#define QDMA_ETH_TXMSG_ICO_MASK               BIT(13)
+-#define QDMA_ETH_TXMSG_UCO_MASK               BIT(12)
+-#define QDMA_ETH_TXMSG_TCO_MASK               BIT(11)
+-#define QDMA_ETH_TXMSG_TSO_MASK               BIT(10)
+-#define QDMA_ETH_TXMSG_FAST_MASK      BIT(9)
+-#define QDMA_ETH_TXMSG_OAM_MASK               BIT(8)
+-#define QDMA_ETH_TXMSG_CHAN_MASK      GENMASK(7, 3)
+-#define QDMA_ETH_TXMSG_QUEUE_MASK     GENMASK(2, 0)
+-/* TX MSG1 */
+-#define QDMA_ETH_TXMSG_NO_DROP                BIT(31)
+-#define QDMA_ETH_TXMSG_METER_MASK     GENMASK(30, 24) /* 0x7f no meters */
+-#define QDMA_ETH_TXMSG_FPORT_MASK     GENMASK(23, 20)
+-#define QDMA_ETH_TXMSG_NBOQ_MASK      GENMASK(19, 15)
+-#define QDMA_ETH_TXMSG_HWF_MASK               BIT(14)
+-#define QDMA_ETH_TXMSG_HOP_MASK               BIT(13)
+-#define QDMA_ETH_TXMSG_PTP_MASK               BIT(12)
+-#define QDMA_ETH_TXMSG_ACNT_G1_MASK   GENMASK(10, 6)  /* 0x1f do not count */
+-#define QDMA_ETH_TXMSG_ACNT_G0_MASK   GENMASK(5, 0)   /* 0x3f do not count */
+-
+-/* RX MSG1 */
+-#define QDMA_ETH_RXMSG_DEI_MASK               BIT(31)
+-#define QDMA_ETH_RXMSG_IP6_MASK               BIT(30)
+-#define QDMA_ETH_RXMSG_IP4_MASK               BIT(29)
+-#define QDMA_ETH_RXMSG_IP4F_MASK      BIT(28)
+-#define QDMA_ETH_RXMSG_L4_VALID_MASK  BIT(27)
+-#define QDMA_ETH_RXMSG_L4F_MASK               BIT(26)
+-#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)
+-
+-struct airoha_qdma_desc {
+-      __le32 rsv;
+-      __le32 ctrl;
+-      __le32 addr;
+-      __le32 data;
+-      __le32 msg0;
+-      __le32 msg1;
+-      __le32 msg2;
+-      __le32 msg3;
+-};
+-
+-/* CTRL0 */
+-#define QDMA_FWD_DESC_CTX_MASK                BIT(31)
+-#define QDMA_FWD_DESC_RING_MASK               GENMASK(30, 28)
+-#define QDMA_FWD_DESC_IDX_MASK                GENMASK(27, 16)
+-#define QDMA_FWD_DESC_LEN_MASK                GENMASK(15, 0)
+-/* CTRL1 */
+-#define QDMA_FWD_DESC_FIRST_IDX_MASK  GENMASK(15, 0)
+-/* CTRL2 */
+-#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK       GENMASK(2, 0)
+-
+-struct airoha_qdma_fwd_desc {
+-      __le32 addr;
+-      __le32 ctrl0;
+-      __le32 ctrl1;
+-      __le32 ctrl2;
+-      __le32 msg0;
+-      __le32 msg1;
+-      __le32 rsv0;
+-      __le32 rsv1;
+-};
+-
+-enum {
+-      QDMA_INT_REG_IDX0,
+-      QDMA_INT_REG_IDX1,
+-      QDMA_INT_REG_IDX2,
+-      QDMA_INT_REG_IDX3,
+-      QDMA_INT_REG_IDX4,
+-      QDMA_INT_REG_MAX
+-};
+-
+-enum {
+-      XSI_PCIE0_PORT,
+-      XSI_PCIE1_PORT,
+-      XSI_USB_PORT,
+-      XSI_AE_PORT,
+-      XSI_ETH_PORT,
+-};
+-
+-enum {
+-      XSI_PCIE0_VIP_PORT_MASK = BIT(22),
+-      XSI_PCIE1_VIP_PORT_MASK = BIT(23),
+-      XSI_USB_VIP_PORT_MASK   = BIT(25),
+-      XSI_ETH_VIP_PORT_MASK   = BIT(24),
+-};
+-
+-enum {
+-      DEV_STATE_INITIALIZED,
+-};
+-
+-enum {
+-      CDM_CRSN_QSEL_Q1 = 1,
+-      CDM_CRSN_QSEL_Q5 = 5,
+-      CDM_CRSN_QSEL_Q6 = 6,
+-      CDM_CRSN_QSEL_Q15 = 15,
+-};
+-
+-enum {
+-      CRSN_08 = 0x8,
+-      CRSN_21 = 0x15, /* KA */
+-      CRSN_22 = 0x16, /* hit bind and force route to CPU */
+-      CRSN_24 = 0x18,
+-      CRSN_25 = 0x19,
+-};
+-
+-enum {
+-      FE_PSE_PORT_CDM1,
+-      FE_PSE_PORT_GDM1,
+-      FE_PSE_PORT_GDM2,
+-      FE_PSE_PORT_GDM3,
+-      FE_PSE_PORT_PPE1,
+-      FE_PSE_PORT_CDM2,
+-      FE_PSE_PORT_CDM3,
+-      FE_PSE_PORT_CDM4,
+-      FE_PSE_PORT_PPE2,
+-      FE_PSE_PORT_GDM4,
+-      FE_PSE_PORT_CDM5,
+-      FE_PSE_PORT_DROP = 0xf,
+-};
+-
+-enum tx_sched_mode {
+-      TC_SCH_WRR8,
+-      TC_SCH_SP,
+-      TC_SCH_WRR7,
+-      TC_SCH_WRR6,
+-      TC_SCH_WRR5,
+-      TC_SCH_WRR4,
+-      TC_SCH_WRR3,
+-      TC_SCH_WRR2,
+-};
+-
+-enum trtcm_param_type {
+-      TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */
+-      TRTCM_TOKEN_RATE_MODE,
+-      TRTCM_BUCKETSIZE_SHIFT_MODE,
+-      TRTCM_BUCKET_COUNTER_MODE,
+-};
+-
+-enum trtcm_mode_type {
+-      TRTCM_COMMIT_MODE,
+-      TRTCM_PEAK_MODE,
+-};
+-
+-enum trtcm_param {
+-      TRTCM_TICK_SEL = BIT(0),
+-      TRTCM_PKT_MODE = BIT(1),
+-      TRTCM_METER_MODE = BIT(2),
+-};
+-
+-#define MIN_TOKEN_SIZE                                4096
+-#define MAX_TOKEN_SIZE_OFFSET                 17
+-#define TRTCM_TOKEN_RATE_MASK                 GENMASK(23, 6)
+-#define TRTCM_TOKEN_RATE_FRACTION_MASK                GENMASK(5, 0)
+-
+-struct airoha_queue_entry {
+-      union {
+-              void *buf;
+-              struct sk_buff *skb;
+-      };
+-      dma_addr_t dma_addr;
+-      u16 dma_len;
+-};
+-
+-struct airoha_queue {
+-      struct airoha_qdma *qdma;
+-
+-      /* protect concurrent queue accesses */
+-      spinlock_t lock;
+-      struct airoha_queue_entry *entry;
+-      struct airoha_qdma_desc *desc;
+-      u16 head;
+-      u16 tail;
+-
+-      int queued;
+-      int ndesc;
+-      int free_thr;
+-      int buf_size;
+-
+-      struct napi_struct napi;
+-      struct page_pool *page_pool;
+-};
+-
+-struct airoha_tx_irq_queue {
+-      struct airoha_qdma *qdma;
+-
+-      struct napi_struct napi;
+-
+-      int size;
+-      u32 *q;
+-};
+-
+-struct airoha_hw_stats {
+-      /* protect concurrent hw_stats accesses */
+-      spinlock_t lock;
+-      struct u64_stats_sync syncp;
+-
+-      /* get_stats64 */
+-      u64 rx_ok_pkts;
+-      u64 tx_ok_pkts;
+-      u64 rx_ok_bytes;
+-      u64 tx_ok_bytes;
+-      u64 rx_multicast;
+-      u64 rx_errors;
+-      u64 rx_drops;
+-      u64 tx_drops;
+-      u64 rx_crc_error;
+-      u64 rx_over_errors;
+-      /* ethtool stats */
+-      u64 tx_broadcast;
+-      u64 tx_multicast;
+-      u64 tx_len[7];
+-      u64 rx_broadcast;
+-      u64 rx_fragment;
+-      u64 rx_jabber;
+-      u64 rx_len[7];
+-};
+-
+-struct airoha_qdma {
+-      struct airoha_eth *eth;
+-      void __iomem *regs;
+-
+-      /* protect concurrent irqmask accesses */
+-      spinlock_t irq_lock;
+-      u32 irqmask[QDMA_INT_REG_MAX];
+-      int irq;
+-
+-      struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ];
+-
+-      struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
+-      struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
+-
+-      /* descriptor and packet buffers for qdma hw forward */
+-      struct {
+-              void *desc;
+-              void *q;
+-      } hfwd;
+-};
+-
+-struct airoha_gdm_port {
+-      struct airoha_qdma *qdma;
+-      struct net_device *dev;
+-      int id;
+-
+-      struct airoha_hw_stats stats;
+-
+-      DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+-
+-      /* qos stats counters */
+-      u64 cpu_tx_packets;
+-      u64 fwd_tx_packets;
+-};
+-
+-struct airoha_eth {
+-      struct device *dev;
+-
+-      unsigned long state;
+-      void __iomem *fe_regs;
+-
+-      struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
+-      struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
+-
+-      struct net_device *napi_dev;
+-
+-      struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA];
+-      struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
+-};
+-
+-static u32 airoha_rr(void __iomem *base, u32 offset)
+-{
+-      return readl(base + offset);
+-}
+-
+-static void airoha_wr(void __iomem *base, u32 offset, u32 val)
+-{
+-      writel(val, base + offset);
+-}
+-
+-static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val)
+-{
+-      val |= (airoha_rr(base, offset) & ~mask);
+-      airoha_wr(base, offset, val);
+-
+-      return val;
+-}
+-
+-#define airoha_fe_rr(eth, offset)                             \
+-      airoha_rr((eth)->fe_regs, (offset))
+-#define airoha_fe_wr(eth, offset, val)                                \
+-      airoha_wr((eth)->fe_regs, (offset), (val))
+-#define airoha_fe_rmw(eth, offset, mask, val)                 \
+-      airoha_rmw((eth)->fe_regs, (offset), (mask), (val))
+-#define airoha_fe_set(eth, offset, val)                               \
+-      airoha_rmw((eth)->fe_regs, (offset), 0, (val))
+-#define airoha_fe_clear(eth, offset, val)                     \
+-      airoha_rmw((eth)->fe_regs, (offset), (val), 0)
+-
+-#define airoha_qdma_rr(qdma, offset)                          \
+-      airoha_rr((qdma)->regs, (offset))
+-#define airoha_qdma_wr(qdma, offset, val)                     \
+-      airoha_wr((qdma)->regs, (offset), (val))
+-#define airoha_qdma_rmw(qdma, offset, mask, val)              \
+-      airoha_rmw((qdma)->regs, (offset), (mask), (val))
+-#define airoha_qdma_set(qdma, offset, val)                    \
+-      airoha_rmw((qdma)->regs, (offset), 0, (val))
+-#define airoha_qdma_clear(qdma, offset, val)                  \
+-      airoha_rmw((qdma)->regs, (offset), (val), 0)
+-
+-static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index,
+-                                  u32 clear, u32 set)
+-{
+-      unsigned long flags;
+-
+-      if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask)))
+-              return;
+-
+-      spin_lock_irqsave(&qdma->irq_lock, flags);
+-
+-      qdma->irqmask[index] &= ~clear;
+-      qdma->irqmask[index] |= set;
+-      airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]);
+-      /* Read irq_enable register in order to guarantee the update above
+-       * completes in the spinlock critical section.
+-       */
+-      airoha_qdma_rr(qdma, REG_INT_ENABLE(index));
+-
+-      spin_unlock_irqrestore(&qdma->irq_lock, flags);
+-}
+-
+-static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index,
+-                                 u32 mask)
+-{
+-      airoha_qdma_set_irqmask(qdma, index, 0, mask);
+-}
+-
+-static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index,
+-                                  u32 mask)
+-{
+-      airoha_qdma_set_irqmask(qdma, index, mask, 0);
+-}
+-
+-static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port)
+-{
+-      /* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
+-       * GDM{2,3,4} can be used as wan port connected to an external
+-       * phy module.
+-       */
+-      return port->id == 1;
+-}
+-
+-static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr)
+-{
+-      struct airoha_eth *eth = port->qdma->eth;
+-      u32 val, reg;
+-
+-      reg = airhoa_is_lan_gdm_port(port) ? REG_FE_LAN_MAC_H
+-                                         : REG_FE_WAN_MAC_H;
+-      val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
+-      airoha_fe_wr(eth, reg, val);
+-
+-      val = (addr[3] << 16) | (addr[4] << 8) | addr[5];
+-      airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
+-      airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
+-}
+-
+-static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
+-                                      u32 val)
+-{
+-      airoha_fe_rmw(eth, addr, GDM_OCFQ_MASK,
+-                    FIELD_PREP(GDM_OCFQ_MASK, val));
+-      airoha_fe_rmw(eth, addr, GDM_MCFQ_MASK,
+-                    FIELD_PREP(GDM_MCFQ_MASK, val));
+-      airoha_fe_rmw(eth, addr, GDM_BCFQ_MASK,
+-                    FIELD_PREP(GDM_BCFQ_MASK, val));
+-      airoha_fe_rmw(eth, addr, GDM_UCFQ_MASK,
+-                    FIELD_PREP(GDM_UCFQ_MASK, val));
+-}
+-
+-static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable)
+-{
+-      u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP;
+-      u32 vip_port, cfg_addr;
+-
+-      switch (port) {
+-      case XSI_PCIE0_PORT:
+-              vip_port = XSI_PCIE0_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(3);
+-              break;
+-      case XSI_PCIE1_PORT:
+-              vip_port = XSI_PCIE1_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(3);
+-              break;
+-      case XSI_USB_PORT:
+-              vip_port = XSI_USB_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(4);
+-              break;
+-      case XSI_ETH_PORT:
+-              vip_port = XSI_ETH_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(4);
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      if (enable) {
+-              airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
+-              airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
+-      } else {
+-              airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, vip_port);
+-              airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port);
+-      }
+-
+-      airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val);
+-
+-      return 0;
+-}
+-
+-static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable)
+-{
+-      const int port_list[] = {
+-              XSI_PCIE0_PORT,
+-              XSI_PCIE1_PORT,
+-              XSI_USB_PORT,
+-              XSI_ETH_PORT
+-      };
+-      int i, err;
+-
+-      for (i = 0; i < ARRAY_SIZE(port_list); i++) {
+-              err = airoha_set_gdm_port(eth, port_list[i], enable);
+-              if (err)
+-                      goto error;
+-      }
+-
+-      return 0;
+-
+-error:
+-      for (i--; i >= 0; i--)
+-              airoha_set_gdm_port(eth, port_list[i], false);
+-
+-      return err;
+-}
+-
+-static void airoha_fe_maccr_init(struct airoha_eth *eth)
+-{
+-      int p;
+-
+-      for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) {
+-              airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
+-                            GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM |
+-                            GDM_DROP_CRC_ERR);
+-              airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p),
+-                                          FE_PSE_PORT_CDM1);
+-              airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
+-                            GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+-                            FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+-                            FIELD_PREP(GDM_LONG_LEN_MASK, 4004));
+-      }
+-
+-      airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK,
+-                    FIELD_PREP(CDM1_VLAN_MASK, 0x8100));
+-
+-      airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD);
+-}
+-
+-static void airoha_fe_vip_setup(struct airoha_eth *eth)
+-{
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(3), ETH_P_PPP_DISC);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(3), PATN_FCPU_EN_MASK | PATN_EN_MASK);
+-
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(4), PPP_LCP);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(4),
+-                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
+-                   PATN_EN_MASK);
+-
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(6), PPP_IPCP);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(6),
+-                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
+-                   PATN_EN_MASK);
+-
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(7), PPP_CHAP);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(7),
+-                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
+-                   PATN_EN_MASK);
+-
+-      /* BOOTP (0x43) */
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(8), 0x43);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(8),
+-                   PATN_FCPU_EN_MASK | PATN_SP_EN_MASK |
+-                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
+-
+-      /* BOOTP (0x44) */
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(9), 0x44);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(9),
+-                   PATN_FCPU_EN_MASK | PATN_SP_EN_MASK |
+-                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
+-
+-      /* ISAKMP */
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(10), 0x1f401f4);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(10),
+-                   PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK |
+-                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
+-
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(11), PPP_IPV6CP);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(11),
+-                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
+-                   PATN_EN_MASK);
+-
+-      /* DHCPv6 */
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(12), 0x2220223);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(12),
+-                   PATN_FCPU_EN_MASK | PATN_DP_EN_MASK | PATN_SP_EN_MASK |
+-                   FIELD_PREP(PATN_TYPE_MASK, 4) | PATN_EN_MASK);
+-
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(19), PPP_PAP);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(19),
+-                   PATN_FCPU_EN_MASK | FIELD_PREP(PATN_TYPE_MASK, 1) |
+-                   PATN_EN_MASK);
+-
+-      /* ETH->ETH_P_1905 (0x893a) */
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(20), 0x893a);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(20),
+-                   PATN_FCPU_EN_MASK | PATN_EN_MASK);
+-
+-      airoha_fe_wr(eth, REG_FE_VIP_PATN(21), ETH_P_LLDP);
+-      airoha_fe_wr(eth, REG_FE_VIP_EN(21),
+-                   PATN_FCPU_EN_MASK | PATN_EN_MASK);
+-}
+-
+-static u32 airoha_fe_get_pse_queue_rsv_pages(struct airoha_eth *eth,
+-                                           u32 port, u32 queue)
+-{
+-      u32 val;
+-
+-      airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
+-                    PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK,
+-                    FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
+-                    FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue));
+-      val = airoha_fe_rr(eth, REG_FE_PSE_QUEUE_CFG_VAL);
+-
+-      return FIELD_GET(PSE_CFG_OQ_RSV_MASK, val);
+-}
+-
+-static void airoha_fe_set_pse_queue_rsv_pages(struct airoha_eth *eth,
+-                                            u32 port, u32 queue, u32 val)
+-{
+-      airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_VAL, PSE_CFG_OQ_RSV_MASK,
+-                    FIELD_PREP(PSE_CFG_OQ_RSV_MASK, val));
+-      airoha_fe_rmw(eth, REG_FE_PSE_QUEUE_CFG_WR,
+-                    PSE_CFG_PORT_ID_MASK | PSE_CFG_QUEUE_ID_MASK |
+-                    PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK,
+-                    FIELD_PREP(PSE_CFG_PORT_ID_MASK, port) |
+-                    FIELD_PREP(PSE_CFG_QUEUE_ID_MASK, queue) |
+-                    PSE_CFG_WR_EN_MASK | PSE_CFG_OQRSV_SEL_MASK);
+-}
+-
+-static u32 airoha_fe_get_pse_all_rsv(struct airoha_eth *eth)
+-{
+-      u32 val = airoha_fe_rr(eth, REG_FE_PSE_BUF_SET);
+-
+-      return FIELD_GET(PSE_ALLRSV_MASK, val);
+-}
+-
+-static int airoha_fe_set_pse_oq_rsv(struct airoha_eth *eth,
+-                                  u32 port, u32 queue, u32 val)
+-{
+-      u32 orig_val = airoha_fe_get_pse_queue_rsv_pages(eth, port, queue);
+-      u32 tmp, all_rsv, fq_limit;
+-
+-      airoha_fe_set_pse_queue_rsv_pages(eth, port, queue, val);
+-
+-      /* modify all rsv */
+-      all_rsv = airoha_fe_get_pse_all_rsv(eth);
+-      all_rsv += (val - orig_val);
+-      airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET, PSE_ALLRSV_MASK,
+-                    FIELD_PREP(PSE_ALLRSV_MASK, all_rsv));
+-
+-      /* modify hthd */
+-      tmp = airoha_fe_rr(eth, PSE_FQ_CFG);
+-      fq_limit = FIELD_GET(PSE_FQ_LIMIT_MASK, tmp);
+-      tmp = fq_limit - all_rsv - 0x20;
+-      airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
+-                    PSE_SHARE_USED_HTHD_MASK,
+-                    FIELD_PREP(PSE_SHARE_USED_HTHD_MASK, tmp));
+-
+-      tmp = fq_limit - all_rsv - 0x100;
+-      airoha_fe_rmw(eth, REG_PSE_SHARE_USED_THD,
+-                    PSE_SHARE_USED_MTHD_MASK,
+-                    FIELD_PREP(PSE_SHARE_USED_MTHD_MASK, tmp));
+-      tmp = (3 * tmp) >> 2;
+-      airoha_fe_rmw(eth, REG_FE_PSE_BUF_SET,
+-                    PSE_SHARE_USED_LTHD_MASK,
+-                    FIELD_PREP(PSE_SHARE_USED_LTHD_MASK, tmp));
+-
+-      return 0;
+-}
+-
+-static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
+-{
+-      const u32 pse_port_num_queues[] = {
+-              [FE_PSE_PORT_CDM1] = 6,
+-              [FE_PSE_PORT_GDM1] = 6,
+-              [FE_PSE_PORT_GDM2] = 32,
+-              [FE_PSE_PORT_GDM3] = 6,
+-              [FE_PSE_PORT_PPE1] = 4,
+-              [FE_PSE_PORT_CDM2] = 6,
+-              [FE_PSE_PORT_CDM3] = 8,
+-              [FE_PSE_PORT_CDM4] = 10,
+-              [FE_PSE_PORT_PPE2] = 4,
+-              [FE_PSE_PORT_GDM4] = 2,
+-              [FE_PSE_PORT_CDM5] = 2,
+-      };
+-      u32 all_rsv;
+-      int q;
+-
+-      all_rsv = airoha_fe_get_pse_all_rsv(eth);
+-      /* hw misses PPE2 oq rsv */
+-      all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2];
+-      airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv);
+-
+-      /* CMD1 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM1]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM1, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-      /* GMD1 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM1]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM1, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-      /* GMD2 */
+-      for (q = 6; q < pse_port_num_queues[FE_PSE_PORT_GDM2]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM2, q, 0);
+-      /* GMD3 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM3]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM3, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-      /* PPE1 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE1]; q++) {
+-              if (q < pse_port_num_queues[FE_PSE_PORT_PPE1])
+-                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q,
+-                                               PSE_QUEUE_RSV_PAGES);
+-              else
+-                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE1, q, 0);
+-      }
+-      /* CDM2 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM2]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM2, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-      /* CDM3 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM3] - 1; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM3, q, 0);
+-      /* CDM4 */
+-      for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-      /* PPE2 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) {
+-              if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2)
+-                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q,
+-                                               PSE_QUEUE_RSV_PAGES);
+-              else
+-                      airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0);
+-      }
+-      /* GMD4 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_GDM4, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-      /* CDM5 */
+-      for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_CDM5]; q++)
+-              airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM5, q,
+-                                       PSE_QUEUE_RSV_PAGES);
+-}
+-
+-static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth)
+-{
+-      int i;
+-
+-      for (i = 0; i < AIROHA_FE_MC_MAX_VLAN_TABLE; i++) {
+-              int err, j;
+-              u32 val;
+-
+-              airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0);
+-
+-              val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) |
+-                    MC_VLAN_CFG_TABLE_SEL_MASK | MC_VLAN_CFG_RW_MASK;
+-              airoha_fe_wr(eth, REG_MC_VLAN_CFG, val);
+-              err = read_poll_timeout(airoha_fe_rr, val,
+-                                      val & MC_VLAN_CFG_CMD_DONE_MASK,
+-                                      USEC_PER_MSEC, 5 * USEC_PER_MSEC,
+-                                      false, eth, REG_MC_VLAN_CFG);
+-              if (err)
+-                      return err;
+-
+-              for (j = 0; j < AIROHA_FE_MC_MAX_VLAN_PORT; j++) {
+-                      airoha_fe_wr(eth, REG_MC_VLAN_DATA, 0x0);
+-
+-                      val = FIELD_PREP(MC_VLAN_CFG_TABLE_ID_MASK, i) |
+-                            FIELD_PREP(MC_VLAN_CFG_PORT_ID_MASK, j) |
+-                            MC_VLAN_CFG_RW_MASK;
+-                      airoha_fe_wr(eth, REG_MC_VLAN_CFG, val);
+-                      err = read_poll_timeout(airoha_fe_rr, val,
+-                                              val & MC_VLAN_CFG_CMD_DONE_MASK,
+-                                              USEC_PER_MSEC,
+-                                              5 * USEC_PER_MSEC, false, eth,
+-                                              REG_MC_VLAN_CFG);
+-                      if (err)
+-                              return err;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth)
+-{
+-      /* CDM1_CRSN_QSEL */
+-      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2),
+-                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_22),
+-                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22),
+-                               CDM_CRSN_QSEL_Q1));
+-      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2),
+-                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_08),
+-                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08),
+-                               CDM_CRSN_QSEL_Q1));
+-      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2),
+-                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_21),
+-                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21),
+-                               CDM_CRSN_QSEL_Q1));
+-      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2),
+-                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_24),
+-                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24),
+-                               CDM_CRSN_QSEL_Q6));
+-      airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2),
+-                    CDM1_CRSN_QSEL_REASON_MASK(CRSN_25),
+-                    FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25),
+-                               CDM_CRSN_QSEL_Q1));
+-      /* CDM2_CRSN_QSEL */
+-      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2),
+-                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_08),
+-                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08),
+-                               CDM_CRSN_QSEL_Q1));
+-      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2),
+-                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_21),
+-                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21),
+-                               CDM_CRSN_QSEL_Q1));
+-      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2),
+-                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_22),
+-                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22),
+-                               CDM_CRSN_QSEL_Q1));
+-      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2),
+-                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_24),
+-                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24),
+-                               CDM_CRSN_QSEL_Q6));
+-      airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2),
+-                    CDM2_CRSN_QSEL_REASON_MASK(CRSN_25),
+-                    FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25),
+-                               CDM_CRSN_QSEL_Q1));
+-}
+-
+-static int airoha_fe_init(struct airoha_eth *eth)
+-{
+-      airoha_fe_maccr_init(eth);
+-
+-      /* PSE IQ reserve */
+-      airoha_fe_rmw(eth, REG_PSE_IQ_REV1, PSE_IQ_RES1_P2_MASK,
+-                    FIELD_PREP(PSE_IQ_RES1_P2_MASK, 0x10));
+-      airoha_fe_rmw(eth, REG_PSE_IQ_REV2,
+-                    PSE_IQ_RES2_P5_MASK | PSE_IQ_RES2_P4_MASK,
+-                    FIELD_PREP(PSE_IQ_RES2_P5_MASK, 0x40) |
+-                    FIELD_PREP(PSE_IQ_RES2_P4_MASK, 0x34));
+-
+-      /* enable FE copy engine for MC/KA/DPI */
+-      airoha_fe_wr(eth, REG_FE_PCE_CFG,
+-                   PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK);
+-      /* set vip queue selection to ring 1 */
+-      airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK,
+-                    FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4));
+-      airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK,
+-                    FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4));
+-      /* set GDM4 source interface offset to 8 */
+-      airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET,
+-                    GDM4_SPORT_OFF2_MASK |
+-                    GDM4_SPORT_OFF1_MASK |
+-                    GDM4_SPORT_OFF0_MASK,
+-                    FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) |
+-                    FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) |
+-                    FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8));
+-
+-      /* set PSE Page as 128B */
+-      airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG,
+-                    FE_DMA_GLO_L2_SPACE_MASK | FE_DMA_GLO_PG_SZ_MASK,
+-                    FIELD_PREP(FE_DMA_GLO_L2_SPACE_MASK, 2) |
+-                    FE_DMA_GLO_PG_SZ_MASK);
+-      airoha_fe_wr(eth, REG_FE_RST_GLO_CFG,
+-                   FE_RST_CORE_MASK | FE_RST_GDM3_MBI_ARB_MASK |
+-                   FE_RST_GDM4_MBI_ARB_MASK);
+-      usleep_range(1000, 2000);
+-
+-      /* connect RxRing1 and RxRing15 to PSE Port0 OQ-1
+-       * connect other rings to PSE Port0 OQ-0
+-       */
+-      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP0, BIT(4));
+-      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP1, BIT(28));
+-      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP2, BIT(4));
+-      airoha_fe_wr(eth, REG_FE_CDM1_OQ_MAP3, BIT(28));
+-
+-      airoha_fe_vip_setup(eth);
+-      airoha_fe_pse_ports_init(eth);
+-
+-      airoha_fe_set(eth, REG_GDM_MISC_CFG,
+-                    GDM2_RDM_ACK_WAIT_PREF_MASK |
+-                    GDM2_CHN_VLD_MODE_MASK);
+-      airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK,
+-                    FIELD_PREP(CDM2_OAM_QSEL_MASK, 15));
+-
+-      /* init fragment and assemble Force Port */
+-      /* NPU Core-3, NPU Bridge Channel-3 */
+-      airoha_fe_rmw(eth, REG_IP_FRAG_FP,
+-                    IP_FRAGMENT_PORT_MASK | IP_FRAGMENT_NBQ_MASK,
+-                    FIELD_PREP(IP_FRAGMENT_PORT_MASK, 6) |
+-                    FIELD_PREP(IP_FRAGMENT_NBQ_MASK, 3));
+-      /* QDMA LAN, RX Ring-22 */
+-      airoha_fe_rmw(eth, REG_IP_FRAG_FP,
+-                    IP_ASSEMBLE_PORT_MASK | IP_ASSEMBLE_NBQ_MASK,
+-                    FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) |
+-                    FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22));
+-
+-      airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK);
+-      airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK);
+-
+-      airoha_fe_crsn_qsel_init(eth);
+-
+-      airoha_fe_clear(eth, REG_FE_CPORT_CFG, FE_CPORT_QUEUE_XFC_MASK);
+-      airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK);
+-
+-      /* default aging mode for mbi unlock issue */
+-      airoha_fe_rmw(eth, REG_GDM2_CHN_RLS,
+-                    MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK,
+-                    FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) |
+-                    FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3));
+-
+-      /* disable IFC by default */
+-      airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK);
+-
+-      /* enable 1:N vlan action, init vlan table */
+-      airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
+-
+-      return airoha_fe_mc_vlan_clear(eth);
+-}
+-
+-static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
+-{
+-      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];
+-      int nframes = 0;
+-
+-      while (q->queued < q->ndesc - 1) {
+-              struct airoha_queue_entry *e = &q->entry[q->head];
+-              struct airoha_qdma_desc *desc = &q->desc[q->head];
+-              struct page *page;
+-              int offset;
+-              u32 val;
+-
+-              page = page_pool_dev_alloc_frag(q->page_pool, &offset,
+-                                              q->buf_size);
+-              if (!page)
+-                      break;
+-
+-              q->head = (q->head + 1) % q->ndesc;
+-              q->queued++;
+-              nframes++;
+-
+-              e->buf = page_address(page) + offset;
+-              e->dma_addr = page_pool_get_dma_addr(page) + offset;
+-              e->dma_len = SKB_WITH_OVERHEAD(q->buf_size);
+-
+-              dma_sync_single_for_device(eth->dev, e->dma_addr, e->dma_len,
+-                                         dir);
+-
+-              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));
+-              val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, q->head);
+-              WRITE_ONCE(desc->data, cpu_to_le32(val));
+-              WRITE_ONCE(desc->msg0, 0);
+-              WRITE_ONCE(desc->msg1, 0);
+-              WRITE_ONCE(desc->msg2, 0);
+-              WRITE_ONCE(desc->msg3, 0);
+-
+-              airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid),
+-                              RX_RING_CPU_IDX_MASK,
+-                              FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
+-      }
+-
+-      return nframes;
+-}
+-
+-static int airoha_qdma_get_gdm_port(struct airoha_eth *eth,
+-                                  struct airoha_qdma_desc *desc)
+-{
+-      u32 port, sport, msg1 = le32_to_cpu(desc->msg1);
+-
+-      sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
+-      switch (sport) {
+-      case 0x10 ... 0x13:
+-              port = 0;
+-              break;
+-      case 0x2 ... 0x4:
+-              port = sport - 1;
+-              break;
+-      default:
+-              return -EINVAL;
+-      }
+-
+-      return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
+-}
+-
+-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];
+-      int done = 0;
+-
+-      while (done < budget) {
+-              struct airoha_queue_entry *e = &q->entry[q->tail];
+-              struct airoha_qdma_desc *desc = &q->desc[q->tail];
+-              dma_addr_t dma_addr = le32_to_cpu(desc->addr);
+-              u32 desc_ctrl = le32_to_cpu(desc->ctrl);
+-              struct sk_buff *skb;
+-              int len, p;
+-
+-              if (!(desc_ctrl & QDMA_DESC_DONE_MASK))
+-                      break;
+-
+-              if (!dma_addr)
+-                      break;
+-
+-              len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl);
+-              if (!len)
+-                      break;
+-
+-              q->tail = (q->tail + 1) % q->ndesc;
+-              q->queued--;
+-
+-              dma_sync_single_for_cpu(eth->dev, dma_addr,
+-                                      SKB_WITH_OVERHEAD(q->buf_size), dir);
+-
+-              p = airoha_qdma_get_gdm_port(eth, desc);
+-              if (p < 0 || !eth->ports[p]) {
+-                      page_pool_put_full_page(q->page_pool,
+-                                              virt_to_head_page(e->buf),
+-                                              true);
+-                      continue;
+-              }
+-
+-              skb = napi_build_skb(e->buf, q->buf_size);
+-              if (!skb) {
+-                      page_pool_put_full_page(q->page_pool,
+-                                              virt_to_head_page(e->buf),
+-                                              true);
+-                      break;
+-              }
+-
+-              skb_reserve(skb, 2);
+-              __skb_put(skb, len);
+-              skb_mark_for_recycle(skb);
+-              skb->dev = eth->ports[p]->dev;
+-              skb->protocol = eth_type_trans(skb, skb->dev);
+-              skb->ip_summed = CHECKSUM_UNNECESSARY;
+-              skb_record_rx_queue(skb, qid);
+-              napi_gro_receive(&q->napi, skb);
+-
+-              done++;
+-      }
+-      airoha_qdma_fill_rx_queue(q);
+-
+-      return done;
+-}
+-
+-static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget)
+-{
+-      struct airoha_queue *q = container_of(napi, struct airoha_queue, napi);
+-      int cur, done = 0;
+-
+-      do {
+-              cur = airoha_qdma_rx_process(q, budget - done);
+-              done += cur;
+-      } while (cur && done < budget);
+-
+-      if (done < budget && napi_complete(napi))
+-              airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1,
+-                                     RX_DONE_INT_MASK);
+-
+-      return done;
+-}
+-
+-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,
+-              .pool_size = 256,
+-              .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV |
+-                       PP_FLAG_PAGE_FRAG,
+-              .dma_dir = DMA_FROM_DEVICE,
+-              .max_len = PAGE_SIZE,
+-              .nid = NUMA_NO_NODE,
+-              .dev = qdma->eth->dev,
+-              .napi = &q->napi,
+-      };
+-      struct airoha_eth *eth = qdma->eth;
+-      int qid = q - &qdma->q_rx[0], thr;
+-      dma_addr_t dma_addr;
+-
+-      q->buf_size = PAGE_SIZE / 2;
+-      q->ndesc = ndesc;
+-      q->qdma = qdma;
+-
+-      q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
+-                              GFP_KERNEL);
+-      if (!q->entry)
+-              return -ENOMEM;
+-
+-      q->page_pool = page_pool_create(&pp_params);
+-      if (IS_ERR(q->page_pool)) {
+-              int err = PTR_ERR(q->page_pool);
+-
+-              q->page_pool = NULL;
+-              return err;
+-      }
+-
+-      q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
+-                                    &dma_addr, GFP_KERNEL);
+-      if (!q->desc)
+-              return -ENOMEM;
+-
+-      netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll);
+-
+-      airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr);
+-      airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid),
+-                      RX_RING_SIZE_MASK,
+-                      FIELD_PREP(RX_RING_SIZE_MASK, ndesc));
+-
+-      thr = clamp(ndesc >> 3, 1, 32);
+-      airoha_qdma_rmw(qdma, REG_RX_RING_SIZE(qid), RX_RING_THR_MASK,
+-                      FIELD_PREP(RX_RING_THR_MASK, thr));
+-      airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK,
+-                      FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head));
+-
+-      airoha_qdma_fill_rx_queue(q);
+-
+-      return 0;
+-}
+-
+-static void airoha_qdma_cleanup_rx_queue(struct airoha_queue *q)
+-{
+-      struct airoha_eth *eth = q->qdma->eth;
+-
+-      while (q->queued) {
+-              struct airoha_queue_entry *e = &q->entry[q->tail];
+-              struct page *page = virt_to_head_page(e->buf);
+-
+-              dma_sync_single_for_cpu(eth->dev, e->dma_addr, e->dma_len,
+-                                      page_pool_get_dma_dir(q->page_pool));
+-              page_pool_put_full_page(q->page_pool, page, false);
+-              q->tail = (q->tail + 1) % q->ndesc;
+-              q->queued--;
+-      }
+-}
+-
+-static int airoha_qdma_init_rx(struct airoha_qdma *qdma)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+-              int err;
+-
+-              if (!(RX_DONE_INT_MASK & BIT(i))) {
+-                      /* rx-queue not binded to irq */
+-                      continue;
+-              }
+-
+-              err = airoha_qdma_init_rx_queue(&qdma->q_rx[i], qdma,
+-                                              RX_DSCP_NUM(i));
+-              if (err)
+-                      return err;
+-      }
+-
+-      return 0;
+-}
+-
+-static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
+-{
+-      struct airoha_tx_irq_queue *irq_q;
+-      int id, done = 0, irq_queued;
+-      struct airoha_qdma *qdma;
+-      struct airoha_eth *eth;
+-      u32 status, head;
+-
+-      irq_q = container_of(napi, struct airoha_tx_irq_queue, napi);
+-      qdma = irq_q->qdma;
+-      id = irq_q - &qdma->q_tx_irq[0];
+-      eth = qdma->eth;
+-
+-      status = airoha_qdma_rr(qdma, REG_IRQ_STATUS(id));
+-      head = FIELD_GET(IRQ_HEAD_IDX_MASK, status);
+-      head = head % irq_q->size;
+-      irq_queued = FIELD_GET(IRQ_ENTRY_LEN_MASK, status);
+-
+-      while (irq_queued > 0 && done < budget) {
+-              u32 qid, val = irq_q->q[head];
+-              struct airoha_qdma_desc *desc;
+-              struct airoha_queue_entry *e;
+-              struct airoha_queue *q;
+-              u32 index, desc_ctrl;
+-              struct sk_buff *skb;
+-
+-              if (val == 0xff)
+-                      break;
+-
+-              irq_q->q[head] = 0xff; /* mark as done */
+-              head = (head + 1) % irq_q->size;
+-              irq_queued--;
+-              done++;
+-
+-              qid = FIELD_GET(IRQ_RING_IDX_MASK, val);
+-              if (qid >= ARRAY_SIZE(qdma->q_tx))
+-                      continue;
+-
+-              q = &qdma->q_tx[qid];
+-              if (!q->ndesc)
+-                      continue;
+-
+-              index = FIELD_GET(IRQ_DESC_IDX_MASK, val);
+-              if (index >= q->ndesc)
+-                      continue;
+-
+-              spin_lock_bh(&q->lock);
+-
+-              if (!q->queued)
+-                      goto unlock;
+-
+-              desc = &q->desc[index];
+-              desc_ctrl = le32_to_cpu(desc->ctrl);
+-
+-              if (!(desc_ctrl & QDMA_DESC_DONE_MASK) &&
+-                  !(desc_ctrl & QDMA_DESC_DROP_MASK))
+-                      goto unlock;
+-
+-              e = &q->entry[index];
+-              skb = e->skb;
+-
+-              dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
+-                               DMA_TO_DEVICE);
+-              memset(e, 0, sizeof(*e));
+-              WRITE_ONCE(desc->msg0, 0);
+-              WRITE_ONCE(desc->msg1, 0);
+-              q->queued--;
+-
+-              /* completion ring can report out-of-order indexes if hw QoS
+-               * is enabled and packets with different priority are queued
+-               * to same DMA ring. Take into account possible out-of-order
+-               * reports incrementing DMA ring tail pointer
+-               */
+-              while (q->tail != q->head && !q->entry[q->tail].dma_addr)
+-                      q->tail = (q->tail + 1) % q->ndesc;
+-
+-              if (skb) {
+-                      u16 queue = skb_get_queue_mapping(skb);
+-                      struct netdev_queue *txq;
+-
+-                      txq = netdev_get_tx_queue(skb->dev, queue);
+-                      netdev_tx_completed_queue(txq, 1, skb->len);
+-                      if (netif_tx_queue_stopped(txq) &&
+-                          q->ndesc - q->queued >= q->free_thr)
+-                              netif_tx_wake_queue(txq);
+-
+-                      dev_kfree_skb_any(skb);
+-              }
+-unlock:
+-              spin_unlock_bh(&q->lock);
+-      }
+-
+-      if (done) {
+-              int i, len = done >> 7;
+-
+-              for (i = 0; i < len; i++)
+-                      airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id),
+-                                      IRQ_CLEAR_LEN_MASK, 0x80);
+-              airoha_qdma_rmw(qdma, REG_IRQ_CLEAR_LEN(id),
+-                              IRQ_CLEAR_LEN_MASK, (done & 0x7f));
+-      }
+-
+-      if (done < budget && napi_complete(napi))
+-              airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0,
+-                                     TX_DONE_INT_MASK(id));
+-
+-      return done;
+-}
+-
+-static int airoha_qdma_init_tx_queue(struct airoha_queue *q,
+-                                   struct airoha_qdma *qdma, int size)
+-{
+-      struct airoha_eth *eth = qdma->eth;
+-      int i, qid = q - &qdma->q_tx[0];
+-      dma_addr_t dma_addr;
+-
+-      spin_lock_init(&q->lock);
+-      q->ndesc = size;
+-      q->qdma = qdma;
+-      q->free_thr = 1 + MAX_SKB_FRAGS;
+-
+-      q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
+-                              GFP_KERNEL);
+-      if (!q->entry)
+-              return -ENOMEM;
+-
+-      q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc),
+-                                    &dma_addr, GFP_KERNEL);
+-      if (!q->desc)
+-              return -ENOMEM;
+-
+-      for (i = 0; i < q->ndesc; i++) {
+-              u32 val;
+-
+-              val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1);
+-              WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val));
+-      }
+-
+-      /* xmit ring drop default setting */
+-      airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid),
+-                      TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK);
+-
+-      airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr);
+-      airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK,
+-                      FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
+-      airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK,
+-                      FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head));
+-
+-      return 0;
+-}
+-
+-static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q,
+-                                 struct airoha_qdma *qdma, int size)
+-{
+-      int id = irq_q - &qdma->q_tx_irq[0];
+-      struct airoha_eth *eth = qdma->eth;
+-      dma_addr_t dma_addr;
+-
+-      netif_napi_add_tx(eth->napi_dev, &irq_q->napi,
+-                        airoha_qdma_tx_napi_poll);
+-      irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32),
+-                                     &dma_addr, GFP_KERNEL);
+-      if (!irq_q->q)
+-              return -ENOMEM;
+-
+-      memset(irq_q->q, 0xff, size * sizeof(u32));
+-      irq_q->size = size;
+-      irq_q->qdma = qdma;
+-
+-      airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr);
+-      airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK,
+-                      FIELD_PREP(TX_IRQ_DEPTH_MASK, size));
+-      airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_THR_MASK,
+-                      FIELD_PREP(TX_IRQ_THR_MASK, 1));
+-
+-      return 0;
+-}
+-
+-static int airoha_qdma_init_tx(struct airoha_qdma *qdma)
+-{
+-      int i, err;
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) {
+-              err = airoha_qdma_tx_irq_init(&qdma->q_tx_irq[i], qdma,
+-                                            IRQ_QUEUE_LEN(i));
+-              if (err)
+-                      return err;
+-      }
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
+-              err = airoha_qdma_init_tx_queue(&qdma->q_tx[i], qdma,
+-                                              TX_DSCP_NUM);
+-              if (err)
+-                      return err;
+-      }
+-
+-      return 0;
+-}
+-
+-static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
+-{
+-      struct airoha_eth *eth = q->qdma->eth;
+-
+-      spin_lock_bh(&q->lock);
+-      while (q->queued) {
+-              struct airoha_queue_entry *e = &q->entry[q->tail];
+-
+-              dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
+-                               DMA_TO_DEVICE);
+-              dev_kfree_skb_any(e->skb);
+-              e->skb = NULL;
+-
+-              q->tail = (q->tail + 1) % q->ndesc;
+-              q->queued--;
+-      }
+-      spin_unlock_bh(&q->lock);
+-}
+-
+-static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma)
+-{
+-      struct airoha_eth *eth = qdma->eth;
+-      dma_addr_t dma_addr;
+-      u32 status;
+-      int size;
+-
+-      size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc);
+-      qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr,
+-                                            GFP_KERNEL);
+-      if (!qdma->hfwd.desc)
+-              return -ENOMEM;
+-
+-      airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr);
+-
+-      size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM;
+-      qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr,
+-                                         GFP_KERNEL);
+-      if (!qdma->hfwd.q)
+-              return -ENOMEM;
+-
+-      airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr);
+-
+-      airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG,
+-                      HW_FWD_DSCP_PAYLOAD_SIZE_MASK,
+-                      FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0));
+-      airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK,
+-                      FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128));
+-      airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG,
+-                      LMGR_INIT_START | LMGR_SRAM_MODE_MASK |
+-                      HW_FWD_DESC_NUM_MASK,
+-                      FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) |
+-                      LMGR_INIT_START);
+-
+-      return read_poll_timeout(airoha_qdma_rr, status,
+-                               !(status & LMGR_INIT_START), USEC_PER_MSEC,
+-                               30 * USEC_PER_MSEC, true, qdma,
+-                               REG_LMGR_INIT_CFG);
+-}
+-
+-static void airoha_qdma_init_qos(struct airoha_qdma *qdma)
+-{
+-      airoha_qdma_clear(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_SCALE_MASK);
+-      airoha_qdma_set(qdma, REG_TXWRR_MODE_CFG, TWRR_WEIGHT_BASE_MASK);
+-
+-      airoha_qdma_clear(qdma, REG_PSE_BUF_USAGE_CFG,
+-                        PSE_BUF_ESTIMATE_EN_MASK);
+-
+-      airoha_qdma_set(qdma, REG_EGRESS_RATE_METER_CFG,
+-                      EGRESS_RATE_METER_EN_MASK |
+-                      EGRESS_RATE_METER_EQ_RATE_EN_MASK);
+-      /* 2047us x 31 = 63.457ms */
+-      airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG,
+-                      EGRESS_RATE_METER_WINDOW_SZ_MASK,
+-                      FIELD_PREP(EGRESS_RATE_METER_WINDOW_SZ_MASK, 0x1f));
+-      airoha_qdma_rmw(qdma, REG_EGRESS_RATE_METER_CFG,
+-                      EGRESS_RATE_METER_TIMESLICE_MASK,
+-                      FIELD_PREP(EGRESS_RATE_METER_TIMESLICE_MASK, 0x7ff));
+-
+-      /* ratelimit init */
+-      airoha_qdma_set(qdma, REG_GLB_TRTCM_CFG, GLB_TRTCM_EN_MASK);
+-      /* fast-tick 25us */
+-      airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_FAST_TICK_MASK,
+-                      FIELD_PREP(GLB_FAST_TICK_MASK, 25));
+-      airoha_qdma_rmw(qdma, REG_GLB_TRTCM_CFG, GLB_SLOW_TICK_RATIO_MASK,
+-                      FIELD_PREP(GLB_SLOW_TICK_RATIO_MASK, 40));
+-
+-      airoha_qdma_set(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_TRTCM_EN_MASK);
+-      airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG, EGRESS_FAST_TICK_MASK,
+-                      FIELD_PREP(EGRESS_FAST_TICK_MASK, 25));
+-      airoha_qdma_rmw(qdma, REG_EGRESS_TRTCM_CFG,
+-                      EGRESS_SLOW_TICK_RATIO_MASK,
+-                      FIELD_PREP(EGRESS_SLOW_TICK_RATIO_MASK, 40));
+-
+-      airoha_qdma_set(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_TRTCM_EN_MASK);
+-      airoha_qdma_clear(qdma, REG_INGRESS_TRTCM_CFG,
+-                        INGRESS_TRTCM_MODE_MASK);
+-      airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG, INGRESS_FAST_TICK_MASK,
+-                      FIELD_PREP(INGRESS_FAST_TICK_MASK, 125));
+-      airoha_qdma_rmw(qdma, REG_INGRESS_TRTCM_CFG,
+-                      INGRESS_SLOW_TICK_RATIO_MASK,
+-                      FIELD_PREP(INGRESS_SLOW_TICK_RATIO_MASK, 8));
+-
+-      airoha_qdma_set(qdma, REG_SLA_TRTCM_CFG, SLA_TRTCM_EN_MASK);
+-      airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_FAST_TICK_MASK,
+-                      FIELD_PREP(SLA_FAST_TICK_MASK, 25));
+-      airoha_qdma_rmw(qdma, REG_SLA_TRTCM_CFG, SLA_SLOW_TICK_RATIO_MASK,
+-                      FIELD_PREP(SLA_SLOW_TICK_RATIO_MASK, 40));
+-}
+-
+-static void airoha_qdma_init_qos_stats(struct airoha_qdma *qdma)
+-{
+-      int i;
+-
+-      for (i = 0; i < AIROHA_NUM_QOS_CHANNELS; i++) {
+-              /* Tx-cpu transferred count */
+-              airoha_qdma_wr(qdma, REG_CNTR_VAL(i << 1), 0);
+-              airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1),
+-                             CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK |
+-                             CNTR_ALL_DSCP_RING_EN_MASK |
+-                             FIELD_PREP(CNTR_CHAN_MASK, i));
+-              /* Tx-fwd transferred count */
+-              airoha_qdma_wr(qdma, REG_CNTR_VAL((i << 1) + 1), 0);
+-              airoha_qdma_wr(qdma, REG_CNTR_CFG(i << 1),
+-                             CNTR_EN_MASK | CNTR_ALL_QUEUE_EN_MASK |
+-                             CNTR_ALL_DSCP_RING_EN_MASK |
+-                             FIELD_PREP(CNTR_SRC_MASK, 1) |
+-                             FIELD_PREP(CNTR_CHAN_MASK, i));
+-      }
+-}
+-
+-static int airoha_qdma_hw_init(struct airoha_qdma *qdma)
+-{
+-      int i;
+-
+-      /* clear pending irqs */
+-      for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++)
+-              airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff);
+-
+-      /* setup irqs */
+-      airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK);
+-      airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK);
+-      airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK);
+-
+-      /* setup irq binding */
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
+-              if (!qdma->q_tx[i].ndesc)
+-                      continue;
+-
+-              if (TX_RING_IRQ_BLOCKING_MAP_MASK & BIT(i))
+-                      airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(i),
+-                                      TX_RING_IRQ_BLOCKING_CFG_MASK);
+-              else
+-                      airoha_qdma_clear(qdma, REG_TX_RING_BLOCKING(i),
+-                                        TX_RING_IRQ_BLOCKING_CFG_MASK);
+-      }
+-
+-      airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG,
+-                     GLOBAL_CFG_RX_2B_OFFSET_MASK |
+-                     FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) |
+-                     GLOBAL_CFG_CPU_TXR_RR_MASK |
+-                     GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK |
+-                     GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK |
+-                     GLOBAL_CFG_MULTICAST_EN_MASK |
+-                     GLOBAL_CFG_IRQ0_EN_MASK | GLOBAL_CFG_IRQ1_EN_MASK |
+-                     GLOBAL_CFG_TX_WB_DONE_MASK |
+-                     FIELD_PREP(GLOBAL_CFG_MAX_ISSUE_NUM_MASK, 2));
+-
+-      airoha_qdma_init_qos(qdma);
+-
+-      /* disable qdma rx delay interrupt */
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+-              if (!qdma->q_rx[i].ndesc)
+-                      continue;
+-
+-              airoha_qdma_clear(qdma, REG_RX_DELAY_INT_IDX(i),
+-                                RX_DELAY_INT_MASK);
+-      }
+-
+-      airoha_qdma_set(qdma, REG_TXQ_CNGST_CFG,
+-                      TXQ_CNGST_DROP_EN | TXQ_CNGST_DEI_DROP_EN);
+-      airoha_qdma_init_qos_stats(qdma);
+-
+-      return 0;
+-}
+-
+-static irqreturn_t airoha_irq_handler(int irq, void *dev_instance)
+-{
+-      struct airoha_qdma *qdma = dev_instance;
+-      u32 intr[ARRAY_SIZE(qdma->irqmask)];
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) {
+-              intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i));
+-              intr[i] &= qdma->irqmask[i];
+-              airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]);
+-      }
+-
+-      if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state))
+-              return IRQ_NONE;
+-
+-      if (intr[1] & RX_DONE_INT_MASK) {
+-              airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1,
+-                                      RX_DONE_INT_MASK);
+-
+-              for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+-                      if (!qdma->q_rx[i].ndesc)
+-                              continue;
+-
+-                      if (intr[1] & BIT(i))
+-                              napi_schedule(&qdma->q_rx[i].napi);
+-              }
+-      }
+-
+-      if (intr[0] & INT_TX_MASK) {
+-              for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) {
+-                      if (!(intr[0] & TX_DONE_INT_MASK(i)))
+-                              continue;
+-
+-                      airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0,
+-                                              TX_DONE_INT_MASK(i));
+-                      napi_schedule(&qdma->q_tx_irq[i].napi);
+-              }
+-      }
+-
+-      return IRQ_HANDLED;
+-}
+-
+-static int airoha_qdma_init(struct platform_device *pdev,
+-                          struct airoha_eth *eth,
+-                          struct airoha_qdma *qdma)
+-{
+-      int err, id = qdma - &eth->qdma[0];
+-      const char *res;
+-
+-      spin_lock_init(&qdma->irq_lock);
+-      qdma->eth = eth;
+-
+-      res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id);
+-      if (!res)
+-              return -ENOMEM;
+-
+-      qdma->regs = devm_platform_ioremap_resource_byname(pdev, res);
+-      if (IS_ERR(qdma->regs))
+-              return dev_err_probe(eth->dev, PTR_ERR(qdma->regs),
+-                                   "failed to iomap qdma%d regs\n", id);
+-
+-      qdma->irq = platform_get_irq(pdev, 4 * id);
+-      if (qdma->irq < 0)
+-              return qdma->irq;
+-
+-      err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler,
+-                             IRQF_SHARED, KBUILD_MODNAME, qdma);
+-      if (err)
+-              return err;
+-
+-      err = airoha_qdma_init_rx(qdma);
+-      if (err)
+-              return err;
+-
+-      err = airoha_qdma_init_tx(qdma);
+-      if (err)
+-              return err;
+-
+-      err = airoha_qdma_init_hfwd_queues(qdma);
+-      if (err)
+-              return err;
+-
+-      return airoha_qdma_hw_init(qdma);
+-}
+-
+-static int airoha_hw_init(struct platform_device *pdev,
+-                        struct airoha_eth *eth)
+-{
+-      int err, i;
+-
+-      /* disable xsi */
+-      err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts),
+-                                      eth->xsi_rsts);
+-      if (err)
+-              return err;
+-
+-      err = reset_control_bulk_assert(ARRAY_SIZE(eth->rsts), eth->rsts);
+-      if (err)
+-              return err;
+-
+-      msleep(20);
+-      err = reset_control_bulk_deassert(ARRAY_SIZE(eth->rsts), eth->rsts);
+-      if (err)
+-              return err;
+-
+-      msleep(20);
+-      err = airoha_fe_init(eth);
+-      if (err)
+-              return err;
+-
+-      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
+-              err = airoha_qdma_init(pdev, eth, &eth->qdma[i]);
+-              if (err)
+-                      return err;
+-      }
+-
+-      set_bit(DEV_STATE_INITIALIZED, &eth->state);
+-
+-      return 0;
+-}
+-
+-static void airoha_hw_cleanup(struct airoha_qdma *qdma)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+-              if (!qdma->q_rx[i].ndesc)
+-                      continue;
+-
+-              netif_napi_del(&qdma->q_rx[i].napi);
+-              airoha_qdma_cleanup_rx_queue(&qdma->q_rx[i]);
+-              if (qdma->q_rx[i].page_pool)
+-                      page_pool_destroy(qdma->q_rx[i].page_pool);
+-      }
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
+-              netif_napi_del(&qdma->q_tx_irq[i].napi);
+-
+-      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]);
+-      }
+-}
+-
+-static void airoha_qdma_start_napi(struct airoha_qdma *qdma)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
+-              napi_enable(&qdma->q_tx_irq[i].napi);
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+-              if (!qdma->q_rx[i].ndesc)
+-                      continue;
+-
+-              napi_enable(&qdma->q_rx[i].napi);
+-      }
+-}
+-
+-static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++)
+-              napi_disable(&qdma->q_tx_irq[i].napi);
+-
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
+-              if (!qdma->q_rx[i].ndesc)
+-                      continue;
+-
+-              napi_disable(&qdma->q_rx[i].napi);
+-      }
+-}
+-
+-static void airoha_update_hw_stats(struct airoha_gdm_port *port)
+-{
+-      struct airoha_eth *eth = port->qdma->eth;
+-      u32 val, i = 0;
+-
+-      spin_lock(&port->stats.lock);
+-      u64_stats_update_begin(&port->stats.syncp);
+-
+-      /* TX */
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
+-      port->stats.tx_ok_pkts += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
+-      port->stats.tx_ok_pkts += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
+-      port->stats.tx_ok_bytes += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
+-      port->stats.tx_ok_bytes += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
+-      port->stats.tx_drops += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
+-      port->stats.tx_broadcast += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
+-      port->stats.tx_multicast += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
+-      port->stats.tx_len[i] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
+-      port->stats.tx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
+-      port->stats.tx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
+-      port->stats.tx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
+-      port->stats.tx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
+-      port->stats.tx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
+-      port->stats.tx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
+-      port->stats.tx_len[i++] += val;
+-
+-      /* RX */
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
+-      port->stats.rx_ok_pkts += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
+-      port->stats.rx_ok_pkts += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
+-      port->stats.rx_ok_bytes += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
+-      port->stats.rx_ok_bytes += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
+-      port->stats.rx_drops += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
+-      port->stats.rx_broadcast += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
+-      port->stats.rx_multicast += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
+-      port->stats.rx_errors += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
+-      port->stats.rx_crc_error += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
+-      port->stats.rx_over_errors += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
+-      port->stats.rx_fragment += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
+-      port->stats.rx_jabber += val;
+-
+-      i = 0;
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
+-      port->stats.rx_len[i] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
+-      port->stats.rx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
+-      port->stats.rx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
+-      port->stats.rx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
+-      port->stats.rx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
+-      port->stats.rx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
+-      port->stats.rx_len[i] += ((u64)val << 32);
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
+-      port->stats.rx_len[i++] += val;
+-
+-      /* reset mib counters */
+-      airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
+-                    FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
+-
+-      u64_stats_update_end(&port->stats.syncp);
+-      spin_unlock(&port->stats.lock);
+-}
+-
+-static int airoha_dev_open(struct net_device *dev)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      struct airoha_qdma *qdma = port->qdma;
+-      int err;
+-
+-      netif_tx_start_all_queues(dev);
+-      err = airoha_set_gdm_ports(qdma->eth, true);
+-      if (err)
+-              return err;
+-
+-      if (netdev_uses_dsa(dev))
+-              airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+-                            GDM_STAG_EN_MASK);
+-      else
+-              airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
+-                              GDM_STAG_EN_MASK);
+-
+-      airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
+-                      GLOBAL_CFG_TX_DMA_EN_MASK |
+-                      GLOBAL_CFG_RX_DMA_EN_MASK);
+-
+-      return 0;
+-}
+-
+-static int airoha_dev_stop(struct net_device *dev)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      struct airoha_qdma *qdma = port->qdma;
+-      int i, err;
+-
+-      netif_tx_disable(dev);
+-      err = airoha_set_gdm_ports(qdma->eth, false);
+-      if (err)
+-              return err;
+-
+-      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]);
+-              netdev_tx_reset_subqueue(dev, i);
+-      }
+-
+-      return 0;
+-}
+-
+-static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      int err;
+-
+-      err = eth_mac_addr(dev, p);
+-      if (err)
+-              return err;
+-
+-      airoha_set_macaddr(port, dev->dev_addr);
+-
+-      return 0;
+-}
+-
+-static int airoha_dev_init(struct net_device *dev)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-
+-      airoha_set_macaddr(port, dev->dev_addr);
+-
+-      return 0;
+-}
+-
+-static void airoha_dev_get_stats64(struct net_device *dev,
+-                                 struct rtnl_link_stats64 *storage)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      unsigned int start;
+-
+-      airoha_update_hw_stats(port);
+-      do {
+-              start = u64_stats_fetch_begin(&port->stats.syncp);
+-              storage->rx_packets = port->stats.rx_ok_pkts;
+-              storage->tx_packets = port->stats.tx_ok_pkts;
+-              storage->rx_bytes = port->stats.rx_ok_bytes;
+-              storage->tx_bytes = port->stats.tx_ok_bytes;
+-              storage->multicast = port->stats.rx_multicast;
+-              storage->rx_errors = port->stats.rx_errors;
+-              storage->rx_dropped = port->stats.rx_drops;
+-              storage->tx_dropped = port->stats.tx_drops;
+-              storage->rx_crc_errors = port->stats.rx_crc_error;
+-              storage->rx_over_errors = port->stats.rx_over_errors;
+-      } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+-}
+-
+-static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
+-                                 struct net_device *sb_dev)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      int queue, channel;
+-
+-      /* For dsa device select QoS channel according to the dsa user port
+-       * index, rely on port id otherwise. Select QoS queue based on the
+-       * skb priority.
+-       */
+-      channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
+-      channel = channel % AIROHA_NUM_QOS_CHANNELS;
+-      queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
+-      queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
+-
+-      return queue < dev->num_tx_queues ? queue : 0;
+-}
+-
+-static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
+-                                 struct net_device *dev)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags;
+-      u32 msg0, msg1, len = skb_headlen(skb);
+-      struct airoha_qdma *qdma = port->qdma;
+-      struct netdev_queue *txq;
+-      struct airoha_queue *q;
+-      void *data = skb->data;
+-      int i, qid;
+-      u16 index;
+-      u8 fport;
+-
+-      qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx);
+-      msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
+-                        qid / AIROHA_NUM_QOS_QUEUES) |
+-             FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK,
+-                        qid % AIROHA_NUM_QOS_QUEUES);
+-      if (skb->ip_summed == CHECKSUM_PARTIAL)
+-              msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) |
+-                      FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) |
+-                      FIELD_PREP(QDMA_ETH_TXMSG_ICO_MASK, 1);
+-
+-      /* TSO: fill MSS info in tcp checksum field */
+-      if (skb_is_gso(skb)) {
+-              if (skb_cow_head(skb, 0))
+-                      goto error;
+-
+-              if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 |
+-                                               SKB_GSO_TCPV6)) {
+-                      __be16 csum = cpu_to_be16(skb_shinfo(skb)->gso_size);
+-
+-                      tcp_hdr(skb)->check = (__force __sum16)csum;
+-                      msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TSO_MASK, 1);
+-              }
+-      }
+-
+-      fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
+-      msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
+-             FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
+-
+-      q = &qdma->q_tx[qid];
+-      if (WARN_ON_ONCE(!q->ndesc))
+-              goto error;
+-
+-      spin_lock_bh(&q->lock);
+-
+-      txq = netdev_get_tx_queue(dev, qid);
+-      if (q->queued + nr_frags > q->ndesc) {
+-              /* not enough space in the queue */
+-              netif_tx_stop_queue(txq);
+-              spin_unlock_bh(&q->lock);
+-              return NETDEV_TX_BUSY;
+-      }
+-
+-      index = q->head;
+-      for (i = 0; i < nr_frags; i++) {
+-              struct airoha_qdma_desc *desc = &q->desc[index];
+-              struct airoha_queue_entry *e = &q->entry[index];
+-              skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+-              dma_addr_t addr;
+-              u32 val;
+-
+-              addr = dma_map_single(dev->dev.parent, data, len,
+-                                    DMA_TO_DEVICE);
+-              if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
+-                      goto error_unmap;
+-
+-              index = (index + 1) % q->ndesc;
+-
+-              val = FIELD_PREP(QDMA_DESC_LEN_MASK, len);
+-              if (i < nr_frags - 1)
+-                      val |= FIELD_PREP(QDMA_DESC_MORE_MASK, 1);
+-              WRITE_ONCE(desc->ctrl, cpu_to_le32(val));
+-              WRITE_ONCE(desc->addr, cpu_to_le32(addr));
+-              val = FIELD_PREP(QDMA_DESC_NEXT_ID_MASK, index);
+-              WRITE_ONCE(desc->data, cpu_to_le32(val));
+-              WRITE_ONCE(desc->msg0, cpu_to_le32(msg0));
+-              WRITE_ONCE(desc->msg1, cpu_to_le32(msg1));
+-              WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff));
+-
+-              e->skb = i ? NULL : skb;
+-              e->dma_addr = addr;
+-              e->dma_len = len;
+-
+-              data = skb_frag_address(frag);
+-              len = skb_frag_size(frag);
+-      }
+-
+-      q->head = index;
+-      q->queued += i;
+-
+-      skb_tx_timestamp(skb);
+-      netdev_tx_sent_queue(txq, skb->len);
+-
+-      if (netif_xmit_stopped(txq) || !netdev_xmit_more())
+-              airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid),
+-                              TX_RING_CPU_IDX_MASK,
+-                              FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
+-
+-      if (q->ndesc - q->queued < q->free_thr)
+-              netif_tx_stop_queue(txq);
+-
+-      spin_unlock_bh(&q->lock);
+-
+-      return NETDEV_TX_OK;
+-
+-error_unmap:
+-      for (i--; i >= 0; i--) {
+-              index = (q->head + i) % q->ndesc;
+-              dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr,
+-                               q->entry[index].dma_len, DMA_TO_DEVICE);
+-      }
+-
+-      spin_unlock_bh(&q->lock);
+-error:
+-      dev_kfree_skb_any(skb);
+-      dev->stats.tx_dropped++;
+-
+-      return NETDEV_TX_OK;
+-}
+-
+-static void airoha_ethtool_get_drvinfo(struct net_device *dev,
+-                                     struct ethtool_drvinfo *info)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      struct airoha_eth *eth = port->qdma->eth;
+-
+-      strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
+-      strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
+-}
+-
+-static void airoha_ethtool_get_mac_stats(struct net_device *dev,
+-                                       struct ethtool_eth_mac_stats *stats)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      unsigned int start;
+-
+-      airoha_update_hw_stats(port);
+-      do {
+-              start = u64_stats_fetch_begin(&port->stats.syncp);
+-              stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
+-              stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
+-              stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
+-      } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+-}
+-
+-static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
+-      {    0,    64 },
+-      {   65,   127 },
+-      {  128,   255 },
+-      {  256,   511 },
+-      {  512,  1023 },
+-      { 1024,  1518 },
+-      { 1519, 10239 },
+-      {},
+-};
+-
+-static void
+-airoha_ethtool_get_rmon_stats(struct net_device *dev,
+-                            struct ethtool_rmon_stats *stats,
+-                            const struct ethtool_rmon_hist_range **ranges)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-      struct airoha_hw_stats *hw_stats = &port->stats;
+-      unsigned int start;
+-
+-      BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
+-                   ARRAY_SIZE(hw_stats->tx_len) + 1);
+-      BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
+-                   ARRAY_SIZE(hw_stats->rx_len) + 1);
+-
+-      *ranges = airoha_ethtool_rmon_ranges;
+-      airoha_update_hw_stats(port);
+-      do {
+-              int i;
+-
+-              start = u64_stats_fetch_begin(&port->stats.syncp);
+-              stats->fragments = hw_stats->rx_fragment;
+-              stats->jabbers = hw_stats->rx_jabber;
+-              for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
+-                   i++) {
+-                      stats->hist[i] = hw_stats->rx_len[i];
+-                      stats->hist_tx[i] = hw_stats->tx_len[i];
+-              }
+-      } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+-}
+-
+-static int airoha_qdma_set_chan_tx_sched(struct airoha_gdm_port *port,
+-                                       int channel, enum tx_sched_mode mode,
+-                                       const u16 *weights, u8 n_weights)
+-{
+-      int i;
+-
+-      for (i = 0; i < AIROHA_NUM_TX_RING; i++)
+-              airoha_qdma_clear(port->qdma, REG_QUEUE_CLOSE_CFG(channel),
+-                                TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
+-
+-      for (i = 0; i < n_weights; i++) {
+-              u32 status;
+-              int err;
+-
+-              airoha_qdma_wr(port->qdma, REG_TXWRR_WEIGHT_CFG,
+-                             TWRR_RW_CMD_MASK |
+-                             FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
+-                             FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
+-                             FIELD_PREP(TWRR_VALUE_MASK, weights[i]));
+-              err = read_poll_timeout(airoha_qdma_rr, status,
+-                                      status & TWRR_RW_CMD_DONE,
+-                                      USEC_PER_MSEC, 10 * USEC_PER_MSEC,
+-                                      true, port->qdma,
+-                                      REG_TXWRR_WEIGHT_CFG);
+-              if (err)
+-                      return err;
+-      }
+-
+-      airoha_qdma_rmw(port->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+-                      CHAN_QOS_MODE_MASK(channel),
+-                      mode << __ffs(CHAN_QOS_MODE_MASK(channel)));
+-
+-      return 0;
+-}
+-
+-static int airoha_qdma_set_tx_prio_sched(struct airoha_gdm_port *port,
+-                                       int channel)
+-{
+-      static const u16 w[AIROHA_NUM_QOS_QUEUES] = {};
+-
+-      return airoha_qdma_set_chan_tx_sched(port, channel, TC_SCH_SP, w,
+-                                           ARRAY_SIZE(w));
+-}
+-
+-static int airoha_qdma_set_tx_ets_sched(struct airoha_gdm_port *port,
+-                                      int channel,
+-                                      struct tc_ets_qopt_offload *opt)
+-{
+-      struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params;
+-      enum tx_sched_mode mode = TC_SCH_SP;
+-      u16 w[AIROHA_NUM_QOS_QUEUES] = {};
+-      int i, nstrict = 0, nwrr, qidx;
+-
+-      if (p->bands > AIROHA_NUM_QOS_QUEUES)
+-              return -EINVAL;
+-
+-      for (i = 0; i < p->bands; i++) {
+-              if (!p->quanta[i])
+-                      nstrict++;
+-      }
+-
+-      /* this configuration is not supported by the hw */
+-      if (nstrict == AIROHA_NUM_QOS_QUEUES - 1)
+-              return -EINVAL;
+-
+-      /* EN7581 SoC supports fixed QoS band priority where WRR queues have
+-       * lowest priorities with respect to SP ones.
+-       * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn
+-       */
+-      nwrr = p->bands - nstrict;
+-      qidx = nstrict && nwrr ? nstrict : 0;
+-      for (i = 1; i <= p->bands; i++) {
+-              if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx)
+-                      return -EINVAL;
+-
+-              qidx = i == nwrr ? 0 : qidx + 1;
+-      }
+-
+-      for (i = 0; i < nwrr; i++)
+-              w[i] = p->weights[nstrict + i];
+-
+-      if (!nstrict)
+-              mode = TC_SCH_WRR8;
+-      else if (nstrict < AIROHA_NUM_QOS_QUEUES - 1)
+-              mode = nstrict + 1;
+-
+-      return airoha_qdma_set_chan_tx_sched(port, channel, mode, w,
+-                                           ARRAY_SIZE(w));
+-}
+-
+-static int airoha_qdma_get_tx_ets_stats(struct airoha_gdm_port *port,
+-                                      int channel,
+-                                      struct tc_ets_qopt_offload *opt)
+-{
+-      u64 cpu_tx_packets = airoha_qdma_rr(port->qdma,
+-                                          REG_CNTR_VAL(channel << 1));
+-      u64 fwd_tx_packets = airoha_qdma_rr(port->qdma,
+-                                          REG_CNTR_VAL((channel << 1) + 1));
+-      u64 tx_packets = (cpu_tx_packets - port->cpu_tx_packets) +
+-                       (fwd_tx_packets - port->fwd_tx_packets);
+-      _bstats_update(opt->stats.bstats, 0, tx_packets);
+-
+-      port->cpu_tx_packets = cpu_tx_packets;
+-      port->fwd_tx_packets = fwd_tx_packets;
+-
+-      return 0;
+-}
+-
+-static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port,
+-                                   struct tc_ets_qopt_offload *opt)
+-{
+-      int channel = TC_H_MAJ(opt->handle) >> 16;
+-
+-      if (opt->parent == TC_H_ROOT)
+-              return -EINVAL;
+-
+-      switch (opt->command) {
+-      case TC_ETS_REPLACE:
+-              return airoha_qdma_set_tx_ets_sched(port, channel, opt);
+-      case TC_ETS_DESTROY:
+-              /* PRIO is default qdisc scheduler */
+-              return airoha_qdma_set_tx_prio_sched(port, channel);
+-      case TC_ETS_STATS:
+-              return airoha_qdma_get_tx_ets_stats(port, channel, opt);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel,
+-                                     u32 addr, enum trtcm_param_type param,
+-                                     enum trtcm_mode_type mode,
+-                                     u32 *val_low, u32 *val_high)
+-{
+-      u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel);
+-      u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) |
+-                        FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
+-                        FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
+-                        FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
+-
+-      airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
+-      if (read_poll_timeout(airoha_qdma_rr, val,
+-                            val & TRTCM_PARAM_RW_DONE_MASK,
+-                            USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
+-                            qdma, REG_TRTCM_CFG_PARAM(addr)))
+-              return -ETIMEDOUT;
+-
+-      *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr));
+-      if (val_high)
+-              *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr));
+-
+-      return 0;
+-}
+-
+-static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,
+-                                     u32 addr, enum trtcm_param_type param,
+-                                     enum trtcm_mode_type mode, u32 val)
+-{
+-      u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel);
+-      u32 config = TRTCM_PARAM_RW_MASK |
+-                   FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) |
+-                   FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
+-                   FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
+-                   FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
+-
+-      airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
+-      airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
+-
+-      return read_poll_timeout(airoha_qdma_rr, val,
+-                               val & TRTCM_PARAM_RW_DONE_MASK,
+-                               USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
+-                               qdma, REG_TRTCM_CFG_PARAM(addr));
+-}
+-
+-static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
+-                                      u32 addr, enum trtcm_mode_type mode,
+-                                      bool enable, u32 enable_mask)
+-{
+-      u32 val;
+-
+-      if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
+-                                      mode, &val, NULL))
+-              return -EINVAL;
+-
+-      val = enable ? val | enable_mask : val & ~enable_mask;
+-
+-      return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
+-                                         mode, val);
+-}
+-
+-static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma,
+-                                            int channel, u32 addr,
+-                                            enum trtcm_mode_type mode,
+-                                            u32 rate_val, u32 bucket_size)
+-{
+-      u32 val, config, tick, unit, rate, rate_frac;
+-      int err;
+-
+-      if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
+-                                      mode, &config, NULL))
+-              return -EINVAL;
+-
+-      val = airoha_qdma_rr(qdma, addr);
+-      tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val);
+-      if (config & TRTCM_TICK_SEL)
+-              tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val);
+-      if (!tick)
+-              return -EINVAL;
+-
+-      unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick;
+-      if (!unit)
+-              return -EINVAL;
+-
+-      rate = rate_val / unit;
+-      rate_frac = rate_val % unit;
+-      rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit;
+-      rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) |
+-             FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac);
+-
+-      err = airoha_qdma_set_trtcm_param(qdma, channel, addr,
+-                                        TRTCM_TOKEN_RATE_MODE, mode, rate);
+-      if (err)
+-              return err;
+-
+-      val = max_t(u32, bucket_size, MIN_TOKEN_SIZE);
+-      val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET);
+-
+-      return airoha_qdma_set_trtcm_param(qdma, channel, addr,
+-                                         TRTCM_BUCKETSIZE_SHIFT_MODE,
+-                                         mode, val);
+-}
+-
+-static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port,
+-                                       int channel, u32 rate,
+-                                       u32 bucket_size)
+-{
+-      int i, err;
+-
+-      for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
+-              err = airoha_qdma_set_trtcm_config(port->qdma, channel,
+-                                                 REG_EGRESS_TRTCM_CFG, i,
+-                                                 !!rate, TRTCM_METER_MODE);
+-              if (err)
+-                      return err;
+-
+-              err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel,
+-                                                       REG_EGRESS_TRTCM_CFG,
+-                                                       i, rate, bucket_size);
+-              if (err)
+-                      return err;
+-      }
+-
+-      return 0;
+-}
+-
+-static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port,
+-                                        struct tc_htb_qopt_offload *opt)
+-{
+-      u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
+-      u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
+-      struct net_device *dev = port->dev;
+-      int num_tx_queues = dev->real_num_tx_queues;
+-      int err;
+-
+-      if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
+-              NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
+-              return -EINVAL;
+-      }
+-
+-      err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum);
+-      if (err) {
+-              NL_SET_ERR_MSG_MOD(opt->extack,
+-                                 "failed configuring htb offload");
+-              return err;
+-      }
+-
+-      if (opt->command == TC_HTB_NODE_MODIFY)
+-              return 0;
+-
+-      err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1);
+-      if (err) {
+-              airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum);
+-              NL_SET_ERR_MSG_MOD(opt->extack,
+-                                 "failed setting real_num_tx_queues");
+-              return err;
+-      }
+-
+-      set_bit(channel, port->qos_sq_bmap);
+-      opt->qid = AIROHA_NUM_TX_RING + channel;
+-
+-      return 0;
+-}
+-
+-static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue)
+-{
+-      struct net_device *dev = port->dev;
+-
+-      netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
+-      airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0);
+-      clear_bit(queue, port->qos_sq_bmap);
+-}
+-
+-static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port,
+-                                         struct tc_htb_qopt_offload *opt)
+-{
+-      u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
+-
+-      if (!test_bit(channel, port->qos_sq_bmap)) {
+-              NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
+-              return -EINVAL;
+-      }
+-
+-      airoha_tc_remove_htb_queue(port, channel);
+-
+-      return 0;
+-}
+-
+-static int airoha_tc_htb_destroy(struct airoha_gdm_port *port)
+-{
+-      int q;
+-
+-      for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
+-              airoha_tc_remove_htb_queue(port, q);
+-
+-      return 0;
+-}
+-
+-static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port,
+-                                          struct tc_htb_qopt_offload *opt)
+-{
+-      u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
+-
+-      if (!test_bit(channel, port->qos_sq_bmap)) {
+-              NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
+-              return -EINVAL;
+-      }
+-
+-      opt->qid = channel;
+-
+-      return 0;
+-}
+-
+-static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port,
+-                                   struct tc_htb_qopt_offload *opt)
+-{
+-      switch (opt->command) {
+-      case TC_HTB_CREATE:
+-              break;
+-      case TC_HTB_DESTROY:
+-              return airoha_tc_htb_destroy(port);
+-      case TC_HTB_NODE_MODIFY:
+-      case TC_HTB_LEAF_ALLOC_QUEUE:
+-              return airoha_tc_htb_alloc_leaf_queue(port, 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(port, opt);
+-      case TC_HTB_LEAF_QUERY_QUEUE:
+-              return airoha_tc_get_htb_get_leaf_queue(port, opt);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-
+-      return 0;
+-}
+-
+-static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
+-                             void *type_data)
+-{
+-      struct airoha_gdm_port *port = netdev_priv(dev);
+-
+-      switch (type) {
+-      case TC_SETUP_QDISC_ETS:
+-              return airoha_tc_setup_qdisc_ets(port, type_data);
+-      case TC_SETUP_QDISC_HTB:
+-              return airoha_tc_setup_qdisc_htb(port, type_data);
+-      default:
+-              return -EOPNOTSUPP;
+-      }
+-}
+-
+-static const struct net_device_ops airoha_netdev_ops = {
+-      .ndo_init               = airoha_dev_init,
+-      .ndo_open               = airoha_dev_open,
+-      .ndo_stop               = airoha_dev_stop,
+-      .ndo_select_queue       = airoha_dev_select_queue,
+-      .ndo_start_xmit         = airoha_dev_xmit,
+-      .ndo_get_stats64        = airoha_dev_get_stats64,
+-      .ndo_set_mac_address    = airoha_dev_set_macaddr,
+-      .ndo_setup_tc           = airoha_dev_tc_setup,
+-};
+-
+-static const struct ethtool_ops airoha_ethtool_ops = {
+-      .get_drvinfo            = airoha_ethtool_get_drvinfo,
+-      .get_eth_mac_stats      = airoha_ethtool_get_mac_stats,
+-      .get_rmon_stats         = airoha_ethtool_get_rmon_stats,
+-};
+-
+-static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
+-{
+-      const __be32 *id_ptr = of_get_property(np, "reg", NULL);
+-      struct airoha_gdm_port *port;
+-      struct airoha_qdma *qdma;
+-      struct net_device *dev;
+-      int err, index;
+-      u32 id;
+-
+-      if (!id_ptr) {
+-              dev_err(eth->dev, "missing gdm port id\n");
+-              return -EINVAL;
+-      }
+-
+-      id = be32_to_cpup(id_ptr);
+-      index = id - 1;
+-
+-      if (!id || id > ARRAY_SIZE(eth->ports)) {
+-              dev_err(eth->dev, "invalid gdm port id: %d\n", id);
+-              return -EINVAL;
+-      }
+-
+-      if (eth->ports[index]) {
+-              dev_err(eth->dev, "duplicate gdm port id: %d\n", id);
+-              return -EINVAL;
+-      }
+-
+-      dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
+-                                    AIROHA_NUM_NETDEV_TX_RINGS,
+-                                    AIROHA_NUM_RX_RING);
+-      if (!dev) {
+-              dev_err(eth->dev, "alloc_etherdev failed\n");
+-              return -ENOMEM;
+-      }
+-
+-      qdma = &eth->qdma[index % AIROHA_MAX_NUM_QDMA];
+-      dev->netdev_ops = &airoha_netdev_ops;
+-      dev->ethtool_ops = &airoha_ethtool_ops;
+-      dev->max_mtu = AIROHA_MAX_MTU;
+-      dev->watchdog_timeo = 5 * HZ;
+-      dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+-                         NETIF_F_TSO6 | NETIF_F_IPV6_CSUM |
+-                         NETIF_F_SG | NETIF_F_TSO |
+-                         NETIF_F_HW_TC;
+-      dev->features |= dev->hw_features;
+-      dev->dev.of_node = np;
+-      dev->irq = qdma->irq;
+-      SET_NETDEV_DEV(dev, eth->dev);
+-
+-      /* reserve hw queues for HTB offloading */
+-      err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
+-      if (err)
+-              return err;
+-
+-      err = of_get_ethdev_address(np, dev);
+-      if (err) {
+-              if (err == -EPROBE_DEFER)
+-                      return err;
+-
+-              eth_hw_addr_random(dev);
+-              dev_info(eth->dev, "generated random MAC address %pM\n",
+-                       dev->dev_addr);
+-      }
+-
+-      port = netdev_priv(dev);
+-      u64_stats_init(&port->stats.syncp);
+-      spin_lock_init(&port->stats.lock);
+-      port->qdma = qdma;
+-      port->dev = dev;
+-      port->id = id;
+-      eth->ports[index] = port;
+-
+-      return register_netdev(dev);
+-}
+-
+-static int airoha_probe(struct platform_device *pdev)
+-{
+-      struct device_node *np;
+-      struct airoha_eth *eth;
+-      int i, err;
+-
+-      eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
+-      if (!eth)
+-              return -ENOMEM;
+-
+-      eth->dev = &pdev->dev;
+-
+-      err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32));
+-      if (err) {
+-              dev_err(eth->dev, "failed configuring DMA mask\n");
+-              return err;
+-      }
+-
+-      eth->fe_regs = devm_platform_ioremap_resource_byname(pdev, "fe");
+-      if (IS_ERR(eth->fe_regs))
+-              return dev_err_probe(eth->dev, PTR_ERR(eth->fe_regs),
+-                                   "failed to iomap fe regs\n");
+-
+-      eth->rsts[0].id = "fe";
+-      eth->rsts[1].id = "pdma";
+-      eth->rsts[2].id = "qdma";
+-      err = devm_reset_control_bulk_get_exclusive(eth->dev,
+-                                                  ARRAY_SIZE(eth->rsts),
+-                                                  eth->rsts);
+-      if (err) {
+-              dev_err(eth->dev, "failed to get bulk reset lines\n");
+-              return err;
+-      }
+-
+-      eth->xsi_rsts[0].id = "xsi-mac";
+-      eth->xsi_rsts[1].id = "hsi0-mac";
+-      eth->xsi_rsts[2].id = "hsi1-mac";
+-      eth->xsi_rsts[3].id = "hsi-mac";
+-      eth->xsi_rsts[4].id = "xfp-mac";
+-      err = devm_reset_control_bulk_get_exclusive(eth->dev,
+-                                                  ARRAY_SIZE(eth->xsi_rsts),
+-                                                  eth->xsi_rsts);
+-      if (err) {
+-              dev_err(eth->dev, "failed to get bulk xsi reset lines\n");
+-              return err;
+-      }
+-
+-      eth->napi_dev = alloc_netdev_dummy(0);
+-      if (!eth->napi_dev)
+-              return -ENOMEM;
+-
+-      /* Enable threaded NAPI by default */
+-      eth->napi_dev->threaded = true;
+-      strscpy(eth->napi_dev->name, "qdma_eth", sizeof(eth->napi_dev->name));
+-      platform_set_drvdata(pdev, eth);
+-
+-      err = airoha_hw_init(pdev, eth);
+-      if (err)
+-              goto error_hw_cleanup;
+-
+-      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
+-              airoha_qdma_start_napi(&eth->qdma[i]);
+-
+-      for_each_child_of_node(pdev->dev.of_node, np) {
+-              if (!of_device_is_compatible(np, "airoha,eth-mac"))
+-                      continue;
+-
+-              if (!of_device_is_available(np))
+-                      continue;
+-
+-              err = airoha_alloc_gdm_port(eth, np);
+-              if (err) {
+-                      of_node_put(np);
+-                      goto error_napi_stop;
+-              }
+-      }
+-
+-      return 0;
+-
+-error_napi_stop:
+-      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
+-              airoha_qdma_stop_napi(&eth->qdma[i]);
+-error_hw_cleanup:
+-      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
+-              airoha_hw_cleanup(&eth->qdma[i]);
+-
+-      for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+-              struct airoha_gdm_port *port = eth->ports[i];
+-
+-              if (port && port->dev->reg_state == NETREG_REGISTERED)
+-                      unregister_netdev(port->dev);
+-      }
+-      free_netdev(eth->napi_dev);
+-      platform_set_drvdata(pdev, NULL);
+-
+-      return err;
+-}
+-
+-static void airoha_remove(struct platform_device *pdev)
+-{
+-      struct airoha_eth *eth = platform_get_drvdata(pdev);
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) {
+-              airoha_qdma_stop_napi(&eth->qdma[i]);
+-              airoha_hw_cleanup(&eth->qdma[i]);
+-      }
+-
+-      for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+-              struct airoha_gdm_port *port = eth->ports[i];
+-
+-              if (!port)
+-                      continue;
+-
+-              airoha_dev_stop(port->dev);
+-              unregister_netdev(port->dev);
+-      }
+-      free_netdev(eth->napi_dev);
+-
+-      platform_set_drvdata(pdev, NULL);
+-}
+-
+-static const struct of_device_id of_airoha_match[] = {
+-      { .compatible = "airoha,en7581-eth" },
+-      { /* sentinel */ }
+-};
+-MODULE_DEVICE_TABLE(of, of_airoha_match);
+-
+-static struct platform_driver airoha_driver = {
+-      .probe = airoha_probe,
+-      .remove_new = airoha_remove,
+-      .driver = {
+-              .name = KBUILD_MODNAME,
+-              .of_match_table = of_airoha_match,
+-      },
+-};
+-module_platform_driver(airoha_driver);
+-
+-MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
+-MODULE_DESCRIPTION("Ethernet driver for Airoha SoC");
diff --git a/target/linux/airoha/patches-6.6/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch b/target/linux/airoha/patches-6.6/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch
new file mode 100644 (file)
index 0000000..8539128
--- /dev/null
@@ -0,0 +1,538 @@
+From b38f4ff0ceacd6ce8d333a8dc90f405a040968d3 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:10 +0100
+Subject: [PATCH 02/15] net: airoha: Move definitions in airoha_eth.h
+
+Move common airoha_eth definitions in airoha_eth.h in order to reuse
+them for Packet Processor Engine (PPE) codebase.
+PPE module is used to enable support for flowtable hw offloading in
+airoha_eth driver.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 240 +---------------------
+ drivers/net/ethernet/airoha/airoha_eth.h | 251 +++++++++++++++++++++++
+ 2 files changed, 252 insertions(+), 239 deletions(-)
+ create mode 100644 drivers/net/ethernet/airoha/airoha_eth.h
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -3,14 +3,9 @@
+  * Copyright (c) 2024 AIROHA Inc
+  * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+  */
+-#include <linux/etherdevice.h>
+-#include <linux/iopoll.h>
+-#include <linux/kernel.h>
+-#include <linux/netdevice.h>
+ #include <linux/of.h>
+ #include <linux/of_net.h>
+ #include <linux/platform_device.h>
+-#include <linux/reset.h>
+ #include <linux/tcp.h>
+ #include <linux/u64_stats_sync.h>
+ #include <net/dsa.h>
+@@ -18,35 +13,7 @@
+ #include <net/pkt_cls.h>
+ #include <uapi/linux/ppp_defs.h>
+-#define AIROHA_MAX_NUM_GDM_PORTS      1
+-#define AIROHA_MAX_NUM_QDMA           2
+-#define AIROHA_MAX_NUM_RSTS           3
+-#define AIROHA_MAX_NUM_XSI_RSTS               5
+-#define AIROHA_MAX_MTU                        2000
+-#define AIROHA_MAX_PACKET_SIZE                2048
+-#define AIROHA_NUM_QOS_CHANNELS               4
+-#define AIROHA_NUM_QOS_QUEUES         8
+-#define AIROHA_NUM_TX_RING            32
+-#define AIROHA_NUM_RX_RING            32
+-#define AIROHA_NUM_NETDEV_TX_RINGS    (AIROHA_NUM_TX_RING + \
+-                                       AIROHA_NUM_QOS_CHANNELS)
+-#define AIROHA_FE_MC_MAX_VLAN_TABLE   64
+-#define AIROHA_FE_MC_MAX_VLAN_PORT    16
+-#define AIROHA_NUM_TX_IRQ             2
+-#define HW_DSCP_NUM                   2048
+-#define IRQ_QUEUE_LEN(_n)             ((_n) ? 1024 : 2048)
+-#define TX_DSCP_NUM                   1024
+-#define RX_DSCP_NUM(_n)                       \
+-      ((_n) ==  2 ? 128 :             \
+-       (_n) == 11 ? 128 :             \
+-       (_n) == 15 ? 128 :             \
+-       (_n) ==  0 ? 1024 : 16)
+-
+-#define PSE_RSV_PAGES                 128
+-#define PSE_QUEUE_RSV_PAGES           64
+-
+-#define QDMA_METER_IDX(_n)            ((_n) & 0xff)
+-#define QDMA_METER_GROUP(_n)          (((_n) >> 8) & 0x3)
++#include "airoha_eth.h"
+ /* FE */
+ #define PSE_BASE                      0x0100
+@@ -706,211 +673,6 @@ struct airoha_qdma_fwd_desc {
+       __le32 rsv1;
+ };
+-enum {
+-      QDMA_INT_REG_IDX0,
+-      QDMA_INT_REG_IDX1,
+-      QDMA_INT_REG_IDX2,
+-      QDMA_INT_REG_IDX3,
+-      QDMA_INT_REG_IDX4,
+-      QDMA_INT_REG_MAX
+-};
+-
+-enum {
+-      XSI_PCIE0_PORT,
+-      XSI_PCIE1_PORT,
+-      XSI_USB_PORT,
+-      XSI_AE_PORT,
+-      XSI_ETH_PORT,
+-};
+-
+-enum {
+-      XSI_PCIE0_VIP_PORT_MASK = BIT(22),
+-      XSI_PCIE1_VIP_PORT_MASK = BIT(23),
+-      XSI_USB_VIP_PORT_MASK   = BIT(25),
+-      XSI_ETH_VIP_PORT_MASK   = BIT(24),
+-};
+-
+-enum {
+-      DEV_STATE_INITIALIZED,
+-};
+-
+-enum {
+-      CDM_CRSN_QSEL_Q1 = 1,
+-      CDM_CRSN_QSEL_Q5 = 5,
+-      CDM_CRSN_QSEL_Q6 = 6,
+-      CDM_CRSN_QSEL_Q15 = 15,
+-};
+-
+-enum {
+-      CRSN_08 = 0x8,
+-      CRSN_21 = 0x15, /* KA */
+-      CRSN_22 = 0x16, /* hit bind and force route to CPU */
+-      CRSN_24 = 0x18,
+-      CRSN_25 = 0x19,
+-};
+-
+-enum {
+-      FE_PSE_PORT_CDM1,
+-      FE_PSE_PORT_GDM1,
+-      FE_PSE_PORT_GDM2,
+-      FE_PSE_PORT_GDM3,
+-      FE_PSE_PORT_PPE1,
+-      FE_PSE_PORT_CDM2,
+-      FE_PSE_PORT_CDM3,
+-      FE_PSE_PORT_CDM4,
+-      FE_PSE_PORT_PPE2,
+-      FE_PSE_PORT_GDM4,
+-      FE_PSE_PORT_CDM5,
+-      FE_PSE_PORT_DROP = 0xf,
+-};
+-
+-enum tx_sched_mode {
+-      TC_SCH_WRR8,
+-      TC_SCH_SP,
+-      TC_SCH_WRR7,
+-      TC_SCH_WRR6,
+-      TC_SCH_WRR5,
+-      TC_SCH_WRR4,
+-      TC_SCH_WRR3,
+-      TC_SCH_WRR2,
+-};
+-
+-enum trtcm_param_type {
+-      TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */
+-      TRTCM_TOKEN_RATE_MODE,
+-      TRTCM_BUCKETSIZE_SHIFT_MODE,
+-      TRTCM_BUCKET_COUNTER_MODE,
+-};
+-
+-enum trtcm_mode_type {
+-      TRTCM_COMMIT_MODE,
+-      TRTCM_PEAK_MODE,
+-};
+-
+-enum trtcm_param {
+-      TRTCM_TICK_SEL = BIT(0),
+-      TRTCM_PKT_MODE = BIT(1),
+-      TRTCM_METER_MODE = BIT(2),
+-};
+-
+-#define MIN_TOKEN_SIZE                                4096
+-#define MAX_TOKEN_SIZE_OFFSET                 17
+-#define TRTCM_TOKEN_RATE_MASK                 GENMASK(23, 6)
+-#define TRTCM_TOKEN_RATE_FRACTION_MASK                GENMASK(5, 0)
+-
+-struct airoha_queue_entry {
+-      union {
+-              void *buf;
+-              struct sk_buff *skb;
+-      };
+-      dma_addr_t dma_addr;
+-      u16 dma_len;
+-};
+-
+-struct airoha_queue {
+-      struct airoha_qdma *qdma;
+-
+-      /* protect concurrent queue accesses */
+-      spinlock_t lock;
+-      struct airoha_queue_entry *entry;
+-      struct airoha_qdma_desc *desc;
+-      u16 head;
+-      u16 tail;
+-
+-      int queued;
+-      int ndesc;
+-      int free_thr;
+-      int buf_size;
+-
+-      struct napi_struct napi;
+-      struct page_pool *page_pool;
+-};
+-
+-struct airoha_tx_irq_queue {
+-      struct airoha_qdma *qdma;
+-
+-      struct napi_struct napi;
+-
+-      int size;
+-      u32 *q;
+-};
+-
+-struct airoha_hw_stats {
+-      /* protect concurrent hw_stats accesses */
+-      spinlock_t lock;
+-      struct u64_stats_sync syncp;
+-
+-      /* get_stats64 */
+-      u64 rx_ok_pkts;
+-      u64 tx_ok_pkts;
+-      u64 rx_ok_bytes;
+-      u64 tx_ok_bytes;
+-      u64 rx_multicast;
+-      u64 rx_errors;
+-      u64 rx_drops;
+-      u64 tx_drops;
+-      u64 rx_crc_error;
+-      u64 rx_over_errors;
+-      /* ethtool stats */
+-      u64 tx_broadcast;
+-      u64 tx_multicast;
+-      u64 tx_len[7];
+-      u64 rx_broadcast;
+-      u64 rx_fragment;
+-      u64 rx_jabber;
+-      u64 rx_len[7];
+-};
+-
+-struct airoha_qdma {
+-      struct airoha_eth *eth;
+-      void __iomem *regs;
+-
+-      /* protect concurrent irqmask accesses */
+-      spinlock_t irq_lock;
+-      u32 irqmask[QDMA_INT_REG_MAX];
+-      int irq;
+-
+-      struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ];
+-
+-      struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
+-      struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
+-
+-      /* descriptor and packet buffers for qdma hw forward */
+-      struct {
+-              void *desc;
+-              void *q;
+-      } hfwd;
+-};
+-
+-struct airoha_gdm_port {
+-      struct airoha_qdma *qdma;
+-      struct net_device *dev;
+-      int id;
+-
+-      struct airoha_hw_stats stats;
+-
+-      DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
+-
+-      /* qos stats counters */
+-      u64 cpu_tx_packets;
+-      u64 fwd_tx_packets;
+-};
+-
+-struct airoha_eth {
+-      struct device *dev;
+-
+-      unsigned long state;
+-      void __iomem *fe_regs;
+-
+-      struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
+-      struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
+-
+-      struct net_device *napi_dev;
+-
+-      struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA];
+-      struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
+-};
+-
+ static u32 airoha_rr(void __iomem *base, u32 offset)
+ {
+       return readl(base + offset);
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -0,0 +1,251 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2024 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++
++#ifndef AIROHA_ETH_H
++#define AIROHA_ETH_H
++
++#include <linux/etherdevice.h>
++#include <linux/iopoll.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/reset.h>
++
++#define AIROHA_MAX_NUM_GDM_PORTS      1
++#define AIROHA_MAX_NUM_QDMA           2
++#define AIROHA_MAX_NUM_RSTS           3
++#define AIROHA_MAX_NUM_XSI_RSTS               5
++#define AIROHA_MAX_MTU                        2000
++#define AIROHA_MAX_PACKET_SIZE                2048
++#define AIROHA_NUM_QOS_CHANNELS               4
++#define AIROHA_NUM_QOS_QUEUES         8
++#define AIROHA_NUM_TX_RING            32
++#define AIROHA_NUM_RX_RING            32
++#define AIROHA_NUM_NETDEV_TX_RINGS    (AIROHA_NUM_TX_RING + \
++                                       AIROHA_NUM_QOS_CHANNELS)
++#define AIROHA_FE_MC_MAX_VLAN_TABLE   64
++#define AIROHA_FE_MC_MAX_VLAN_PORT    16
++#define AIROHA_NUM_TX_IRQ             2
++#define HW_DSCP_NUM                   2048
++#define IRQ_QUEUE_LEN(_n)             ((_n) ? 1024 : 2048)
++#define TX_DSCP_NUM                   1024
++#define RX_DSCP_NUM(_n)                       \
++      ((_n) ==  2 ? 128 :             \
++       (_n) == 11 ? 128 :             \
++       (_n) == 15 ? 128 :             \
++       (_n) ==  0 ? 1024 : 16)
++
++#define PSE_RSV_PAGES                 128
++#define PSE_QUEUE_RSV_PAGES           64
++
++#define QDMA_METER_IDX(_n)            ((_n) & 0xff)
++#define QDMA_METER_GROUP(_n)          (((_n) >> 8) & 0x3)
++
++enum {
++      QDMA_INT_REG_IDX0,
++      QDMA_INT_REG_IDX1,
++      QDMA_INT_REG_IDX2,
++      QDMA_INT_REG_IDX3,
++      QDMA_INT_REG_IDX4,
++      QDMA_INT_REG_MAX
++};
++
++enum {
++      XSI_PCIE0_PORT,
++      XSI_PCIE1_PORT,
++      XSI_USB_PORT,
++      XSI_AE_PORT,
++      XSI_ETH_PORT,
++};
++
++enum {
++      XSI_PCIE0_VIP_PORT_MASK = BIT(22),
++      XSI_PCIE1_VIP_PORT_MASK = BIT(23),
++      XSI_USB_VIP_PORT_MASK   = BIT(25),
++      XSI_ETH_VIP_PORT_MASK   = BIT(24),
++};
++
++enum {
++      DEV_STATE_INITIALIZED,
++};
++
++enum {
++      CDM_CRSN_QSEL_Q1 = 1,
++      CDM_CRSN_QSEL_Q5 = 5,
++      CDM_CRSN_QSEL_Q6 = 6,
++      CDM_CRSN_QSEL_Q15 = 15,
++};
++
++enum {
++      CRSN_08 = 0x8,
++      CRSN_21 = 0x15, /* KA */
++      CRSN_22 = 0x16, /* hit bind and force route to CPU */
++      CRSN_24 = 0x18,
++      CRSN_25 = 0x19,
++};
++
++enum {
++      FE_PSE_PORT_CDM1,
++      FE_PSE_PORT_GDM1,
++      FE_PSE_PORT_GDM2,
++      FE_PSE_PORT_GDM3,
++      FE_PSE_PORT_PPE1,
++      FE_PSE_PORT_CDM2,
++      FE_PSE_PORT_CDM3,
++      FE_PSE_PORT_CDM4,
++      FE_PSE_PORT_PPE2,
++      FE_PSE_PORT_GDM4,
++      FE_PSE_PORT_CDM5,
++      FE_PSE_PORT_DROP = 0xf,
++};
++
++enum tx_sched_mode {
++      TC_SCH_WRR8,
++      TC_SCH_SP,
++      TC_SCH_WRR7,
++      TC_SCH_WRR6,
++      TC_SCH_WRR5,
++      TC_SCH_WRR4,
++      TC_SCH_WRR3,
++      TC_SCH_WRR2,
++};
++
++enum trtcm_param_type {
++      TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */
++      TRTCM_TOKEN_RATE_MODE,
++      TRTCM_BUCKETSIZE_SHIFT_MODE,
++      TRTCM_BUCKET_COUNTER_MODE,
++};
++
++enum trtcm_mode_type {
++      TRTCM_COMMIT_MODE,
++      TRTCM_PEAK_MODE,
++};
++
++enum trtcm_param {
++      TRTCM_TICK_SEL = BIT(0),
++      TRTCM_PKT_MODE = BIT(1),
++      TRTCM_METER_MODE = BIT(2),
++};
++
++#define MIN_TOKEN_SIZE                                4096
++#define MAX_TOKEN_SIZE_OFFSET                 17
++#define TRTCM_TOKEN_RATE_MASK                 GENMASK(23, 6)
++#define TRTCM_TOKEN_RATE_FRACTION_MASK                GENMASK(5, 0)
++
++struct airoha_queue_entry {
++      union {
++              void *buf;
++              struct sk_buff *skb;
++      };
++      dma_addr_t dma_addr;
++      u16 dma_len;
++};
++
++struct airoha_queue {
++      struct airoha_qdma *qdma;
++
++      /* protect concurrent queue accesses */
++      spinlock_t lock;
++      struct airoha_queue_entry *entry;
++      struct airoha_qdma_desc *desc;
++      u16 head;
++      u16 tail;
++
++      int queued;
++      int ndesc;
++      int free_thr;
++      int buf_size;
++
++      struct napi_struct napi;
++      struct page_pool *page_pool;
++};
++
++struct airoha_tx_irq_queue {
++      struct airoha_qdma *qdma;
++
++      struct napi_struct napi;
++
++      int size;
++      u32 *q;
++};
++
++struct airoha_hw_stats {
++      /* protect concurrent hw_stats accesses */
++      spinlock_t lock;
++      struct u64_stats_sync syncp;
++
++      /* get_stats64 */
++      u64 rx_ok_pkts;
++      u64 tx_ok_pkts;
++      u64 rx_ok_bytes;
++      u64 tx_ok_bytes;
++      u64 rx_multicast;
++      u64 rx_errors;
++      u64 rx_drops;
++      u64 tx_drops;
++      u64 rx_crc_error;
++      u64 rx_over_errors;
++      /* ethtool stats */
++      u64 tx_broadcast;
++      u64 tx_multicast;
++      u64 tx_len[7];
++      u64 rx_broadcast;
++      u64 rx_fragment;
++      u64 rx_jabber;
++      u64 rx_len[7];
++};
++
++struct airoha_qdma {
++      struct airoha_eth *eth;
++      void __iomem *regs;
++
++      /* protect concurrent irqmask accesses */
++      spinlock_t irq_lock;
++      u32 irqmask[QDMA_INT_REG_MAX];
++      int irq;
++
++      struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ];
++
++      struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
++      struct airoha_queue q_rx[AIROHA_NUM_RX_RING];
++
++      /* descriptor and packet buffers for qdma hw forward */
++      struct {
++              void *desc;
++              void *q;
++      } hfwd;
++};
++
++struct airoha_gdm_port {
++      struct airoha_qdma *qdma;
++      struct net_device *dev;
++      int id;
++
++      struct airoha_hw_stats stats;
++
++      DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
++
++      /* qos stats counters */
++      u64 cpu_tx_packets;
++      u64 fwd_tx_packets;
++};
++
++struct airoha_eth {
++      struct device *dev;
++
++      unsigned long state;
++      void __iomem *fe_regs;
++
++      struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
++      struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
++
++      struct net_device *napi_dev;
++
++      struct airoha_qdma qdma[AIROHA_MAX_NUM_QDMA];
++      struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
++};
++
++#endif /* AIROHA_ETH_H */
diff --git a/target/linux/airoha/patches-6.6/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch b/target/linux/airoha/patches-6.6/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch
new file mode 100644 (file)
index 0000000..bf24638
--- /dev/null
@@ -0,0 +1,101 @@
+From e0758a8694fbaffdc72940774db295585e951119 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:11 +0100
+Subject: [PATCH 03/15] net: airoha: Move reg/write utility routines in
+ airoha_eth.h
+
+This is a preliminary patch to introduce flowtable hw offloading
+support for airoha_eth driver.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 28 +++---------------------
+ drivers/net/ethernet/airoha/airoha_eth.h | 26 ++++++++++++++++++++++
+ 2 files changed, 29 insertions(+), 25 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -673,17 +673,17 @@ struct airoha_qdma_fwd_desc {
+       __le32 rsv1;
+ };
+-static u32 airoha_rr(void __iomem *base, u32 offset)
++u32 airoha_rr(void __iomem *base, u32 offset)
+ {
+       return readl(base + offset);
+ }
+-static void airoha_wr(void __iomem *base, u32 offset, u32 val)
++void airoha_wr(void __iomem *base, u32 offset, u32 val)
+ {
+       writel(val, base + offset);
+ }
+-static u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val)
++u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val)
+ {
+       val |= (airoha_rr(base, offset) & ~mask);
+       airoha_wr(base, offset, val);
+@@ -691,28 +691,6 @@ static u32 airoha_rmw(void __iomem *base
+       return val;
+ }
+-#define airoha_fe_rr(eth, offset)                             \
+-      airoha_rr((eth)->fe_regs, (offset))
+-#define airoha_fe_wr(eth, offset, val)                                \
+-      airoha_wr((eth)->fe_regs, (offset), (val))
+-#define airoha_fe_rmw(eth, offset, mask, val)                 \
+-      airoha_rmw((eth)->fe_regs, (offset), (mask), (val))
+-#define airoha_fe_set(eth, offset, val)                               \
+-      airoha_rmw((eth)->fe_regs, (offset), 0, (val))
+-#define airoha_fe_clear(eth, offset, val)                     \
+-      airoha_rmw((eth)->fe_regs, (offset), (val), 0)
+-
+-#define airoha_qdma_rr(qdma, offset)                          \
+-      airoha_rr((qdma)->regs, (offset))
+-#define airoha_qdma_wr(qdma, offset, val)                     \
+-      airoha_wr((qdma)->regs, (offset), (val))
+-#define airoha_qdma_rmw(qdma, offset, mask, val)              \
+-      airoha_rmw((qdma)->regs, (offset), (mask), (val))
+-#define airoha_qdma_set(qdma, offset, val)                    \
+-      airoha_rmw((qdma)->regs, (offset), 0, (val))
+-#define airoha_qdma_clear(qdma, offset, val)                  \
+-      airoha_rmw((qdma)->regs, (offset), (val), 0)
+-
+ static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index,
+                                   u32 clear, u32 set)
+ {
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -248,4 +248,30 @@ struct airoha_eth {
+       struct airoha_gdm_port *ports[AIROHA_MAX_NUM_GDM_PORTS];
+ };
++u32 airoha_rr(void __iomem *base, u32 offset);
++void airoha_wr(void __iomem *base, u32 offset, u32 val);
++u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val);
++
++#define airoha_fe_rr(eth, offset)                             \
++      airoha_rr((eth)->fe_regs, (offset))
++#define airoha_fe_wr(eth, offset, val)                                \
++      airoha_wr((eth)->fe_regs, (offset), (val))
++#define airoha_fe_rmw(eth, offset, mask, val)                 \
++      airoha_rmw((eth)->fe_regs, (offset), (mask), (val))
++#define airoha_fe_set(eth, offset, val)                               \
++      airoha_rmw((eth)->fe_regs, (offset), 0, (val))
++#define airoha_fe_clear(eth, offset, val)                     \
++      airoha_rmw((eth)->fe_regs, (offset), (val), 0)
++
++#define airoha_qdma_rr(qdma, offset)                          \
++      airoha_rr((qdma)->regs, (offset))
++#define airoha_qdma_wr(qdma, offset, val)                     \
++      airoha_wr((qdma)->regs, (offset), (val))
++#define airoha_qdma_rmw(qdma, offset, mask, val)              \
++      airoha_rmw((qdma)->regs, (offset), (mask), (val))
++#define airoha_qdma_set(qdma, offset, val)                    \
++      airoha_rmw((qdma)->regs, (offset), 0, (val))
++#define airoha_qdma_clear(qdma, offset, val)                  \
++      airoha_rmw((qdma)->regs, (offset), (val), 0)
++
+ #endif /* AIROHA_ETH_H */
diff --git a/target/linux/airoha/patches-6.6/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch b/target/linux/airoha/patches-6.6/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch
new file mode 100644 (file)
index 0000000..3b2b8bf
--- /dev/null
@@ -0,0 +1,1361 @@
+From ec663d9a82bf4d16721f6b1fc29df4892ba6c088 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:12 +0100
+Subject: [PATCH 04/15] net: airoha: Move register definitions in airoha_regs.h
+
+Move common airoha_eth register definitions in airoha_regs.h in order
+to reuse them for Packet Processor Engine (PPE) codebase.
+PPE module is used to enable support for flowtable hw offloading in
+airoha_eth driver.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c  | 659 +--------------------
+ drivers/net/ethernet/airoha/airoha_regs.h | 670 ++++++++++++++++++++++
+ 2 files changed, 671 insertions(+), 658 deletions(-)
+ create mode 100644 drivers/net/ethernet/airoha/airoha_regs.h
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -13,666 +13,9 @@
+ #include <net/pkt_cls.h>
+ #include <uapi/linux/ppp_defs.h>
++#include "airoha_regs.h"
+ #include "airoha_eth.h"
+-/* FE */
+-#define PSE_BASE                      0x0100
+-#define CSR_IFC_BASE                  0x0200
+-#define CDM1_BASE                     0x0400
+-#define GDM1_BASE                     0x0500
+-#define PPE1_BASE                     0x0c00
+-
+-#define CDM2_BASE                     0x1400
+-#define GDM2_BASE                     0x1500
+-
+-#define GDM3_BASE                     0x1100
+-#define GDM4_BASE                     0x2500
+-
+-#define GDM_BASE(_n)                  \
+-      ((_n) == 4 ? GDM4_BASE :        \
+-       (_n) == 3 ? GDM3_BASE :        \
+-       (_n) == 2 ? GDM2_BASE : GDM1_BASE)
+-
+-#define REG_FE_DMA_GLO_CFG            0x0000
+-#define FE_DMA_GLO_L2_SPACE_MASK      GENMASK(7, 4)
+-#define FE_DMA_GLO_PG_SZ_MASK         BIT(3)
+-
+-#define REG_FE_RST_GLO_CFG            0x0004
+-#define FE_RST_GDM4_MBI_ARB_MASK      BIT(3)
+-#define FE_RST_GDM3_MBI_ARB_MASK      BIT(2)
+-#define FE_RST_CORE_MASK              BIT(0)
+-
+-#define REG_FE_WAN_MAC_H              0x0030
+-#define REG_FE_LAN_MAC_H              0x0040
+-
+-#define REG_FE_MAC_LMIN(_n)           ((_n) + 0x04)
+-#define REG_FE_MAC_LMAX(_n)           ((_n) + 0x08)
+-
+-#define REG_FE_CDM1_OQ_MAP0           0x0050
+-#define REG_FE_CDM1_OQ_MAP1           0x0054
+-#define REG_FE_CDM1_OQ_MAP2           0x0058
+-#define REG_FE_CDM1_OQ_MAP3           0x005c
+-
+-#define REG_FE_PCE_CFG                        0x0070
+-#define PCE_DPI_EN_MASK                       BIT(2)
+-#define PCE_KA_EN_MASK                        BIT(1)
+-#define PCE_MC_EN_MASK                        BIT(0)
+-
+-#define REG_FE_PSE_QUEUE_CFG_WR               0x0080
+-#define PSE_CFG_PORT_ID_MASK          GENMASK(27, 24)
+-#define PSE_CFG_QUEUE_ID_MASK         GENMASK(20, 16)
+-#define PSE_CFG_WR_EN_MASK            BIT(8)
+-#define PSE_CFG_OQRSV_SEL_MASK                BIT(0)
+-
+-#define REG_FE_PSE_QUEUE_CFG_VAL      0x0084
+-#define PSE_CFG_OQ_RSV_MASK           GENMASK(13, 0)
+-
+-#define PSE_FQ_CFG                    0x008c
+-#define PSE_FQ_LIMIT_MASK             GENMASK(14, 0)
+-
+-#define REG_FE_PSE_BUF_SET            0x0090
+-#define PSE_SHARE_USED_LTHD_MASK      GENMASK(31, 16)
+-#define PSE_ALLRSV_MASK                       GENMASK(14, 0)
+-
+-#define REG_PSE_SHARE_USED_THD                0x0094
+-#define PSE_SHARE_USED_MTHD_MASK      GENMASK(31, 16)
+-#define PSE_SHARE_USED_HTHD_MASK      GENMASK(15, 0)
+-
+-#define REG_GDM_MISC_CFG              0x0148
+-#define GDM2_RDM_ACK_WAIT_PREF_MASK   BIT(9)
+-#define GDM2_CHN_VLD_MODE_MASK                BIT(5)
+-
+-#define REG_FE_CSR_IFC_CFG            CSR_IFC_BASE
+-#define FE_IFC_EN_MASK                        BIT(0)
+-
+-#define REG_FE_VIP_PORT_EN            0x01f0
+-#define REG_FE_IFC_PORT_EN            0x01f4
+-
+-#define REG_PSE_IQ_REV1                       (PSE_BASE + 0x08)
+-#define PSE_IQ_RES1_P2_MASK           GENMASK(23, 16)
+-
+-#define REG_PSE_IQ_REV2                       (PSE_BASE + 0x0c)
+-#define PSE_IQ_RES2_P5_MASK           GENMASK(15, 8)
+-#define PSE_IQ_RES2_P4_MASK           GENMASK(7, 0)
+-
+-#define REG_FE_VIP_EN(_n)             (0x0300 + ((_n) << 3))
+-#define PATN_FCPU_EN_MASK             BIT(7)
+-#define PATN_SWP_EN_MASK              BIT(6)
+-#define PATN_DP_EN_MASK                       BIT(5)
+-#define PATN_SP_EN_MASK                       BIT(4)
+-#define PATN_TYPE_MASK                        GENMASK(3, 1)
+-#define PATN_EN_MASK                  BIT(0)
+-
+-#define REG_FE_VIP_PATN(_n)           (0x0304 + ((_n) << 3))
+-#define PATN_DP_MASK                  GENMASK(31, 16)
+-#define PATN_SP_MASK                  GENMASK(15, 0)
+-
+-#define REG_CDM1_VLAN_CTRL            CDM1_BASE
+-#define CDM1_VLAN_MASK                        GENMASK(31, 16)
+-
+-#define REG_CDM1_FWD_CFG              (CDM1_BASE + 0x08)
+-#define CDM1_VIP_QSEL_MASK            GENMASK(24, 20)
+-
+-#define REG_CDM1_CRSN_QSEL(_n)                (CDM1_BASE + 0x10 + ((_n) << 2))
+-#define CDM1_CRSN_QSEL_REASON_MASK(_n)        \
+-      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
+-
+-#define REG_CDM2_FWD_CFG              (CDM2_BASE + 0x08)
+-#define CDM2_OAM_QSEL_MASK            GENMASK(31, 27)
+-#define CDM2_VIP_QSEL_MASK            GENMASK(24, 20)
+-
+-#define REG_CDM2_CRSN_QSEL(_n)                (CDM2_BASE + 0x10 + ((_n) << 2))
+-#define CDM2_CRSN_QSEL_REASON_MASK(_n)        \
+-      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
+-
+-#define REG_GDM_FWD_CFG(_n)           GDM_BASE(_n)
+-#define GDM_DROP_CRC_ERR              BIT(23)
+-#define GDM_IP4_CKSUM                 BIT(22)
+-#define GDM_TCP_CKSUM                 BIT(21)
+-#define GDM_UDP_CKSUM                 BIT(20)
+-#define GDM_UCFQ_MASK                 GENMASK(15, 12)
+-#define GDM_BCFQ_MASK                 GENMASK(11, 8)
+-#define GDM_MCFQ_MASK                 GENMASK(7, 4)
+-#define GDM_OCFQ_MASK                 GENMASK(3, 0)
+-
+-#define REG_GDM_INGRESS_CFG(_n)               (GDM_BASE(_n) + 0x10)
+-#define GDM_INGRESS_FC_EN_MASK                BIT(1)
+-#define GDM_STAG_EN_MASK              BIT(0)
+-
+-#define REG_GDM_LEN_CFG(_n)           (GDM_BASE(_n) + 0x14)
+-#define GDM_SHORT_LEN_MASK            GENMASK(13, 0)
+-#define GDM_LONG_LEN_MASK             GENMASK(29, 16)
+-
+-#define REG_FE_CPORT_CFG              (GDM1_BASE + 0x40)
+-#define FE_CPORT_PAD                  BIT(26)
+-#define FE_CPORT_PORT_XFC_MASK                BIT(25)
+-#define FE_CPORT_QUEUE_XFC_MASK               BIT(24)
+-
+-#define REG_FE_GDM_MIB_CLEAR(_n)      (GDM_BASE(_n) + 0xf0)
+-#define FE_GDM_MIB_RX_CLEAR_MASK      BIT(1)
+-#define FE_GDM_MIB_TX_CLEAR_MASK      BIT(0)
+-
+-#define REG_FE_GDM1_MIB_CFG           (GDM1_BASE + 0xf4)
+-#define FE_STRICT_RFC2819_MODE_MASK   BIT(31)
+-#define FE_GDM1_TX_MIB_SPLIT_EN_MASK  BIT(17)
+-#define FE_GDM1_RX_MIB_SPLIT_EN_MASK  BIT(16)
+-#define FE_TX_MIB_ID_MASK             GENMASK(15, 8)
+-#define FE_RX_MIB_ID_MASK             GENMASK(7, 0)
+-
+-#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x104)
+-#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x10c)
+-#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x110)
+-#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x114)
+-#define REG_FE_GDM_TX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x118)
+-#define REG_FE_GDM_TX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x11c)
+-#define REG_FE_GDM_TX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x120)
+-#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x124)
+-#define REG_FE_GDM_TX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x128)
+-#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x12c)
+-#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x130)
+-#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x134)
+-#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x138)
+-#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x13c)
+-#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x140)
+-
+-#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x148)
+-#define REG_FE_GDM_RX_FC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x14c)
+-#define REG_FE_GDM_RX_RC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x150)
+-#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n)   (GDM_BASE(_n) + 0x154)
+-#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n)      (GDM_BASE(_n) + 0x158)
+-#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x15c)
+-#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x160)
+-#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x164)
+-#define REG_FE_GDM_RX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x168)
+-#define REG_FE_GDM_RX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x16c)
+-#define REG_FE_GDM_RX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x170)
+-#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n)     (GDM_BASE(_n) + 0x174)
+-#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n)                (GDM_BASE(_n) + 0x178)
+-#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n)      (GDM_BASE(_n) + 0x17c)
+-#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x180)
+-#define REG_FE_GDM_RX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x184)
+-#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x188)
+-#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x18c)
+-#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x190)
+-#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x194)
+-#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x198)
+-#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x19c)
+-
+-#define REG_PPE1_TB_HASH_CFG          (PPE1_BASE + 0x250)
+-#define PPE1_SRAM_TABLE_EN_MASK               BIT(0)
+-#define PPE1_SRAM_HASH1_EN_MASK               BIT(8)
+-#define PPE1_DRAM_TABLE_EN_MASK               BIT(16)
+-#define PPE1_DRAM_HASH1_EN_MASK               BIT(24)
+-
+-#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x280)
+-#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x284)
+-#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x288)
+-#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x28c)
+-
+-#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x290)
+-#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x294)
+-#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x298)
+-#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x29c)
+-#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2b8)
+-#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2bc)
+-#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2c0)
+-#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2c4)
+-#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2c8)
+-#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2cc)
+-#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2e8)
+-#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2ec)
+-#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2f0)
+-#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2f4)
+-#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2f8)
+-#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2fc)
+-
+-#define REG_GDM2_CHN_RLS              (GDM2_BASE + 0x20)
+-#define MBI_RX_AGE_SEL_MASK           GENMASK(26, 25)
+-#define MBI_TX_AGE_SEL_MASK           GENMASK(18, 17)
+-
+-#define REG_GDM3_FWD_CFG              GDM3_BASE
+-#define GDM3_PAD_EN_MASK              BIT(28)
+-
+-#define REG_GDM4_FWD_CFG              GDM4_BASE
+-#define GDM4_PAD_EN_MASK              BIT(28)
+-#define GDM4_SPORT_OFFSET0_MASK               GENMASK(11, 8)
+-
+-#define REG_GDM4_SRC_PORT_SET         (GDM4_BASE + 0x23c)
+-#define GDM4_SPORT_OFF2_MASK          GENMASK(19, 16)
+-#define GDM4_SPORT_OFF1_MASK          GENMASK(15, 12)
+-#define GDM4_SPORT_OFF0_MASK          GENMASK(11, 8)
+-
+-#define REG_IP_FRAG_FP                        0x2010
+-#define IP_ASSEMBLE_PORT_MASK         GENMASK(24, 21)
+-#define IP_ASSEMBLE_NBQ_MASK          GENMASK(20, 16)
+-#define IP_FRAGMENT_PORT_MASK         GENMASK(8, 5)
+-#define IP_FRAGMENT_NBQ_MASK          GENMASK(4, 0)
+-
+-#define REG_MC_VLAN_EN                        0x2100
+-#define MC_VLAN_EN_MASK                       BIT(0)
+-
+-#define REG_MC_VLAN_CFG                       0x2104
+-#define MC_VLAN_CFG_CMD_DONE_MASK     BIT(31)
+-#define MC_VLAN_CFG_TABLE_ID_MASK     GENMASK(21, 16)
+-#define MC_VLAN_CFG_PORT_ID_MASK      GENMASK(11, 8)
+-#define MC_VLAN_CFG_TABLE_SEL_MASK    BIT(4)
+-#define MC_VLAN_CFG_RW_MASK           BIT(0)
+-
+-#define REG_MC_VLAN_DATA              0x2108
+-
+-#define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
+-
+-/* QDMA */
+-#define REG_QDMA_GLOBAL_CFG                   0x0004
+-#define GLOBAL_CFG_RX_2B_OFFSET_MASK          BIT(31)
+-#define GLOBAL_CFG_DMA_PREFERENCE_MASK                GENMASK(30, 29)
+-#define GLOBAL_CFG_CPU_TXR_RR_MASK            BIT(28)
+-#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK                BIT(27)
+-#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK     BIT(26)
+-#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK   BIT(25)
+-#define GLOBAL_CFG_OAM_MODIFY_MASK            BIT(24)
+-#define GLOBAL_CFG_RESET_MASK                 BIT(23)
+-#define GLOBAL_CFG_RESET_DONE_MASK            BIT(22)
+-#define GLOBAL_CFG_MULTICAST_EN_MASK          BIT(21)
+-#define GLOBAL_CFG_IRQ1_EN_MASK                       BIT(20)
+-#define GLOBAL_CFG_IRQ0_EN_MASK                       BIT(19)
+-#define GLOBAL_CFG_LOOPCNT_EN_MASK            BIT(18)
+-#define GLOBAL_CFG_RD_BYPASS_WR_MASK          BIT(17)
+-#define GLOBAL_CFG_QDMA_LOOPBACK_MASK         BIT(16)
+-#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK          GENMASK(13, 8)
+-#define GLOBAL_CFG_CHECK_DONE_MASK            BIT(7)
+-#define GLOBAL_CFG_TX_WB_DONE_MASK            BIT(6)
+-#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK         GENMASK(5, 4)
+-#define GLOBAL_CFG_RX_DMA_BUSY_MASK           BIT(3)
+-#define GLOBAL_CFG_RX_DMA_EN_MASK             BIT(2)
+-#define GLOBAL_CFG_TX_DMA_BUSY_MASK           BIT(1)
+-#define GLOBAL_CFG_TX_DMA_EN_MASK             BIT(0)
+-
+-#define REG_FWD_DSCP_BASE                     0x0010
+-#define REG_FWD_BUF_BASE                      0x0014
+-
+-#define REG_HW_FWD_DSCP_CFG                   0x0018
+-#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK         GENMASK(29, 28)
+-#define HW_FWD_DSCP_SCATTER_LEN_MASK          GENMASK(17, 16)
+-#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK      GENMASK(15, 0)
+-
+-#define REG_INT_STATUS(_n)            \
+-      (((_n) == 4) ? 0x0730 :         \
+-       ((_n) == 3) ? 0x0724 :         \
+-       ((_n) == 2) ? 0x0720 :         \
+-       ((_n) == 1) ? 0x0024 : 0x0020)
+-
+-#define REG_INT_ENABLE(_n)            \
+-      (((_n) == 4) ? 0x0750 :         \
+-       ((_n) == 3) ? 0x0744 :         \
+-       ((_n) == 2) ? 0x0740 :         \
+-       ((_n) == 1) ? 0x002c : 0x0028)
+-
+-/* QDMA_CSR_INT_ENABLE1 */
+-#define RX15_COHERENT_INT_MASK                BIT(31)
+-#define RX14_COHERENT_INT_MASK                BIT(30)
+-#define RX13_COHERENT_INT_MASK                BIT(29)
+-#define RX12_COHERENT_INT_MASK                BIT(28)
+-#define RX11_COHERENT_INT_MASK                BIT(27)
+-#define RX10_COHERENT_INT_MASK                BIT(26)
+-#define RX9_COHERENT_INT_MASK         BIT(25)
+-#define RX8_COHERENT_INT_MASK         BIT(24)
+-#define RX7_COHERENT_INT_MASK         BIT(23)
+-#define RX6_COHERENT_INT_MASK         BIT(22)
+-#define RX5_COHERENT_INT_MASK         BIT(21)
+-#define RX4_COHERENT_INT_MASK         BIT(20)
+-#define RX3_COHERENT_INT_MASK         BIT(19)
+-#define RX2_COHERENT_INT_MASK         BIT(18)
+-#define RX1_COHERENT_INT_MASK         BIT(17)
+-#define RX0_COHERENT_INT_MASK         BIT(16)
+-#define TX7_COHERENT_INT_MASK         BIT(15)
+-#define TX6_COHERENT_INT_MASK         BIT(14)
+-#define TX5_COHERENT_INT_MASK         BIT(13)
+-#define TX4_COHERENT_INT_MASK         BIT(12)
+-#define TX3_COHERENT_INT_MASK         BIT(11)
+-#define TX2_COHERENT_INT_MASK         BIT(10)
+-#define TX1_COHERENT_INT_MASK         BIT(9)
+-#define TX0_COHERENT_INT_MASK         BIT(8)
+-#define CNT_OVER_FLOW_INT_MASK                BIT(7)
+-#define IRQ1_FULL_INT_MASK            BIT(5)
+-#define IRQ1_INT_MASK                 BIT(4)
+-#define HWFWD_DSCP_LOW_INT_MASK               BIT(3)
+-#define HWFWD_DSCP_EMPTY_INT_MASK     BIT(2)
+-#define IRQ0_FULL_INT_MASK            BIT(1)
+-#define IRQ0_INT_MASK                 BIT(0)
+-
+-#define TX_DONE_INT_MASK(_n)                                  \
+-      ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK              \
+-            : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
+-
+-#define INT_TX_MASK                                           \
+-      (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK |                   \
+-       IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
+-
+-#define INT_IDX0_MASK                                         \
+-      (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK |        \
+-       TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK |        \
+-       TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK |        \
+-       TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK |        \
+-       RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK |        \
+-       RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK |        \
+-       RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK |        \
+-       RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK |        \
+-       RX15_COHERENT_INT_MASK | INT_TX_MASK)
+-
+-/* QDMA_CSR_INT_ENABLE2 */
+-#define RX15_NO_CPU_DSCP_INT_MASK     BIT(31)
+-#define RX14_NO_CPU_DSCP_INT_MASK     BIT(30)
+-#define RX13_NO_CPU_DSCP_INT_MASK     BIT(29)
+-#define RX12_NO_CPU_DSCP_INT_MASK     BIT(28)
+-#define RX11_NO_CPU_DSCP_INT_MASK     BIT(27)
+-#define RX10_NO_CPU_DSCP_INT_MASK     BIT(26)
+-#define RX9_NO_CPU_DSCP_INT_MASK      BIT(25)
+-#define RX8_NO_CPU_DSCP_INT_MASK      BIT(24)
+-#define RX7_NO_CPU_DSCP_INT_MASK      BIT(23)
+-#define RX6_NO_CPU_DSCP_INT_MASK      BIT(22)
+-#define RX5_NO_CPU_DSCP_INT_MASK      BIT(21)
+-#define RX4_NO_CPU_DSCP_INT_MASK      BIT(20)
+-#define RX3_NO_CPU_DSCP_INT_MASK      BIT(19)
+-#define RX2_NO_CPU_DSCP_INT_MASK      BIT(18)
+-#define RX1_NO_CPU_DSCP_INT_MASK      BIT(17)
+-#define RX0_NO_CPU_DSCP_INT_MASK      BIT(16)
+-#define RX15_DONE_INT_MASK            BIT(15)
+-#define RX14_DONE_INT_MASK            BIT(14)
+-#define RX13_DONE_INT_MASK            BIT(13)
+-#define RX12_DONE_INT_MASK            BIT(12)
+-#define RX11_DONE_INT_MASK            BIT(11)
+-#define RX10_DONE_INT_MASK            BIT(10)
+-#define RX9_DONE_INT_MASK             BIT(9)
+-#define RX8_DONE_INT_MASK             BIT(8)
+-#define RX7_DONE_INT_MASK             BIT(7)
+-#define RX6_DONE_INT_MASK             BIT(6)
+-#define RX5_DONE_INT_MASK             BIT(5)
+-#define RX4_DONE_INT_MASK             BIT(4)
+-#define RX3_DONE_INT_MASK             BIT(3)
+-#define RX2_DONE_INT_MASK             BIT(2)
+-#define RX1_DONE_INT_MASK             BIT(1)
+-#define RX0_DONE_INT_MASK             BIT(0)
+-
+-#define RX_DONE_INT_MASK                                      \
+-      (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK |                \
+-       RX2_DONE_INT_MASK | RX3_DONE_INT_MASK |                \
+-       RX4_DONE_INT_MASK | RX7_DONE_INT_MASK |                \
+-       RX8_DONE_INT_MASK | RX9_DONE_INT_MASK |                \
+-       RX15_DONE_INT_MASK)
+-#define INT_IDX1_MASK                                         \
+-      (RX_DONE_INT_MASK |                                     \
+-       RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK |  \
+-       RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK |  \
+-       RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK |  \
+-       RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK |  \
+-       RX15_NO_CPU_DSCP_INT_MASK)
+-
+-/* QDMA_CSR_INT_ENABLE5 */
+-#define TX31_COHERENT_INT_MASK                BIT(31)
+-#define TX30_COHERENT_INT_MASK                BIT(30)
+-#define TX29_COHERENT_INT_MASK                BIT(29)
+-#define TX28_COHERENT_INT_MASK                BIT(28)
+-#define TX27_COHERENT_INT_MASK                BIT(27)
+-#define TX26_COHERENT_INT_MASK                BIT(26)
+-#define TX25_COHERENT_INT_MASK                BIT(25)
+-#define TX24_COHERENT_INT_MASK                BIT(24)
+-#define TX23_COHERENT_INT_MASK                BIT(23)
+-#define TX22_COHERENT_INT_MASK                BIT(22)
+-#define TX21_COHERENT_INT_MASK                BIT(21)
+-#define TX20_COHERENT_INT_MASK                BIT(20)
+-#define TX19_COHERENT_INT_MASK                BIT(19)
+-#define TX18_COHERENT_INT_MASK                BIT(18)
+-#define TX17_COHERENT_INT_MASK                BIT(17)
+-#define TX16_COHERENT_INT_MASK                BIT(16)
+-#define TX15_COHERENT_INT_MASK                BIT(15)
+-#define TX14_COHERENT_INT_MASK                BIT(14)
+-#define TX13_COHERENT_INT_MASK                BIT(13)
+-#define TX12_COHERENT_INT_MASK                BIT(12)
+-#define TX11_COHERENT_INT_MASK                BIT(11)
+-#define TX10_COHERENT_INT_MASK                BIT(10)
+-#define TX9_COHERENT_INT_MASK         BIT(9)
+-#define TX8_COHERENT_INT_MASK         BIT(8)
+-
+-#define INT_IDX4_MASK                                         \
+-      (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK |        \
+-       TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK |      \
+-       TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK |      \
+-       TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK |      \
+-       TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK |      \
+-       TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK |      \
+-       TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK |      \
+-       TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK |      \
+-       TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK |      \
+-       TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK |      \
+-       TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK |      \
+-       TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK)
+-
+-#define REG_TX_IRQ_BASE(_n)           ((_n) ? 0x0048 : 0x0050)
+-
+-#define REG_TX_IRQ_CFG(_n)            ((_n) ? 0x004c : 0x0054)
+-#define TX_IRQ_THR_MASK                       GENMASK(27, 16)
+-#define TX_IRQ_DEPTH_MASK             GENMASK(11, 0)
+-
+-#define REG_IRQ_CLEAR_LEN(_n)         ((_n) ? 0x0064 : 0x0058)
+-#define IRQ_CLEAR_LEN_MASK            GENMASK(7, 0)
+-
+-#define REG_IRQ_STATUS(_n)            ((_n) ? 0x0068 : 0x005c)
+-#define IRQ_ENTRY_LEN_MASK            GENMASK(27, 16)
+-#define IRQ_HEAD_IDX_MASK             GENMASK(11, 0)
+-
+-#define REG_TX_RING_BASE(_n)  \
+-      (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5))
+-
+-#define REG_TX_RING_BLOCKING(_n)      \
+-      (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5))
+-
+-#define TX_RING_IRQ_BLOCKING_MAP_MASK                 BIT(6)
+-#define TX_RING_IRQ_BLOCKING_CFG_MASK                 BIT(4)
+-#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK          BIT(2)
+-#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK    BIT(1)
+-#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK    BIT(0)
+-
+-#define REG_TX_CPU_IDX(_n)    \
+-      (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5))
+-
+-#define TX_RING_CPU_IDX_MASK          GENMASK(15, 0)
+-
+-#define REG_TX_DMA_IDX(_n)    \
+-      (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5))
+-
+-#define TX_RING_DMA_IDX_MASK          GENMASK(15, 0)
+-
+-#define IRQ_RING_IDX_MASK             GENMASK(20, 16)
+-#define IRQ_DESC_IDX_MASK             GENMASK(15, 0)
+-
+-#define REG_RX_RING_BASE(_n)  \
+-      (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5))
+-
+-#define REG_RX_RING_SIZE(_n)  \
+-      (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5))
+-
+-#define RX_RING_THR_MASK              GENMASK(31, 16)
+-#define RX_RING_SIZE_MASK             GENMASK(15, 0)
+-
+-#define REG_RX_CPU_IDX(_n)    \
+-      (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5))
+-
+-#define RX_RING_CPU_IDX_MASK          GENMASK(15, 0)
+-
+-#define REG_RX_DMA_IDX(_n)    \
+-      (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5))
+-
+-#define REG_RX_DELAY_INT_IDX(_n)      \
+-      (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5))
+-
+-#define RX_DELAY_INT_MASK             GENMASK(15, 0)
+-
+-#define RX_RING_DMA_IDX_MASK          GENMASK(15, 0)
+-
+-#define REG_INGRESS_TRTCM_CFG         0x0070
+-#define INGRESS_TRTCM_EN_MASK         BIT(31)
+-#define INGRESS_TRTCM_MODE_MASK               BIT(30)
+-#define INGRESS_SLOW_TICK_RATIO_MASK  GENMASK(29, 16)
+-#define INGRESS_FAST_TICK_MASK                GENMASK(15, 0)
+-
+-#define REG_QUEUE_CLOSE_CFG(_n)               (0x00a0 + ((_n) & 0xfc))
+-#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m)   BIT((_m) + (((_n) & 0x3) << 3))
+-
+-#define REG_TXQ_DIS_CFG_BASE(_n)      ((_n) ? 0x20a0 : 0x00a0)
+-#define REG_TXQ_DIS_CFG(_n, _m)               (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2)
+-
+-#define REG_CNTR_CFG(_n)              (0x0400 + ((_n) << 3))
+-#define CNTR_EN_MASK                  BIT(31)
+-#define CNTR_ALL_CHAN_EN_MASK         BIT(30)
+-#define CNTR_ALL_QUEUE_EN_MASK                BIT(29)
+-#define CNTR_ALL_DSCP_RING_EN_MASK    BIT(28)
+-#define CNTR_SRC_MASK                 GENMASK(27, 24)
+-#define CNTR_DSCP_RING_MASK           GENMASK(20, 16)
+-#define CNTR_CHAN_MASK                        GENMASK(7, 3)
+-#define CNTR_QUEUE_MASK                       GENMASK(2, 0)
+-
+-#define REG_CNTR_VAL(_n)              (0x0404 + ((_n) << 3))
+-
+-#define REG_LMGR_INIT_CFG             0x1000
+-#define LMGR_INIT_START                       BIT(31)
+-#define LMGR_SRAM_MODE_MASK           BIT(30)
+-#define HW_FWD_PKTSIZE_OVERHEAD_MASK  GENMASK(27, 20)
+-#define HW_FWD_DESC_NUM_MASK          GENMASK(16, 0)
+-
+-#define REG_FWD_DSCP_LOW_THR          0x1004
+-#define FWD_DSCP_LOW_THR_MASK         GENMASK(17, 0)
+-
+-#define REG_EGRESS_RATE_METER_CFG             0x100c
+-#define EGRESS_RATE_METER_EN_MASK             BIT(31)
+-#define EGRESS_RATE_METER_EQ_RATE_EN_MASK     BIT(17)
+-#define EGRESS_RATE_METER_WINDOW_SZ_MASK      GENMASK(16, 12)
+-#define EGRESS_RATE_METER_TIMESLICE_MASK      GENMASK(10, 0)
+-
+-#define REG_EGRESS_TRTCM_CFG          0x1010
+-#define EGRESS_TRTCM_EN_MASK          BIT(31)
+-#define EGRESS_TRTCM_MODE_MASK                BIT(30)
+-#define EGRESS_SLOW_TICK_RATIO_MASK   GENMASK(29, 16)
+-#define EGRESS_FAST_TICK_MASK         GENMASK(15, 0)
+-
+-#define TRTCM_PARAM_RW_MASK           BIT(31)
+-#define TRTCM_PARAM_RW_DONE_MASK      BIT(30)
+-#define TRTCM_PARAM_TYPE_MASK         GENMASK(29, 28)
+-#define TRTCM_METER_GROUP_MASK                GENMASK(27, 26)
+-#define TRTCM_PARAM_INDEX_MASK                GENMASK(23, 17)
+-#define TRTCM_PARAM_RATE_TYPE_MASK    BIT(16)
+-
+-#define REG_TRTCM_CFG_PARAM(_n)               ((_n) + 0x4)
+-#define REG_TRTCM_DATA_LOW(_n)                ((_n) + 0x8)
+-#define REG_TRTCM_DATA_HIGH(_n)               ((_n) + 0xc)
+-
+-#define REG_TXWRR_MODE_CFG            0x1020
+-#define TWRR_WEIGHT_SCALE_MASK                BIT(31)
+-#define TWRR_WEIGHT_BASE_MASK         BIT(3)
+-
+-#define REG_TXWRR_WEIGHT_CFG          0x1024
+-#define TWRR_RW_CMD_MASK              BIT(31)
+-#define TWRR_RW_CMD_DONE              BIT(30)
+-#define TWRR_CHAN_IDX_MASK            GENMASK(23, 19)
+-#define TWRR_QUEUE_IDX_MASK           GENMASK(18, 16)
+-#define TWRR_VALUE_MASK                       GENMASK(15, 0)
+-
+-#define REG_PSE_BUF_USAGE_CFG         0x1028
+-#define PSE_BUF_ESTIMATE_EN_MASK      BIT(29)
+-
+-#define REG_CHAN_QOS_MODE(_n)         (0x1040 + ((_n) << 2))
+-#define CHAN_QOS_MODE_MASK(_n)                GENMASK(2 + ((_n) << 2), (_n) << 2)
+-
+-#define REG_GLB_TRTCM_CFG             0x1080
+-#define GLB_TRTCM_EN_MASK             BIT(31)
+-#define GLB_TRTCM_MODE_MASK           BIT(30)
+-#define GLB_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
+-#define GLB_FAST_TICK_MASK            GENMASK(15, 0)
+-
+-#define REG_TXQ_CNGST_CFG             0x10a0
+-#define TXQ_CNGST_DROP_EN             BIT(31)
+-#define TXQ_CNGST_DEI_DROP_EN         BIT(30)
+-
+-#define REG_SLA_TRTCM_CFG             0x1150
+-#define SLA_TRTCM_EN_MASK             BIT(31)
+-#define SLA_TRTCM_MODE_MASK           BIT(30)
+-#define SLA_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
+-#define SLA_FAST_TICK_MASK            GENMASK(15, 0)
+-
+-/* CTRL */
+-#define QDMA_DESC_DONE_MASK           BIT(31)
+-#define QDMA_DESC_DROP_MASK           BIT(30) /* tx: drop - rx: overflow */
+-#define QDMA_DESC_MORE_MASK           BIT(29) /* more SG elements */
+-#define QDMA_DESC_DEI_MASK            BIT(25)
+-#define QDMA_DESC_NO_DROP_MASK                BIT(24)
+-#define QDMA_DESC_LEN_MASK            GENMASK(15, 0)
+-/* DATA */
+-#define QDMA_DESC_NEXT_ID_MASK                GENMASK(15, 0)
+-/* TX MSG0 */
+-#define QDMA_ETH_TXMSG_MIC_IDX_MASK   BIT(30)
+-#define QDMA_ETH_TXMSG_SP_TAG_MASK    GENMASK(29, 14)
+-#define QDMA_ETH_TXMSG_ICO_MASK               BIT(13)
+-#define QDMA_ETH_TXMSG_UCO_MASK               BIT(12)
+-#define QDMA_ETH_TXMSG_TCO_MASK               BIT(11)
+-#define QDMA_ETH_TXMSG_TSO_MASK               BIT(10)
+-#define QDMA_ETH_TXMSG_FAST_MASK      BIT(9)
+-#define QDMA_ETH_TXMSG_OAM_MASK               BIT(8)
+-#define QDMA_ETH_TXMSG_CHAN_MASK      GENMASK(7, 3)
+-#define QDMA_ETH_TXMSG_QUEUE_MASK     GENMASK(2, 0)
+-/* TX MSG1 */
+-#define QDMA_ETH_TXMSG_NO_DROP                BIT(31)
+-#define QDMA_ETH_TXMSG_METER_MASK     GENMASK(30, 24) /* 0x7f no meters */
+-#define QDMA_ETH_TXMSG_FPORT_MASK     GENMASK(23, 20)
+-#define QDMA_ETH_TXMSG_NBOQ_MASK      GENMASK(19, 15)
+-#define QDMA_ETH_TXMSG_HWF_MASK               BIT(14)
+-#define QDMA_ETH_TXMSG_HOP_MASK               BIT(13)
+-#define QDMA_ETH_TXMSG_PTP_MASK               BIT(12)
+-#define QDMA_ETH_TXMSG_ACNT_G1_MASK   GENMASK(10, 6)  /* 0x1f do not count */
+-#define QDMA_ETH_TXMSG_ACNT_G0_MASK   GENMASK(5, 0)   /* 0x3f do not count */
+-
+-/* RX MSG1 */
+-#define QDMA_ETH_RXMSG_DEI_MASK               BIT(31)
+-#define QDMA_ETH_RXMSG_IP6_MASK               BIT(30)
+-#define QDMA_ETH_RXMSG_IP4_MASK               BIT(29)
+-#define QDMA_ETH_RXMSG_IP4F_MASK      BIT(28)
+-#define QDMA_ETH_RXMSG_L4_VALID_MASK  BIT(27)
+-#define QDMA_ETH_RXMSG_L4F_MASK               BIT(26)
+-#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)
+-
+-struct airoha_qdma_desc {
+-      __le32 rsv;
+-      __le32 ctrl;
+-      __le32 addr;
+-      __le32 data;
+-      __le32 msg0;
+-      __le32 msg1;
+-      __le32 msg2;
+-      __le32 msg3;
+-};
+-
+-/* CTRL0 */
+-#define QDMA_FWD_DESC_CTX_MASK                BIT(31)
+-#define QDMA_FWD_DESC_RING_MASK               GENMASK(30, 28)
+-#define QDMA_FWD_DESC_IDX_MASK                GENMASK(27, 16)
+-#define QDMA_FWD_DESC_LEN_MASK                GENMASK(15, 0)
+-/* CTRL1 */
+-#define QDMA_FWD_DESC_FIRST_IDX_MASK  GENMASK(15, 0)
+-/* CTRL2 */
+-#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK       GENMASK(2, 0)
+-
+-struct airoha_qdma_fwd_desc {
+-      __le32 addr;
+-      __le32 ctrl0;
+-      __le32 ctrl1;
+-      __le32 ctrl2;
+-      __le32 msg0;
+-      __le32 msg1;
+-      __le32 rsv0;
+-      __le32 rsv1;
+-};
+-
+ u32 airoha_rr(void __iomem *base, u32 offset)
+ {
+       return readl(base + offset);
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_regs.h
+@@ -0,0 +1,670 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2024 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++
++#ifndef AIROHA_REGS_H
++#define AIROHA_REGS_H
++
++#include <linux/types.h>
++
++/* FE */
++#define PSE_BASE                      0x0100
++#define CSR_IFC_BASE                  0x0200
++#define CDM1_BASE                     0x0400
++#define GDM1_BASE                     0x0500
++#define PPE1_BASE                     0x0c00
++
++#define CDM2_BASE                     0x1400
++#define GDM2_BASE                     0x1500
++
++#define GDM3_BASE                     0x1100
++#define GDM4_BASE                     0x2500
++
++#define GDM_BASE(_n)                  \
++      ((_n) == 4 ? GDM4_BASE :        \
++       (_n) == 3 ? GDM3_BASE :        \
++       (_n) == 2 ? GDM2_BASE : GDM1_BASE)
++
++#define REG_FE_DMA_GLO_CFG            0x0000
++#define FE_DMA_GLO_L2_SPACE_MASK      GENMASK(7, 4)
++#define FE_DMA_GLO_PG_SZ_MASK         BIT(3)
++
++#define REG_FE_RST_GLO_CFG            0x0004
++#define FE_RST_GDM4_MBI_ARB_MASK      BIT(3)
++#define FE_RST_GDM3_MBI_ARB_MASK      BIT(2)
++#define FE_RST_CORE_MASK              BIT(0)
++
++#define REG_FE_WAN_MAC_H              0x0030
++#define REG_FE_LAN_MAC_H              0x0040
++
++#define REG_FE_MAC_LMIN(_n)           ((_n) + 0x04)
++#define REG_FE_MAC_LMAX(_n)           ((_n) + 0x08)
++
++#define REG_FE_CDM1_OQ_MAP0           0x0050
++#define REG_FE_CDM1_OQ_MAP1           0x0054
++#define REG_FE_CDM1_OQ_MAP2           0x0058
++#define REG_FE_CDM1_OQ_MAP3           0x005c
++
++#define REG_FE_PCE_CFG                        0x0070
++#define PCE_DPI_EN_MASK                       BIT(2)
++#define PCE_KA_EN_MASK                        BIT(1)
++#define PCE_MC_EN_MASK                        BIT(0)
++
++#define REG_FE_PSE_QUEUE_CFG_WR               0x0080
++#define PSE_CFG_PORT_ID_MASK          GENMASK(27, 24)
++#define PSE_CFG_QUEUE_ID_MASK         GENMASK(20, 16)
++#define PSE_CFG_WR_EN_MASK            BIT(8)
++#define PSE_CFG_OQRSV_SEL_MASK                BIT(0)
++
++#define REG_FE_PSE_QUEUE_CFG_VAL      0x0084
++#define PSE_CFG_OQ_RSV_MASK           GENMASK(13, 0)
++
++#define PSE_FQ_CFG                    0x008c
++#define PSE_FQ_LIMIT_MASK             GENMASK(14, 0)
++
++#define REG_FE_PSE_BUF_SET            0x0090
++#define PSE_SHARE_USED_LTHD_MASK      GENMASK(31, 16)
++#define PSE_ALLRSV_MASK                       GENMASK(14, 0)
++
++#define REG_PSE_SHARE_USED_THD                0x0094
++#define PSE_SHARE_USED_MTHD_MASK      GENMASK(31, 16)
++#define PSE_SHARE_USED_HTHD_MASK      GENMASK(15, 0)
++
++#define REG_GDM_MISC_CFG              0x0148
++#define GDM2_RDM_ACK_WAIT_PREF_MASK   BIT(9)
++#define GDM2_CHN_VLD_MODE_MASK                BIT(5)
++
++#define REG_FE_CSR_IFC_CFG            CSR_IFC_BASE
++#define FE_IFC_EN_MASK                        BIT(0)
++
++#define REG_FE_VIP_PORT_EN            0x01f0
++#define REG_FE_IFC_PORT_EN            0x01f4
++
++#define REG_PSE_IQ_REV1                       (PSE_BASE + 0x08)
++#define PSE_IQ_RES1_P2_MASK           GENMASK(23, 16)
++
++#define REG_PSE_IQ_REV2                       (PSE_BASE + 0x0c)
++#define PSE_IQ_RES2_P5_MASK           GENMASK(15, 8)
++#define PSE_IQ_RES2_P4_MASK           GENMASK(7, 0)
++
++#define REG_FE_VIP_EN(_n)             (0x0300 + ((_n) << 3))
++#define PATN_FCPU_EN_MASK             BIT(7)
++#define PATN_SWP_EN_MASK              BIT(6)
++#define PATN_DP_EN_MASK                       BIT(5)
++#define PATN_SP_EN_MASK                       BIT(4)
++#define PATN_TYPE_MASK                        GENMASK(3, 1)
++#define PATN_EN_MASK                  BIT(0)
++
++#define REG_FE_VIP_PATN(_n)           (0x0304 + ((_n) << 3))
++#define PATN_DP_MASK                  GENMASK(31, 16)
++#define PATN_SP_MASK                  GENMASK(15, 0)
++
++#define REG_CDM1_VLAN_CTRL            CDM1_BASE
++#define CDM1_VLAN_MASK                        GENMASK(31, 16)
++
++#define REG_CDM1_FWD_CFG              (CDM1_BASE + 0x08)
++#define CDM1_VIP_QSEL_MASK            GENMASK(24, 20)
++
++#define REG_CDM1_CRSN_QSEL(_n)                (CDM1_BASE + 0x10 + ((_n) << 2))
++#define CDM1_CRSN_QSEL_REASON_MASK(_n)        \
++      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
++
++#define REG_CDM2_FWD_CFG              (CDM2_BASE + 0x08)
++#define CDM2_OAM_QSEL_MASK            GENMASK(31, 27)
++#define CDM2_VIP_QSEL_MASK            GENMASK(24, 20)
++
++#define REG_CDM2_CRSN_QSEL(_n)                (CDM2_BASE + 0x10 + ((_n) << 2))
++#define CDM2_CRSN_QSEL_REASON_MASK(_n)        \
++      GENMASK(4 + (((_n) % 4) << 3),  (((_n) % 4) << 3))
++
++#define REG_GDM_FWD_CFG(_n)           GDM_BASE(_n)
++#define GDM_DROP_CRC_ERR              BIT(23)
++#define GDM_IP4_CKSUM                 BIT(22)
++#define GDM_TCP_CKSUM                 BIT(21)
++#define GDM_UDP_CKSUM                 BIT(20)
++#define GDM_UCFQ_MASK                 GENMASK(15, 12)
++#define GDM_BCFQ_MASK                 GENMASK(11, 8)
++#define GDM_MCFQ_MASK                 GENMASK(7, 4)
++#define GDM_OCFQ_MASK                 GENMASK(3, 0)
++
++#define REG_GDM_INGRESS_CFG(_n)               (GDM_BASE(_n) + 0x10)
++#define GDM_INGRESS_FC_EN_MASK                BIT(1)
++#define GDM_STAG_EN_MASK              BIT(0)
++
++#define REG_GDM_LEN_CFG(_n)           (GDM_BASE(_n) + 0x14)
++#define GDM_SHORT_LEN_MASK            GENMASK(13, 0)
++#define GDM_LONG_LEN_MASK             GENMASK(29, 16)
++
++#define REG_FE_CPORT_CFG              (GDM1_BASE + 0x40)
++#define FE_CPORT_PAD                  BIT(26)
++#define FE_CPORT_PORT_XFC_MASK                BIT(25)
++#define FE_CPORT_QUEUE_XFC_MASK               BIT(24)
++
++#define REG_FE_GDM_MIB_CLEAR(_n)      (GDM_BASE(_n) + 0xf0)
++#define FE_GDM_MIB_RX_CLEAR_MASK      BIT(1)
++#define FE_GDM_MIB_TX_CLEAR_MASK      BIT(0)
++
++#define REG_FE_GDM1_MIB_CFG           (GDM1_BASE + 0xf4)
++#define FE_STRICT_RFC2819_MODE_MASK   BIT(31)
++#define FE_GDM1_TX_MIB_SPLIT_EN_MASK  BIT(17)
++#define FE_GDM1_RX_MIB_SPLIT_EN_MASK  BIT(16)
++#define FE_TX_MIB_ID_MASK             GENMASK(15, 8)
++#define FE_RX_MIB_ID_MASK             GENMASK(7, 0)
++
++#define REG_FE_GDM_TX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x104)
++#define REG_FE_GDM_TX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x10c)
++#define REG_FE_GDM_TX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x110)
++#define REG_FE_GDM_TX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x114)
++#define REG_FE_GDM_TX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x118)
++#define REG_FE_GDM_TX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x11c)
++#define REG_FE_GDM_TX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x120)
++#define REG_FE_GDM_TX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x124)
++#define REG_FE_GDM_TX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x128)
++#define REG_FE_GDM_TX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x12c)
++#define REG_FE_GDM_TX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x130)
++#define REG_FE_GDM_TX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x134)
++#define REG_FE_GDM_TX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x138)
++#define REG_FE_GDM_TX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x13c)
++#define REG_FE_GDM_TX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x140)
++
++#define REG_FE_GDM_RX_OK_PKT_CNT_L(_n)                (GDM_BASE(_n) + 0x148)
++#define REG_FE_GDM_RX_FC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x14c)
++#define REG_FE_GDM_RX_RC_DROP_CNT(_n)         (GDM_BASE(_n) + 0x150)
++#define REG_FE_GDM_RX_OVERFLOW_DROP_CNT(_n)   (GDM_BASE(_n) + 0x154)
++#define REG_FE_GDM_RX_ERROR_DROP_CNT(_n)      (GDM_BASE(_n) + 0x158)
++#define REG_FE_GDM_RX_OK_BYTE_CNT_L(_n)               (GDM_BASE(_n) + 0x15c)
++#define REG_FE_GDM_RX_ETH_PKT_CNT_L(_n)               (GDM_BASE(_n) + 0x160)
++#define REG_FE_GDM_RX_ETH_BYTE_CNT_L(_n)      (GDM_BASE(_n) + 0x164)
++#define REG_FE_GDM_RX_ETH_DROP_CNT(_n)                (GDM_BASE(_n) + 0x168)
++#define REG_FE_GDM_RX_ETH_BC_CNT(_n)          (GDM_BASE(_n) + 0x16c)
++#define REG_FE_GDM_RX_ETH_MC_CNT(_n)          (GDM_BASE(_n) + 0x170)
++#define REG_FE_GDM_RX_ETH_CRC_ERR_CNT(_n)     (GDM_BASE(_n) + 0x174)
++#define REG_FE_GDM_RX_ETH_FRAG_CNT(_n)                (GDM_BASE(_n) + 0x178)
++#define REG_FE_GDM_RX_ETH_JABBER_CNT(_n)      (GDM_BASE(_n) + 0x17c)
++#define REG_FE_GDM_RX_ETH_RUNT_CNT(_n)                (GDM_BASE(_n) + 0x180)
++#define REG_FE_GDM_RX_ETH_LONG_CNT(_n)                (GDM_BASE(_n) + 0x184)
++#define REG_FE_GDM_RX_ETH_E64_CNT_L(_n)               (GDM_BASE(_n) + 0x188)
++#define REG_FE_GDM_RX_ETH_L64_CNT_L(_n)               (GDM_BASE(_n) + 0x18c)
++#define REG_FE_GDM_RX_ETH_L127_CNT_L(_n)      (GDM_BASE(_n) + 0x190)
++#define REG_FE_GDM_RX_ETH_L255_CNT_L(_n)      (GDM_BASE(_n) + 0x194)
++#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x198)
++#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x19c)
++
++#define REG_PPE1_TB_HASH_CFG          (PPE1_BASE + 0x250)
++#define PPE1_SRAM_TABLE_EN_MASK               BIT(0)
++#define PPE1_SRAM_HASH1_EN_MASK               BIT(8)
++#define PPE1_DRAM_TABLE_EN_MASK               BIT(16)
++#define PPE1_DRAM_HASH1_EN_MASK               BIT(24)
++
++#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x280)
++#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x284)
++#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x288)
++#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x28c)
++
++#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x290)
++#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x294)
++#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n)               (GDM_BASE(_n) + 0x298)
++#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n)      (GDM_BASE(_n) + 0x29c)
++#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2b8)
++#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2bc)
++#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2c0)
++#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2c4)
++#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2c8)
++#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2cc)
++#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n)               (GDM_BASE(_n) + 0x2e8)
++#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n)               (GDM_BASE(_n) + 0x2ec)
++#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n)      (GDM_BASE(_n) + 0x2f0)
++#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n)      (GDM_BASE(_n) + 0x2f4)
++#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n)      (GDM_BASE(_n) + 0x2f8)
++#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n)     (GDM_BASE(_n) + 0x2fc)
++
++#define REG_GDM2_CHN_RLS              (GDM2_BASE + 0x20)
++#define MBI_RX_AGE_SEL_MASK           GENMASK(26, 25)
++#define MBI_TX_AGE_SEL_MASK           GENMASK(18, 17)
++
++#define REG_GDM3_FWD_CFG              GDM3_BASE
++#define GDM3_PAD_EN_MASK              BIT(28)
++
++#define REG_GDM4_FWD_CFG              GDM4_BASE
++#define GDM4_PAD_EN_MASK              BIT(28)
++#define GDM4_SPORT_OFFSET0_MASK               GENMASK(11, 8)
++
++#define REG_GDM4_SRC_PORT_SET         (GDM4_BASE + 0x23c)
++#define GDM4_SPORT_OFF2_MASK          GENMASK(19, 16)
++#define GDM4_SPORT_OFF1_MASK          GENMASK(15, 12)
++#define GDM4_SPORT_OFF0_MASK          GENMASK(11, 8)
++
++#define REG_IP_FRAG_FP                        0x2010
++#define IP_ASSEMBLE_PORT_MASK         GENMASK(24, 21)
++#define IP_ASSEMBLE_NBQ_MASK          GENMASK(20, 16)
++#define IP_FRAGMENT_PORT_MASK         GENMASK(8, 5)
++#define IP_FRAGMENT_NBQ_MASK          GENMASK(4, 0)
++
++#define REG_MC_VLAN_EN                        0x2100
++#define MC_VLAN_EN_MASK                       BIT(0)
++
++#define REG_MC_VLAN_CFG                       0x2104
++#define MC_VLAN_CFG_CMD_DONE_MASK     BIT(31)
++#define MC_VLAN_CFG_TABLE_ID_MASK     GENMASK(21, 16)
++#define MC_VLAN_CFG_PORT_ID_MASK      GENMASK(11, 8)
++#define MC_VLAN_CFG_TABLE_SEL_MASK    BIT(4)
++#define MC_VLAN_CFG_RW_MASK           BIT(0)
++
++#define REG_MC_VLAN_DATA              0x2108
++
++#define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
++
++/* QDMA */
++#define REG_QDMA_GLOBAL_CFG                   0x0004
++#define GLOBAL_CFG_RX_2B_OFFSET_MASK          BIT(31)
++#define GLOBAL_CFG_DMA_PREFERENCE_MASK                GENMASK(30, 29)
++#define GLOBAL_CFG_CPU_TXR_RR_MASK            BIT(28)
++#define GLOBAL_CFG_DSCP_BYTE_SWAP_MASK                BIT(27)
++#define GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK     BIT(26)
++#define GLOBAL_CFG_MULTICAST_MODIFY_FP_MASK   BIT(25)
++#define GLOBAL_CFG_OAM_MODIFY_MASK            BIT(24)
++#define GLOBAL_CFG_RESET_MASK                 BIT(23)
++#define GLOBAL_CFG_RESET_DONE_MASK            BIT(22)
++#define GLOBAL_CFG_MULTICAST_EN_MASK          BIT(21)
++#define GLOBAL_CFG_IRQ1_EN_MASK                       BIT(20)
++#define GLOBAL_CFG_IRQ0_EN_MASK                       BIT(19)
++#define GLOBAL_CFG_LOOPCNT_EN_MASK            BIT(18)
++#define GLOBAL_CFG_RD_BYPASS_WR_MASK          BIT(17)
++#define GLOBAL_CFG_QDMA_LOOPBACK_MASK         BIT(16)
++#define GLOBAL_CFG_LPBK_RXQ_SEL_MASK          GENMASK(13, 8)
++#define GLOBAL_CFG_CHECK_DONE_MASK            BIT(7)
++#define GLOBAL_CFG_TX_WB_DONE_MASK            BIT(6)
++#define GLOBAL_CFG_MAX_ISSUE_NUM_MASK         GENMASK(5, 4)
++#define GLOBAL_CFG_RX_DMA_BUSY_MASK           BIT(3)
++#define GLOBAL_CFG_RX_DMA_EN_MASK             BIT(2)
++#define GLOBAL_CFG_TX_DMA_BUSY_MASK           BIT(1)
++#define GLOBAL_CFG_TX_DMA_EN_MASK             BIT(0)
++
++#define REG_FWD_DSCP_BASE                     0x0010
++#define REG_FWD_BUF_BASE                      0x0014
++
++#define REG_HW_FWD_DSCP_CFG                   0x0018
++#define HW_FWD_DSCP_PAYLOAD_SIZE_MASK         GENMASK(29, 28)
++#define HW_FWD_DSCP_SCATTER_LEN_MASK          GENMASK(17, 16)
++#define HW_FWD_DSCP_MIN_SCATTER_LEN_MASK      GENMASK(15, 0)
++
++#define REG_INT_STATUS(_n)            \
++      (((_n) == 4) ? 0x0730 :         \
++       ((_n) == 3) ? 0x0724 :         \
++       ((_n) == 2) ? 0x0720 :         \
++       ((_n) == 1) ? 0x0024 : 0x0020)
++
++#define REG_INT_ENABLE(_n)            \
++      (((_n) == 4) ? 0x0750 :         \
++       ((_n) == 3) ? 0x0744 :         \
++       ((_n) == 2) ? 0x0740 :         \
++       ((_n) == 1) ? 0x002c : 0x0028)
++
++/* QDMA_CSR_INT_ENABLE1 */
++#define RX15_COHERENT_INT_MASK                BIT(31)
++#define RX14_COHERENT_INT_MASK                BIT(30)
++#define RX13_COHERENT_INT_MASK                BIT(29)
++#define RX12_COHERENT_INT_MASK                BIT(28)
++#define RX11_COHERENT_INT_MASK                BIT(27)
++#define RX10_COHERENT_INT_MASK                BIT(26)
++#define RX9_COHERENT_INT_MASK         BIT(25)
++#define RX8_COHERENT_INT_MASK         BIT(24)
++#define RX7_COHERENT_INT_MASK         BIT(23)
++#define RX6_COHERENT_INT_MASK         BIT(22)
++#define RX5_COHERENT_INT_MASK         BIT(21)
++#define RX4_COHERENT_INT_MASK         BIT(20)
++#define RX3_COHERENT_INT_MASK         BIT(19)
++#define RX2_COHERENT_INT_MASK         BIT(18)
++#define RX1_COHERENT_INT_MASK         BIT(17)
++#define RX0_COHERENT_INT_MASK         BIT(16)
++#define TX7_COHERENT_INT_MASK         BIT(15)
++#define TX6_COHERENT_INT_MASK         BIT(14)
++#define TX5_COHERENT_INT_MASK         BIT(13)
++#define TX4_COHERENT_INT_MASK         BIT(12)
++#define TX3_COHERENT_INT_MASK         BIT(11)
++#define TX2_COHERENT_INT_MASK         BIT(10)
++#define TX1_COHERENT_INT_MASK         BIT(9)
++#define TX0_COHERENT_INT_MASK         BIT(8)
++#define CNT_OVER_FLOW_INT_MASK                BIT(7)
++#define IRQ1_FULL_INT_MASK            BIT(5)
++#define IRQ1_INT_MASK                 BIT(4)
++#define HWFWD_DSCP_LOW_INT_MASK               BIT(3)
++#define HWFWD_DSCP_EMPTY_INT_MASK     BIT(2)
++#define IRQ0_FULL_INT_MASK            BIT(1)
++#define IRQ0_INT_MASK                 BIT(0)
++
++#define TX_DONE_INT_MASK(_n)                                  \
++      ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK              \
++            : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
++
++#define INT_TX_MASK                                           \
++      (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK |                   \
++       IRQ0_INT_MASK | IRQ0_FULL_INT_MASK)
++
++#define INT_IDX0_MASK                                         \
++      (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK |        \
++       TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK |        \
++       TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK |        \
++       TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK |        \
++       RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK |        \
++       RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK |        \
++       RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK |        \
++       RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK |        \
++       RX15_COHERENT_INT_MASK | INT_TX_MASK)
++
++/* QDMA_CSR_INT_ENABLE2 */
++#define RX15_NO_CPU_DSCP_INT_MASK     BIT(31)
++#define RX14_NO_CPU_DSCP_INT_MASK     BIT(30)
++#define RX13_NO_CPU_DSCP_INT_MASK     BIT(29)
++#define RX12_NO_CPU_DSCP_INT_MASK     BIT(28)
++#define RX11_NO_CPU_DSCP_INT_MASK     BIT(27)
++#define RX10_NO_CPU_DSCP_INT_MASK     BIT(26)
++#define RX9_NO_CPU_DSCP_INT_MASK      BIT(25)
++#define RX8_NO_CPU_DSCP_INT_MASK      BIT(24)
++#define RX7_NO_CPU_DSCP_INT_MASK      BIT(23)
++#define RX6_NO_CPU_DSCP_INT_MASK      BIT(22)
++#define RX5_NO_CPU_DSCP_INT_MASK      BIT(21)
++#define RX4_NO_CPU_DSCP_INT_MASK      BIT(20)
++#define RX3_NO_CPU_DSCP_INT_MASK      BIT(19)
++#define RX2_NO_CPU_DSCP_INT_MASK      BIT(18)
++#define RX1_NO_CPU_DSCP_INT_MASK      BIT(17)
++#define RX0_NO_CPU_DSCP_INT_MASK      BIT(16)
++#define RX15_DONE_INT_MASK            BIT(15)
++#define RX14_DONE_INT_MASK            BIT(14)
++#define RX13_DONE_INT_MASK            BIT(13)
++#define RX12_DONE_INT_MASK            BIT(12)
++#define RX11_DONE_INT_MASK            BIT(11)
++#define RX10_DONE_INT_MASK            BIT(10)
++#define RX9_DONE_INT_MASK             BIT(9)
++#define RX8_DONE_INT_MASK             BIT(8)
++#define RX7_DONE_INT_MASK             BIT(7)
++#define RX6_DONE_INT_MASK             BIT(6)
++#define RX5_DONE_INT_MASK             BIT(5)
++#define RX4_DONE_INT_MASK             BIT(4)
++#define RX3_DONE_INT_MASK             BIT(3)
++#define RX2_DONE_INT_MASK             BIT(2)
++#define RX1_DONE_INT_MASK             BIT(1)
++#define RX0_DONE_INT_MASK             BIT(0)
++
++#define RX_DONE_INT_MASK                                      \
++      (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK |                \
++       RX2_DONE_INT_MASK | RX3_DONE_INT_MASK |                \
++       RX4_DONE_INT_MASK | RX7_DONE_INT_MASK |                \
++       RX8_DONE_INT_MASK | RX9_DONE_INT_MASK |                \
++       RX15_DONE_INT_MASK)
++#define INT_IDX1_MASK                                         \
++      (RX_DONE_INT_MASK |                                     \
++       RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK |  \
++       RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK |  \
++       RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK |  \
++       RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK |  \
++       RX15_NO_CPU_DSCP_INT_MASK)
++
++/* QDMA_CSR_INT_ENABLE5 */
++#define TX31_COHERENT_INT_MASK                BIT(31)
++#define TX30_COHERENT_INT_MASK                BIT(30)
++#define TX29_COHERENT_INT_MASK                BIT(29)
++#define TX28_COHERENT_INT_MASK                BIT(28)
++#define TX27_COHERENT_INT_MASK                BIT(27)
++#define TX26_COHERENT_INT_MASK                BIT(26)
++#define TX25_COHERENT_INT_MASK                BIT(25)
++#define TX24_COHERENT_INT_MASK                BIT(24)
++#define TX23_COHERENT_INT_MASK                BIT(23)
++#define TX22_COHERENT_INT_MASK                BIT(22)
++#define TX21_COHERENT_INT_MASK                BIT(21)
++#define TX20_COHERENT_INT_MASK                BIT(20)
++#define TX19_COHERENT_INT_MASK                BIT(19)
++#define TX18_COHERENT_INT_MASK                BIT(18)
++#define TX17_COHERENT_INT_MASK                BIT(17)
++#define TX16_COHERENT_INT_MASK                BIT(16)
++#define TX15_COHERENT_INT_MASK                BIT(15)
++#define TX14_COHERENT_INT_MASK                BIT(14)
++#define TX13_COHERENT_INT_MASK                BIT(13)
++#define TX12_COHERENT_INT_MASK                BIT(12)
++#define TX11_COHERENT_INT_MASK                BIT(11)
++#define TX10_COHERENT_INT_MASK                BIT(10)
++#define TX9_COHERENT_INT_MASK         BIT(9)
++#define TX8_COHERENT_INT_MASK         BIT(8)
++
++#define INT_IDX4_MASK                                         \
++      (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK |        \
++       TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK |      \
++       TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK |      \
++       TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK |      \
++       TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK |      \
++       TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK |      \
++       TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK |      \
++       TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK |      \
++       TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK |      \
++       TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK |      \
++       TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK |      \
++       TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK)
++
++#define REG_TX_IRQ_BASE(_n)           ((_n) ? 0x0048 : 0x0050)
++
++#define REG_TX_IRQ_CFG(_n)            ((_n) ? 0x004c : 0x0054)
++#define TX_IRQ_THR_MASK                       GENMASK(27, 16)
++#define TX_IRQ_DEPTH_MASK             GENMASK(11, 0)
++
++#define REG_IRQ_CLEAR_LEN(_n)         ((_n) ? 0x0064 : 0x0058)
++#define IRQ_CLEAR_LEN_MASK            GENMASK(7, 0)
++
++#define REG_IRQ_STATUS(_n)            ((_n) ? 0x0068 : 0x005c)
++#define IRQ_ENTRY_LEN_MASK            GENMASK(27, 16)
++#define IRQ_HEAD_IDX_MASK             GENMASK(11, 0)
++
++#define REG_TX_RING_BASE(_n)  \
++      (((_n) < 8) ? 0x0100 + ((_n) << 5) : 0x0b00 + (((_n) - 8) << 5))
++
++#define REG_TX_RING_BLOCKING(_n)      \
++      (((_n) < 8) ? 0x0104 + ((_n) << 5) : 0x0b04 + (((_n) - 8) << 5))
++
++#define TX_RING_IRQ_BLOCKING_MAP_MASK                 BIT(6)
++#define TX_RING_IRQ_BLOCKING_CFG_MASK                 BIT(4)
++#define TX_RING_IRQ_BLOCKING_TX_DROP_EN_MASK          BIT(2)
++#define TX_RING_IRQ_BLOCKING_MAX_TH_TXRING_EN_MASK    BIT(1)
++#define TX_RING_IRQ_BLOCKING_MIN_TH_TXRING_EN_MASK    BIT(0)
++
++#define REG_TX_CPU_IDX(_n)    \
++      (((_n) < 8) ? 0x0108 + ((_n) << 5) : 0x0b08 + (((_n) - 8) << 5))
++
++#define TX_RING_CPU_IDX_MASK          GENMASK(15, 0)
++
++#define REG_TX_DMA_IDX(_n)    \
++      (((_n) < 8) ? 0x010c + ((_n) << 5) : 0x0b0c + (((_n) - 8) << 5))
++
++#define TX_RING_DMA_IDX_MASK          GENMASK(15, 0)
++
++#define IRQ_RING_IDX_MASK             GENMASK(20, 16)
++#define IRQ_DESC_IDX_MASK             GENMASK(15, 0)
++
++#define REG_RX_RING_BASE(_n)  \
++      (((_n) < 16) ? 0x0200 + ((_n) << 5) : 0x0e00 + (((_n) - 16) << 5))
++
++#define REG_RX_RING_SIZE(_n)  \
++      (((_n) < 16) ? 0x0204 + ((_n) << 5) : 0x0e04 + (((_n) - 16) << 5))
++
++#define RX_RING_THR_MASK              GENMASK(31, 16)
++#define RX_RING_SIZE_MASK             GENMASK(15, 0)
++
++#define REG_RX_CPU_IDX(_n)    \
++      (((_n) < 16) ? 0x0208 + ((_n) << 5) : 0x0e08 + (((_n) - 16) << 5))
++
++#define RX_RING_CPU_IDX_MASK          GENMASK(15, 0)
++
++#define REG_RX_DMA_IDX(_n)    \
++      (((_n) < 16) ? 0x020c + ((_n) << 5) : 0x0e0c + (((_n) - 16) << 5))
++
++#define REG_RX_DELAY_INT_IDX(_n)      \
++      (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5))
++
++#define RX_DELAY_INT_MASK             GENMASK(15, 0)
++
++#define RX_RING_DMA_IDX_MASK          GENMASK(15, 0)
++
++#define REG_INGRESS_TRTCM_CFG         0x0070
++#define INGRESS_TRTCM_EN_MASK         BIT(31)
++#define INGRESS_TRTCM_MODE_MASK               BIT(30)
++#define INGRESS_SLOW_TICK_RATIO_MASK  GENMASK(29, 16)
++#define INGRESS_FAST_TICK_MASK                GENMASK(15, 0)
++
++#define REG_QUEUE_CLOSE_CFG(_n)               (0x00a0 + ((_n) & 0xfc))
++#define TXQ_DISABLE_CHAN_QUEUE_MASK(_n, _m)   BIT((_m) + (((_n) & 0x3) << 3))
++
++#define REG_TXQ_DIS_CFG_BASE(_n)      ((_n) ? 0x20a0 : 0x00a0)
++#define REG_TXQ_DIS_CFG(_n, _m)               (REG_TXQ_DIS_CFG_BASE((_n)) + (_m) << 2)
++
++#define REG_CNTR_CFG(_n)              (0x0400 + ((_n) << 3))
++#define CNTR_EN_MASK                  BIT(31)
++#define CNTR_ALL_CHAN_EN_MASK         BIT(30)
++#define CNTR_ALL_QUEUE_EN_MASK                BIT(29)
++#define CNTR_ALL_DSCP_RING_EN_MASK    BIT(28)
++#define CNTR_SRC_MASK                 GENMASK(27, 24)
++#define CNTR_DSCP_RING_MASK           GENMASK(20, 16)
++#define CNTR_CHAN_MASK                        GENMASK(7, 3)
++#define CNTR_QUEUE_MASK                       GENMASK(2, 0)
++
++#define REG_CNTR_VAL(_n)              (0x0404 + ((_n) << 3))
++
++#define REG_LMGR_INIT_CFG             0x1000
++#define LMGR_INIT_START                       BIT(31)
++#define LMGR_SRAM_MODE_MASK           BIT(30)
++#define HW_FWD_PKTSIZE_OVERHEAD_MASK  GENMASK(27, 20)
++#define HW_FWD_DESC_NUM_MASK          GENMASK(16, 0)
++
++#define REG_FWD_DSCP_LOW_THR          0x1004
++#define FWD_DSCP_LOW_THR_MASK         GENMASK(17, 0)
++
++#define REG_EGRESS_RATE_METER_CFG             0x100c
++#define EGRESS_RATE_METER_EN_MASK             BIT(31)
++#define EGRESS_RATE_METER_EQ_RATE_EN_MASK     BIT(17)
++#define EGRESS_RATE_METER_WINDOW_SZ_MASK      GENMASK(16, 12)
++#define EGRESS_RATE_METER_TIMESLICE_MASK      GENMASK(10, 0)
++
++#define REG_EGRESS_TRTCM_CFG          0x1010
++#define EGRESS_TRTCM_EN_MASK          BIT(31)
++#define EGRESS_TRTCM_MODE_MASK                BIT(30)
++#define EGRESS_SLOW_TICK_RATIO_MASK   GENMASK(29, 16)
++#define EGRESS_FAST_TICK_MASK         GENMASK(15, 0)
++
++#define TRTCM_PARAM_RW_MASK           BIT(31)
++#define TRTCM_PARAM_RW_DONE_MASK      BIT(30)
++#define TRTCM_PARAM_TYPE_MASK         GENMASK(29, 28)
++#define TRTCM_METER_GROUP_MASK                GENMASK(27, 26)
++#define TRTCM_PARAM_INDEX_MASK                GENMASK(23, 17)
++#define TRTCM_PARAM_RATE_TYPE_MASK    BIT(16)
++
++#define REG_TRTCM_CFG_PARAM(_n)               ((_n) + 0x4)
++#define REG_TRTCM_DATA_LOW(_n)                ((_n) + 0x8)
++#define REG_TRTCM_DATA_HIGH(_n)               ((_n) + 0xc)
++
++#define REG_TXWRR_MODE_CFG            0x1020
++#define TWRR_WEIGHT_SCALE_MASK                BIT(31)
++#define TWRR_WEIGHT_BASE_MASK         BIT(3)
++
++#define REG_TXWRR_WEIGHT_CFG          0x1024
++#define TWRR_RW_CMD_MASK              BIT(31)
++#define TWRR_RW_CMD_DONE              BIT(30)
++#define TWRR_CHAN_IDX_MASK            GENMASK(23, 19)
++#define TWRR_QUEUE_IDX_MASK           GENMASK(18, 16)
++#define TWRR_VALUE_MASK                       GENMASK(15, 0)
++
++#define REG_PSE_BUF_USAGE_CFG         0x1028
++#define PSE_BUF_ESTIMATE_EN_MASK      BIT(29)
++
++#define REG_CHAN_QOS_MODE(_n)         (0x1040 + ((_n) << 2))
++#define CHAN_QOS_MODE_MASK(_n)                GENMASK(2 + ((_n) << 2), (_n) << 2)
++
++#define REG_GLB_TRTCM_CFG             0x1080
++#define GLB_TRTCM_EN_MASK             BIT(31)
++#define GLB_TRTCM_MODE_MASK           BIT(30)
++#define GLB_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
++#define GLB_FAST_TICK_MASK            GENMASK(15, 0)
++
++#define REG_TXQ_CNGST_CFG             0x10a0
++#define TXQ_CNGST_DROP_EN             BIT(31)
++#define TXQ_CNGST_DEI_DROP_EN         BIT(30)
++
++#define REG_SLA_TRTCM_CFG             0x1150
++#define SLA_TRTCM_EN_MASK             BIT(31)
++#define SLA_TRTCM_MODE_MASK           BIT(30)
++#define SLA_SLOW_TICK_RATIO_MASK      GENMASK(29, 16)
++#define SLA_FAST_TICK_MASK            GENMASK(15, 0)
++
++/* CTRL */
++#define QDMA_DESC_DONE_MASK           BIT(31)
++#define QDMA_DESC_DROP_MASK           BIT(30) /* tx: drop - rx: overflow */
++#define QDMA_DESC_MORE_MASK           BIT(29) /* more SG elements */
++#define QDMA_DESC_DEI_MASK            BIT(25)
++#define QDMA_DESC_NO_DROP_MASK                BIT(24)
++#define QDMA_DESC_LEN_MASK            GENMASK(15, 0)
++/* DATA */
++#define QDMA_DESC_NEXT_ID_MASK                GENMASK(15, 0)
++/* TX MSG0 */
++#define QDMA_ETH_TXMSG_MIC_IDX_MASK   BIT(30)
++#define QDMA_ETH_TXMSG_SP_TAG_MASK    GENMASK(29, 14)
++#define QDMA_ETH_TXMSG_ICO_MASK               BIT(13)
++#define QDMA_ETH_TXMSG_UCO_MASK               BIT(12)
++#define QDMA_ETH_TXMSG_TCO_MASK               BIT(11)
++#define QDMA_ETH_TXMSG_TSO_MASK               BIT(10)
++#define QDMA_ETH_TXMSG_FAST_MASK      BIT(9)
++#define QDMA_ETH_TXMSG_OAM_MASK               BIT(8)
++#define QDMA_ETH_TXMSG_CHAN_MASK      GENMASK(7, 3)
++#define QDMA_ETH_TXMSG_QUEUE_MASK     GENMASK(2, 0)
++/* TX MSG1 */
++#define QDMA_ETH_TXMSG_NO_DROP                BIT(31)
++#define QDMA_ETH_TXMSG_METER_MASK     GENMASK(30, 24) /* 0x7f no meters */
++#define QDMA_ETH_TXMSG_FPORT_MASK     GENMASK(23, 20)
++#define QDMA_ETH_TXMSG_NBOQ_MASK      GENMASK(19, 15)
++#define QDMA_ETH_TXMSG_HWF_MASK               BIT(14)
++#define QDMA_ETH_TXMSG_HOP_MASK               BIT(13)
++#define QDMA_ETH_TXMSG_PTP_MASK               BIT(12)
++#define QDMA_ETH_TXMSG_ACNT_G1_MASK   GENMASK(10, 6)  /* 0x1f do not count */
++#define QDMA_ETH_TXMSG_ACNT_G0_MASK   GENMASK(5, 0)   /* 0x3f do not count */
++
++/* RX MSG1 */
++#define QDMA_ETH_RXMSG_DEI_MASK               BIT(31)
++#define QDMA_ETH_RXMSG_IP6_MASK               BIT(30)
++#define QDMA_ETH_RXMSG_IP4_MASK               BIT(29)
++#define QDMA_ETH_RXMSG_IP4F_MASK      BIT(28)
++#define QDMA_ETH_RXMSG_L4_VALID_MASK  BIT(27)
++#define QDMA_ETH_RXMSG_L4F_MASK               BIT(26)
++#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)
++
++struct airoha_qdma_desc {
++      __le32 rsv;
++      __le32 ctrl;
++      __le32 addr;
++      __le32 data;
++      __le32 msg0;
++      __le32 msg1;
++      __le32 msg2;
++      __le32 msg3;
++};
++
++/* CTRL0 */
++#define QDMA_FWD_DESC_CTX_MASK                BIT(31)
++#define QDMA_FWD_DESC_RING_MASK               GENMASK(30, 28)
++#define QDMA_FWD_DESC_IDX_MASK                GENMASK(27, 16)
++#define QDMA_FWD_DESC_LEN_MASK                GENMASK(15, 0)
++/* CTRL1 */
++#define QDMA_FWD_DESC_FIRST_IDX_MASK  GENMASK(15, 0)
++/* CTRL2 */
++#define QDMA_FWD_DESC_MORE_PKT_NUM_MASK       GENMASK(2, 0)
++
++struct airoha_qdma_fwd_desc {
++      __le32 addr;
++      __le32 ctrl0;
++      __le32 ctrl1;
++      __le32 ctrl2;
++      __le32 msg0;
++      __le32 msg1;
++      __le32 rsv0;
++      __le32 rsv1;
++};
++
++#endif /* AIROHA_REGS_H */
diff --git a/target/linux/airoha/patches-6.6/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch b/target/linux/airoha/patches-6.6/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch
new file mode 100644 (file)
index 0000000..61e889f
--- /dev/null
@@ -0,0 +1,287 @@
+From af3cf757d5c99011b9b94ea8d78aeaccc0153fdc Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:13 +0100
+Subject: [PATCH 05/15] net: airoha: Move DSA tag in DMA descriptor
+
+Packet Processor Engine (PPE) module reads DSA tags from the DMA descriptor
+and requires untagged DSA packets to properly parse them. Move DSA tag
+in the DMA descriptor on TX side and read DSA tag from DMA descriptor
+on RX side. In order to avoid skb reallocation, store tag in skb_dst on
+RX side.
+This is a preliminary patch to enable netfilter flowtable hw offloading
+on EN7581 SoC.
+
+Tested-by: Sayantan Nandy <sayantan.nandy@airoha.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c  | 125 ++++++++++++++++++++--
+ drivers/net/ethernet/airoha/airoha_eth.h  |   7 ++
+ drivers/net/ethernet/airoha/airoha_regs.h |   2 +
+ 3 files changed, 128 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -9,6 +9,7 @@
+ #include <linux/tcp.h>
+ #include <linux/u64_stats_sync.h>
+ #include <net/dsa.h>
++#include <net/dst_metadata.h>
+ #include <net/page_pool/helpers.h>
+ #include <net/pkt_cls.h>
+ #include <uapi/linux/ppp_defs.h>
+@@ -656,6 +657,7 @@ static int airoha_qdma_rx_process(struct
+               struct airoha_qdma_desc *desc = &q->desc[q->tail];
+               dma_addr_t dma_addr = le32_to_cpu(desc->addr);
+               u32 desc_ctrl = le32_to_cpu(desc->ctrl);
++              struct airoha_gdm_port *port;
+               struct sk_buff *skb;
+               int len, p;
+@@ -683,6 +685,7 @@ static int airoha_qdma_rx_process(struct
+                       continue;
+               }
++              port = eth->ports[p];
+               skb = napi_build_skb(e->buf, q->buf_size);
+               if (!skb) {
+                       page_pool_put_full_page(q->page_pool,
+@@ -694,10 +697,26 @@ static int airoha_qdma_rx_process(struct
+               skb_reserve(skb, 2);
+               __skb_put(skb, len);
+               skb_mark_for_recycle(skb);
+-              skb->dev = eth->ports[p]->dev;
++              skb->dev = port->dev;
+               skb->protocol = eth_type_trans(skb, skb->dev);
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               skb_record_rx_queue(skb, qid);
++
++              if (netdev_uses_dsa(port->dev)) {
++                      /* 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
++                       * via skb dst info.
++                       */
++                      u32 sptag = FIELD_GET(QDMA_ETH_RXMSG_SPTAG,
++                                            le32_to_cpu(desc->msg0));
++
++                      if (sptag < ARRAY_SIZE(port->dsa_meta) &&
++                          port->dsa_meta[sptag])
++                              skb_dst_set_noref(skb,
++                                                &port->dsa_meta[sptag]->dst);
++              }
++
+               napi_gro_receive(&q->napi, skb);
+               done++;
+@@ -1637,25 +1656,76 @@ static u16 airoha_dev_select_queue(struc
+       return queue < dev->num_tx_queues ? queue : 0;
+ }
++static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
++{
++#if IS_ENABLED(CONFIG_NET_DSA)
++      struct ethhdr *ehdr;
++      struct dsa_port *dp;
++      u8 xmit_tpid;
++      u16 tag;
++
++      if (!netdev_uses_dsa(dev))
++              return 0;
++
++      dp = dev->dsa_ptr;
++      if (IS_ERR(dp))
++              return 0;
++
++      if (dp->tag_ops->proto != DSA_TAG_PROTO_MTK)
++              return 0;
++
++      if (skb_cow_head(skb, 0))
++              return 0;
++
++      ehdr = (struct ethhdr *)skb->data;
++      tag = be16_to_cpu(ehdr->h_proto);
++      xmit_tpid = tag >> 8;
++
++      switch (xmit_tpid) {
++      case MTK_HDR_XMIT_TAGGED_TPID_8100:
++              ehdr->h_proto = cpu_to_be16(ETH_P_8021Q);
++              tag &= ~(MTK_HDR_XMIT_TAGGED_TPID_8100 << 8);
++              break;
++      case MTK_HDR_XMIT_TAGGED_TPID_88A8:
++              ehdr->h_proto = cpu_to_be16(ETH_P_8021AD);
++              tag &= ~(MTK_HDR_XMIT_TAGGED_TPID_88A8 << 8);
++              break;
++      default:
++              /* PPE module requires untagged DSA packets to work properly,
++               * so move DSA tag to DMA descriptor.
++               */
++              memmove(skb->data + MTK_HDR_LEN, skb->data, 2 * ETH_ALEN);
++              __skb_pull(skb, MTK_HDR_LEN);
++              break;
++      }
++
++      return tag;
++#else
++      return 0;
++#endif
++}
++
+ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
+                                  struct net_device *dev)
+ {
+       struct airoha_gdm_port *port = netdev_priv(dev);
+-      u32 nr_frags = 1 + skb_shinfo(skb)->nr_frags;
+-      u32 msg0, msg1, len = skb_headlen(skb);
+       struct airoha_qdma *qdma = port->qdma;
++      u32 nr_frags, tag, msg0, msg1, len;
+       struct netdev_queue *txq;
+       struct airoha_queue *q;
+-      void *data = skb->data;
++      void *data;
+       int i, qid;
+       u16 index;
+       u8 fport;
+       qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx);
++      tag = airoha_get_dsa_tag(skb, dev);
++
+       msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK,
+                         qid / AIROHA_NUM_QOS_QUEUES) |
+              FIELD_PREP(QDMA_ETH_TXMSG_QUEUE_MASK,
+-                        qid % AIROHA_NUM_QOS_QUEUES);
++                        qid % AIROHA_NUM_QOS_QUEUES) |
++             FIELD_PREP(QDMA_ETH_TXMSG_SP_TAG_MASK, tag);
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               msg0 |= FIELD_PREP(QDMA_ETH_TXMSG_TCO_MASK, 1) |
+                       FIELD_PREP(QDMA_ETH_TXMSG_UCO_MASK, 1) |
+@@ -1686,6 +1756,8 @@ static netdev_tx_t airoha_dev_xmit(struc
+       spin_lock_bh(&q->lock);
+       txq = netdev_get_tx_queue(dev, qid);
++      nr_frags = 1 + skb_shinfo(skb)->nr_frags;
++
+       if (q->queued + nr_frags > q->ndesc) {
+               /* not enough space in the queue */
+               netif_tx_stop_queue(txq);
+@@ -1693,7 +1765,10 @@ static netdev_tx_t airoha_dev_xmit(struc
+               return NETDEV_TX_BUSY;
+       }
++      len = skb_headlen(skb);
++      data = skb->data;
+       index = q->head;
++
+       for (i = 0; i < nr_frags; i++) {
+               struct airoha_qdma_desc *desc = &q->desc[index];
+               struct airoha_queue_entry *e = &q->entry[index];
+@@ -2224,6 +2299,37 @@ static const struct ethtool_ops airoha_e
+       .get_rmon_stats         = airoha_ethtool_get_rmon_stats,
+ };
++static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(port->dsa_meta); i++) {
++              struct metadata_dst *md_dst;
++
++              md_dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX,
++                                          GFP_KERNEL);
++              if (!md_dst)
++                      return -ENOMEM;
++
++              md_dst->u.port_info.port_id = i;
++              port->dsa_meta[i] = md_dst;
++      }
++
++      return 0;
++}
++
++static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(port->dsa_meta); i++) {
++              if (!port->dsa_meta[i])
++                      continue;
++
++              metadata_dst_free(port->dsa_meta[i]);
++      }
++}
++
+ static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
+ {
+       const __be32 *id_ptr = of_get_property(np, "reg", NULL);
+@@ -2296,6 +2402,10 @@ static int airoha_alloc_gdm_port(struct
+       port->id = id;
+       eth->ports[index] = port;
++      err = airoha_metadata_dst_alloc(port);
++      if (err)
++              return err;
++
+       return register_netdev(dev);
+ }
+@@ -2388,8 +2498,10 @@ error_hw_cleanup:
+       for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+               struct airoha_gdm_port *port = eth->ports[i];
+-              if (port && port->dev->reg_state == NETREG_REGISTERED)
++              if (port && port->dev->reg_state == NETREG_REGISTERED) {
+                       unregister_netdev(port->dev);
++                      airoha_metadata_dst_free(port);
++              }
+       }
+       free_netdev(eth->napi_dev);
+       platform_set_drvdata(pdev, NULL);
+@@ -2415,6 +2527,7 @@ static void airoha_remove(struct platfor
+               airoha_dev_stop(port->dev);
+               unregister_netdev(port->dev);
++              airoha_metadata_dst_free(port);
+       }
+       free_netdev(eth->napi_dev);
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -15,6 +15,7 @@
+ #define AIROHA_MAX_NUM_GDM_PORTS      1
+ #define AIROHA_MAX_NUM_QDMA           2
++#define AIROHA_MAX_DSA_PORTS          7
+ #define AIROHA_MAX_NUM_RSTS           3
+ #define AIROHA_MAX_NUM_XSI_RSTS               5
+ #define AIROHA_MAX_MTU                        2000
+@@ -43,6 +44,10 @@
+ #define QDMA_METER_IDX(_n)            ((_n) & 0xff)
+ #define QDMA_METER_GROUP(_n)          (((_n) >> 8) & 0x3)
++#define MTK_HDR_LEN                   4
++#define MTK_HDR_XMIT_TAGGED_TPID_8100 1
++#define MTK_HDR_XMIT_TAGGED_TPID_88A8 2
++
+ enum {
+       QDMA_INT_REG_IDX0,
+       QDMA_INT_REG_IDX1,
+@@ -231,6 +236,8 @@ struct airoha_gdm_port {
+       /* qos stats counters */
+       u64 cpu_tx_packets;
+       u64 fwd_tx_packets;
++
++      struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
+ };
+ struct airoha_eth {
+--- a/drivers/net/ethernet/airoha/airoha_regs.h
++++ b/drivers/net/ethernet/airoha/airoha_regs.h
+@@ -624,6 +624,8 @@
+ #define QDMA_ETH_TXMSG_ACNT_G1_MASK   GENMASK(10, 6)  /* 0x1f do not count */
+ #define QDMA_ETH_TXMSG_ACNT_G0_MASK   GENMASK(5, 0)   /* 0x3f do not count */
++/* RX MSG0 */
++#define QDMA_ETH_RXMSG_SPTAG          GENMASK(21, 14)
+ /* RX MSG1 */
+ #define QDMA_ETH_RXMSG_DEI_MASK               BIT(31)
+ #define QDMA_ETH_RXMSG_IP6_MASK               BIT(30)
diff --git a/target/linux/airoha/patches-6.6/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch b/target/linux/airoha/patches-6.6/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch
new file mode 100644 (file)
index 0000000..fdf12c2
--- /dev/null
@@ -0,0 +1,46 @@
+From ab667db1e6014634c6607ebdddc16c1b8394a935 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:14 +0100
+Subject: [PATCH 06/15] net: dsa: mt7530: Enable Rx sptag for EN7581 SoC
+
+Packet Processor Engine (PPE) module used for hw acceleration on EN7581
+mac block, in order to properly parse packets, requires DSA untagged
+packets on TX side and read DSA tag from DMA descriptor on RX side.
+For this reason, enable RX Special Tag (SPTAG) for EN7581 SoC.
+This is a preliminary patch to enable netfilter flowtable hw offloading
+on EN7581 SoC.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/dsa/mt7530.c | 5 +++++
+ drivers/net/dsa/mt7530.h | 4 ++++
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2585,6 +2585,11 @@ mt7531_setup_common(struct dsa_switch *d
+       /* Allow mirroring frames received on the local port (monitor port). */
+       mt7530_set(priv, MT753X_AGC, LOCAL_EN);
++      /* Enable Special Tag for rx frames */
++      if (priv->id == ID_EN7581)
++              mt7530_write(priv, MT753X_CPORT_SPTAG_CFG,
++                           CPORT_SW2FE_STAG_EN | CPORT_FE2SW_STAG_EN);
++
+       /* Flush the FDB table */
+       ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL);
+       if (ret < 0)
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -615,6 +615,10 @@ enum mt7531_xtal_fsel {
+ #define  MT7531_GPIO12_RG_RXD3_MASK   GENMASK(19, 16)
+ #define  MT7531_EXT_P_MDIO_12         (2 << 16)
++#define MT753X_CPORT_SPTAG_CFG                0x7c10
++#define  CPORT_SW2FE_STAG_EN          BIT(1)
++#define  CPORT_FE2SW_STAG_EN          BIT(0)
++
+ /* Registers for LED GPIO control (MT7530 only)
+  * All registers follow this pattern:
+  * [ 2: 0]  port 0
diff --git a/target/linux/airoha/patches-6.6/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch b/target/linux/airoha/patches-6.6/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch
new file mode 100644 (file)
index 0000000..af4fa78
--- /dev/null
@@ -0,0 +1,144 @@
+From 80369686737fe07c233a1152da0b84372dabdcd6 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:15 +0100
+Subject: [PATCH 07/15] net: airoha: Enable support for multiple net_devices
+
+In the current codebase airoha_eth driver supports just a single
+net_device connected to the Packet Switch Engine (PSE) lan port (GDM1).
+As shown in commit 23020f049327 ("net: airoha: Introduce ethernet
+support for EN7581 SoC"), PSE can switch packets between four GDM ports.
+Enable the capability to create a net_device for each GDM port of the
+PSE module. Moreover, since the QDMA blocks can be shared between
+net_devices, do not stop TX/RX DMA in airoha_dev_stop() if there are
+active net_devices for this QDMA block.
+This is a preliminary patch to enable flowtable hw offloading for EN7581
+SoC.
+
+Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 35 ++++++++++++++----------
+ drivers/net/ethernet/airoha/airoha_eth.h |  4 ++-
+ 2 files changed, 24 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -1563,6 +1563,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);
+       return 0;
+ }
+@@ -1578,16 +1579,20 @@ static int airoha_dev_stop(struct net_de
+       if (err)
+               return err;
+-      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++)
++              netdev_tx_reset_subqueue(dev, i);
+-      for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
+-              if (!qdma->q_tx[i].ndesc)
+-                      continue;
++      if (atomic_dec_and_test(&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]);
+-              netdev_tx_reset_subqueue(dev, i);
++                      airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
++              }
+       }
+       return 0;
+@@ -2330,13 +2335,14 @@ static void airoha_metadata_dst_free(str
+       }
+ }
+-static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
++static int airoha_alloc_gdm_port(struct airoha_eth *eth,
++                               struct device_node *np, int index)
+ {
+       const __be32 *id_ptr = of_get_property(np, "reg", NULL);
+       struct airoha_gdm_port *port;
+       struct airoha_qdma *qdma;
+       struct net_device *dev;
+-      int err, index;
++      int err, p;
+       u32 id;
+       if (!id_ptr) {
+@@ -2345,14 +2351,14 @@ static int airoha_alloc_gdm_port(struct
+       }
+       id = be32_to_cpup(id_ptr);
+-      index = id - 1;
++      p = id - 1;
+       if (!id || id > ARRAY_SIZE(eth->ports)) {
+               dev_err(eth->dev, "invalid gdm port id: %d\n", id);
+               return -EINVAL;
+       }
+-      if (eth->ports[index]) {
++      if (eth->ports[p]) {
+               dev_err(eth->dev, "duplicate gdm port id: %d\n", id);
+               return -EINVAL;
+       }
+@@ -2400,7 +2406,7 @@ static int airoha_alloc_gdm_port(struct
+       port->qdma = qdma;
+       port->dev = dev;
+       port->id = id;
+-      eth->ports[index] = port;
++      eth->ports[p] = port;
+       err = airoha_metadata_dst_alloc(port);
+       if (err)
+@@ -2472,6 +2478,7 @@ static int airoha_probe(struct platform_
+       for (i = 0; i < ARRAY_SIZE(eth->qdma); i++)
+               airoha_qdma_start_napi(&eth->qdma[i]);
++      i = 0;
+       for_each_child_of_node(pdev->dev.of_node, np) {
+               if (!of_device_is_compatible(np, "airoha,eth-mac"))
+                       continue;
+@@ -2479,7 +2486,7 @@ static int airoha_probe(struct platform_
+               if (!of_device_is_available(np))
+                       continue;
+-              err = airoha_alloc_gdm_port(eth, np);
++              err = airoha_alloc_gdm_port(eth, np, i++);
+               if (err) {
+                       of_node_put(np);
+                       goto error_napi_stop;
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -13,7 +13,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/reset.h>
+-#define AIROHA_MAX_NUM_GDM_PORTS      1
++#define AIROHA_MAX_NUM_GDM_PORTS      4
+ #define AIROHA_MAX_NUM_QDMA           2
+ #define AIROHA_MAX_DSA_PORTS          7
+ #define AIROHA_MAX_NUM_RSTS           3
+@@ -212,6 +212,8 @@ struct airoha_qdma {
+       u32 irqmask[QDMA_INT_REG_MAX];
+       int irq;
++      atomic_t users;
++
+       struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ];
+       struct airoha_queue q_tx[AIROHA_NUM_TX_RING];
diff --git a/target/linux/airoha/patches-6.6/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch b/target/linux/airoha/patches-6.6/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch
new file mode 100644 (file)
index 0000000..8bc0f85
--- /dev/null
@@ -0,0 +1,77 @@
+From 67fde5d58cd43d129a979e918ec9cd5d2e2fbcfb Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:16 +0100
+Subject: [PATCH 08/15] net: airoha: Move REG_GDM_FWD_CFG() initialization in
+ airoha_dev_init()
+
+Move REG_GDM_FWD_CFG() register initialization in airoha_dev_init
+routine. Moreover, always send traffic PPE module in order to be
+processed by hw accelerator.
+This is a preliminary patch to enable netfilter flowtable hw offloading
+on EN7581 SoC.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 14 ++++----------
+ 1 file changed, 4 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -107,25 +107,20 @@ static void airoha_set_gdm_port_fwd_cfg(
+ static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable)
+ {
+-      u32 val = enable ? FE_PSE_PORT_PPE1 : FE_PSE_PORT_DROP;
+-      u32 vip_port, cfg_addr;
++      u32 vip_port;
+       switch (port) {
+       case XSI_PCIE0_PORT:
+               vip_port = XSI_PCIE0_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(3);
+               break;
+       case XSI_PCIE1_PORT:
+               vip_port = XSI_PCIE1_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(3);
+               break;
+       case XSI_USB_PORT:
+               vip_port = XSI_USB_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(4);
+               break;
+       case XSI_ETH_PORT:
+               vip_port = XSI_ETH_VIP_PORT_MASK;
+-              cfg_addr = REG_GDM_FWD_CFG(4);
+               break;
+       default:
+               return -EINVAL;
+@@ -139,8 +134,6 @@ static int airoha_set_gdm_port(struct ai
+               airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, vip_port);
+       }
+-      airoha_set_gdm_port_fwd_cfg(eth, cfg_addr, val);
+-
+       return 0;
+ }
+@@ -177,8 +170,6 @@ static void airoha_fe_maccr_init(struct
+               airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
+                             GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM |
+                             GDM_DROP_CRC_ERR);
+-              airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(p),
+-                                          FE_PSE_PORT_CDM1);
+               airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p),
+                             GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
+                             FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
+@@ -1615,8 +1606,11 @@ static int airoha_dev_set_macaddr(struct
+ static int airoha_dev_init(struct net_device *dev)
+ {
+       struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_eth *eth = port->qdma->eth;
+       airoha_set_macaddr(port, dev->dev_addr);
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
++                                  FE_PSE_PORT_PPE1);
+       return 0;
+ }
diff --git a/target/linux/airoha/patches-6.6/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch b/target/linux/airoha/patches-6.6/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch
new file mode 100644 (file)
index 0000000..11f879d
--- /dev/null
@@ -0,0 +1,120 @@
+From c28b8375f6d02ef3b5e8c51234cc3f6d47d9fb7f Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:17 +0100
+Subject: [PATCH 09/15] net: airoha: Rename airoha_set_gdm_port_fwd_cfg() in
+ airoha_set_vip_for_gdm_port()
+
+Rename airoha_set_gdm_port() in airoha_set_vip_for_gdm_port().
+Get rid of airoha_set_gdm_ports routine.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c | 49 ++++++------------------
+ drivers/net/ethernet/airoha/airoha_eth.h |  8 ----
+ 2 files changed, 11 insertions(+), 46 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -105,25 +105,23 @@ static void airoha_set_gdm_port_fwd_cfg(
+                     FIELD_PREP(GDM_UCFQ_MASK, val));
+ }
+-static int airoha_set_gdm_port(struct airoha_eth *eth, int port, bool enable)
++static int airoha_set_vip_for_gdm_port(struct airoha_gdm_port *port,
++                                     bool enable)
+ {
++      struct airoha_eth *eth = port->qdma->eth;
+       u32 vip_port;
+-      switch (port) {
+-      case XSI_PCIE0_PORT:
++      switch (port->id) {
++      case 3:
++              /* FIXME: handle XSI_PCIE1_PORT */
+               vip_port = XSI_PCIE0_VIP_PORT_MASK;
+               break;
+-      case XSI_PCIE1_PORT:
+-              vip_port = XSI_PCIE1_VIP_PORT_MASK;
+-              break;
+-      case XSI_USB_PORT:
+-              vip_port = XSI_USB_VIP_PORT_MASK;
+-              break;
+-      case XSI_ETH_PORT:
++      case 4:
++              /* FIXME: handle XSI_USB_PORT */
+               vip_port = XSI_ETH_VIP_PORT_MASK;
+               break;
+       default:
+-              return -EINVAL;
++              return 0;
+       }
+       if (enable) {
+@@ -137,31 +135,6 @@ static int airoha_set_gdm_port(struct ai
+       return 0;
+ }
+-static int airoha_set_gdm_ports(struct airoha_eth *eth, bool enable)
+-{
+-      const int port_list[] = {
+-              XSI_PCIE0_PORT,
+-              XSI_PCIE1_PORT,
+-              XSI_USB_PORT,
+-              XSI_ETH_PORT
+-      };
+-      int i, err;
+-
+-      for (i = 0; i < ARRAY_SIZE(port_list); i++) {
+-              err = airoha_set_gdm_port(eth, port_list[i], enable);
+-              if (err)
+-                      goto error;
+-      }
+-
+-      return 0;
+-
+-error:
+-      for (i--; i >= 0; i--)
+-              airoha_set_gdm_port(eth, port_list[i], false);
+-
+-      return err;
+-}
+-
+ static void airoha_fe_maccr_init(struct airoha_eth *eth)
+ {
+       int p;
+@@ -1540,7 +1513,7 @@ static int airoha_dev_open(struct net_de
+       int err;
+       netif_tx_start_all_queues(dev);
+-      err = airoha_set_gdm_ports(qdma->eth, true);
++      err = airoha_set_vip_for_gdm_port(port, true);
+       if (err)
+               return err;
+@@ -1566,7 +1539,7 @@ static int airoha_dev_stop(struct net_de
+       int i, err;
+       netif_tx_disable(dev);
+-      err = airoha_set_gdm_ports(qdma->eth, false);
++      err = airoha_set_vip_for_gdm_port(port, false);
+       if (err)
+               return err;
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -58,14 +58,6 @@ enum {
+ };
+ enum {
+-      XSI_PCIE0_PORT,
+-      XSI_PCIE1_PORT,
+-      XSI_USB_PORT,
+-      XSI_AE_PORT,
+-      XSI_ETH_PORT,
+-};
+-
+-enum {
+       XSI_PCIE0_VIP_PORT_MASK = BIT(22),
+       XSI_PCIE1_VIP_PORT_MASK = BIT(23),
+       XSI_USB_VIP_PORT_MASK   = BIT(25),
diff --git a/target/linux/airoha/patches-6.6/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch b/target/linux/airoha/patches-6.6/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch
new file mode 100644 (file)
index 0000000..41c5622
--- /dev/null
@@ -0,0 +1,627 @@
+From 23290c7bc190def4e1ca61610992d9b7c32e33f3 Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:20 +0100
+Subject: [PATCH 12/15] net: airoha: Introduce Airoha NPU support
+
+Packet Processor Engine (PPE) module available on EN7581 SoC populates
+the PPE table with 5-tuples flower rules learned from traffic forwarded
+between the GDM ports connected to the Packet Switch Engine (PSE) module.
+The airoha_eth driver can enable hw acceleration of learned 5-tuples
+rules if the user configure them in netfilter flowtable (netfilter
+flowtable support will be added with subsequent patches).
+airoha_eth driver configures and collects data from the PPE module via a
+Network Processor Unit (NPU) RISC-V module available on the EN7581 SoC.
+Introduce basic support for Airoha NPU module.
+
+Tested-by: Sayantan Nandy <sayantan.nandy@airoha.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/Kconfig      |   9 +
+ drivers/net/ethernet/airoha/Makefile     |   1 +
+ drivers/net/ethernet/airoha/airoha_eth.h |   2 +
+ drivers/net/ethernet/airoha/airoha_npu.c | 520 +++++++++++++++++++++++
+ drivers/net/ethernet/airoha/airoha_npu.h |  34 ++
+ 5 files changed, 566 insertions(+)
+ create mode 100644 drivers/net/ethernet/airoha/airoha_npu.c
+ create mode 100644 drivers/net/ethernet/airoha/airoha_npu.h
+
+--- a/drivers/net/ethernet/airoha/Kconfig
++++ b/drivers/net/ethernet/airoha/Kconfig
+@@ -7,9 +7,18 @@ config NET_VENDOR_AIROHA
+ if NET_VENDOR_AIROHA
++config NET_AIROHA_NPU
++      tristate "Airoha NPU support"
++      select WANT_DEV_COREDUMP
++      select REGMAP_MMIO
++      help
++        This driver supports Airoha Network Processor (NPU) available
++        on the Airoha Soc family.
++
+ config NET_AIROHA
+       tristate "Airoha SoC Gigabit Ethernet support"
+       depends on NET_DSA || !NET_DSA
++      select NET_AIROHA_NPU
+       select PAGE_POOL
+       help
+         This driver supports the gigabit ethernet MACs in the
+--- a/drivers/net/ethernet/airoha/Makefile
++++ b/drivers/net/ethernet/airoha/Makefile
+@@ -4,3 +4,4 @@
+ #
+ obj-$(CONFIG_NET_AIROHA) += airoha_eth.o
++obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -240,6 +240,8 @@ struct airoha_eth {
+       unsigned long state;
+       void __iomem *fe_regs;
++      struct airoha_npu __rcu *npu;
++
+       struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
+       struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_npu.c
+@@ -0,0 +1,520 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2025 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++
++#include <linux/devcoredump.h>
++#include <linux/firmware.h>
++#include <linux/platform_device.h>
++#include <linux/of_net.h>
++#include <linux/of_platform.h>
++#include <linux/of_reserved_mem.h>
++#include <linux/regmap.h>
++
++#include "airoha_npu.h"
++
++#define NPU_EN7581_FIRMWARE_DATA              "airoha/en7581_npu_data.bin"
++#define NPU_EN7581_FIRMWARE_RV32              "airoha/en7581_npu_rv32.bin"
++#define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE     0x200000
++#define NPU_EN7581_FIRMWARE_DATA_MAX_SIZE     0x10000
++#define NPU_DUMP_SIZE                         512
++
++#define REG_NPU_LOCAL_SRAM            0x0
++
++#define NPU_PC_BASE_ADDR              0x305000
++#define REG_PC_DBG(_n)                        (0x305000 + ((_n) * 0x100))
++
++#define NPU_CLUSTER_BASE_ADDR         0x306000
++
++#define REG_CR_BOOT_TRIGGER           (NPU_CLUSTER_BASE_ADDR + 0x000)
++#define REG_CR_BOOT_CONFIG            (NPU_CLUSTER_BASE_ADDR + 0x004)
++#define REG_CR_BOOT_BASE(_n)          (NPU_CLUSTER_BASE_ADDR + 0x020 + ((_n) << 2))
++
++#define NPU_MBOX_BASE_ADDR            0x30c000
++
++#define REG_CR_MBOX_INT_STATUS                (NPU_MBOX_BASE_ADDR + 0x000)
++#define MBOX_INT_STATUS_MASK          BIT(8)
++
++#define REG_CR_MBOX_INT_MASK(_n)      (NPU_MBOX_BASE_ADDR + 0x004 + ((_n) << 2))
++#define REG_CR_MBQ0_CTRL(_n)          (NPU_MBOX_BASE_ADDR + 0x030 + ((_n) << 2))
++#define REG_CR_MBQ8_CTRL(_n)          (NPU_MBOX_BASE_ADDR + 0x0b0 + ((_n) << 2))
++#define REG_CR_NPU_MIB(_n)            (NPU_MBOX_BASE_ADDR + 0x140 + ((_n) << 2))
++
++#define NPU_TIMER_BASE_ADDR           0x310100
++#define REG_WDT_TIMER_CTRL(_n)                (NPU_TIMER_BASE_ADDR + ((_n) * 0x100))
++#define WDT_EN_MASK                   BIT(25)
++#define WDT_INTR_MASK                 BIT(21)
++
++enum {
++      NPU_OP_SET = 1,
++      NPU_OP_SET_NO_WAIT,
++      NPU_OP_GET,
++      NPU_OP_GET_NO_WAIT,
++};
++
++enum {
++      NPU_FUNC_WIFI,
++      NPU_FUNC_TUNNEL,
++      NPU_FUNC_NOTIFY,
++      NPU_FUNC_DBA,
++      NPU_FUNC_TR471,
++      NPU_FUNC_PPE,
++};
++
++enum {
++      NPU_MBOX_ERROR,
++      NPU_MBOX_SUCCESS,
++};
++
++enum {
++      PPE_FUNC_SET_WAIT,
++      PPE_FUNC_SET_WAIT_HWNAT_INIT,
++      PPE_FUNC_SET_WAIT_HWNAT_DEINIT,
++      PPE_FUNC_SET_WAIT_API,
++};
++
++enum {
++      PPE2_SRAM_SET_ENTRY,
++      PPE_SRAM_SET_ENTRY,
++      PPE_SRAM_SET_VAL,
++      PPE_SRAM_RESET_VAL,
++};
++
++enum {
++      QDMA_WAN_ETHER = 1,
++      QDMA_WAN_PON_XDSL,
++};
++
++#define MBOX_MSG_FUNC_ID      GENMASK(14, 11)
++#define MBOX_MSG_STATIC_BUF   BIT(5)
++#define MBOX_MSG_STATUS               GENMASK(4, 2)
++#define MBOX_MSG_DONE         BIT(1)
++#define MBOX_MSG_WAIT_RSP     BIT(0)
++
++#define PPE_TYPE_L2B_IPV4     2
++#define PPE_TYPE_L2B_IPV4_IPV6        3
++
++struct ppe_mbox_data {
++      u32 func_type;
++      u32 func_id;
++      union {
++              struct {
++                      u8 cds;
++                      u8 xpon_hal_api;
++                      u8 wan_xsi;
++                      u8 ct_joyme4;
++                      int ppe_type;
++                      int wan_mode;
++                      int wan_sel;
++              } init_info;
++              struct {
++                      int func_id;
++                      u32 size;
++                      u32 data;
++              } set_info;
++      };
++};
++
++static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
++                             void *p, int size)
++{
++      u16 core = 0; /* FIXME */
++      u32 val, offset = core << 4;
++      dma_addr_t dma_addr;
++      void *addr;
++      int ret;
++
++      addr = kmemdup(p, size, GFP_ATOMIC);
++      if (!addr)
++              return -ENOMEM;
++
++      dma_addr = dma_map_single(npu->dev, addr, size, DMA_TO_DEVICE);
++      ret = dma_mapping_error(npu->dev, dma_addr);
++      if (ret)
++              goto out;
++
++      spin_lock_bh(&npu->cores[core].lock);
++
++      regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(0) + offset, dma_addr);
++      regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(1) + offset, size);
++      regmap_read(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, &val);
++      regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, val + 1);
++      val = FIELD_PREP(MBOX_MSG_FUNC_ID, func_id) | MBOX_MSG_WAIT_RSP;
++      regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(3) + offset, val);
++
++      ret = regmap_read_poll_timeout_atomic(npu->regmap,
++                                            REG_CR_MBQ0_CTRL(3) + offset,
++                                            val, (val & MBOX_MSG_DONE),
++                                            100, 100 * MSEC_PER_SEC);
++      if (!ret && FIELD_GET(MBOX_MSG_STATUS, val) != NPU_MBOX_SUCCESS)
++              ret = -EINVAL;
++
++      spin_unlock_bh(&npu->cores[core].lock);
++
++      dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE);
++out:
++      kfree(addr);
++
++      return ret;
++}
++
++static int airoha_npu_run_firmware(struct device *dev, void __iomem *base,
++                                 struct reserved_mem *rmem)
++{
++      const struct firmware *fw;
++      void __iomem *addr;
++      int ret;
++
++      ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_RV32, dev);
++      if (ret)
++              return ret == -ENOENT ? -EPROBE_DEFER : ret;
++
++      if (fw->size > NPU_EN7581_FIRMWARE_RV32_MAX_SIZE) {
++              dev_err(dev, "%s: fw size too overlimit (%zu)\n",
++                      NPU_EN7581_FIRMWARE_RV32, fw->size);
++              ret = -E2BIG;
++              goto out;
++      }
++
++      addr = devm_ioremap(dev, rmem->base, rmem->size);
++      if (!addr) {
++              ret = -ENOMEM;
++              goto out;
++      }
++
++      memcpy_toio(addr, fw->data, fw->size);
++      release_firmware(fw);
++
++      ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_DATA, dev);
++      if (ret)
++              return ret == -ENOENT ? -EPROBE_DEFER : ret;
++
++      if (fw->size > NPU_EN7581_FIRMWARE_DATA_MAX_SIZE) {
++              dev_err(dev, "%s: fw size too overlimit (%zu)\n",
++                      NPU_EN7581_FIRMWARE_DATA, fw->size);
++              ret = -E2BIG;
++              goto out;
++      }
++
++      memcpy_toio(base + REG_NPU_LOCAL_SRAM, fw->data, fw->size);
++out:
++      release_firmware(fw);
++
++      return ret;
++}
++
++static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance)
++{
++      struct airoha_npu *npu = npu_instance;
++
++      /* clear mbox interrupt status */
++      regmap_write(npu->regmap, REG_CR_MBOX_INT_STATUS,
++                   MBOX_INT_STATUS_MASK);
++
++      /* acknowledge npu */
++      regmap_update_bits(npu->regmap, REG_CR_MBQ8_CTRL(3),
++                         MBOX_MSG_STATUS | MBOX_MSG_DONE, MBOX_MSG_DONE);
++
++      return IRQ_HANDLED;
++}
++
++static void airoha_npu_wdt_work(struct work_struct *work)
++{
++      struct airoha_npu_core *core;
++      struct airoha_npu *npu;
++      void *dump;
++      u32 val[3];
++      int c;
++
++      core = container_of(work, struct airoha_npu_core, wdt_work);
++      npu = core->npu;
++
++      dump = vzalloc(NPU_DUMP_SIZE);
++      if (!dump)
++              return;
++
++      c = core - &npu->cores[0];
++      regmap_bulk_read(npu->regmap, REG_PC_DBG(c), val, ARRAY_SIZE(val));
++      snprintf(dump, NPU_DUMP_SIZE, "PC: %08x SP: %08x LR: %08x\n",
++               val[0], val[1], val[2]);
++
++      dev_coredumpv(npu->dev, dump, NPU_DUMP_SIZE, GFP_KERNEL);
++}
++
++static irqreturn_t airoha_npu_wdt_handler(int irq, void *core_instance)
++{
++      struct airoha_npu_core *core = core_instance;
++      struct airoha_npu *npu = core->npu;
++      int c = core - &npu->cores[0];
++      u32 val;
++
++      regmap_set_bits(npu->regmap, REG_WDT_TIMER_CTRL(c), WDT_INTR_MASK);
++      if (!regmap_read(npu->regmap, REG_WDT_TIMER_CTRL(c), &val) &&
++          FIELD_GET(WDT_EN_MASK, val))
++              schedule_work(&core->wdt_work);
++
++      return IRQ_HANDLED;
++}
++
++static int airoha_npu_ppe_init(struct airoha_npu *npu)
++{
++      struct ppe_mbox_data ppe_data = {
++              .func_type = NPU_OP_SET,
++              .func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT,
++              .init_info = {
++                      .ppe_type = PPE_TYPE_L2B_IPV4_IPV6,
++                      .wan_mode = QDMA_WAN_ETHER,
++              },
++      };
++
++      return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data,
++                                 sizeof(struct ppe_mbox_data));
++}
++
++static int airoha_npu_ppe_deinit(struct airoha_npu *npu)
++{
++      struct ppe_mbox_data ppe_data = {
++              .func_type = NPU_OP_SET,
++              .func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT,
++      };
++
++      return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data,
++                                 sizeof(struct ppe_mbox_data));
++}
++
++static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu,
++                                           dma_addr_t foe_addr,
++                                           int sram_num_entries)
++{
++      struct ppe_mbox_data ppe_data = {
++              .func_type = NPU_OP_SET,
++              .func_id = PPE_FUNC_SET_WAIT_API,
++              .set_info = {
++                      .func_id = PPE_SRAM_RESET_VAL,
++                      .data = foe_addr,
++                      .size = sram_num_entries,
++              },
++      };
++
++      return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data,
++                                 sizeof(struct ppe_mbox_data));
++}
++
++static int airoha_npu_foe_commit_entry(struct airoha_npu *npu,
++                                     dma_addr_t foe_addr,
++                                     u32 entry_size, u32 hash, bool ppe2)
++{
++      struct ppe_mbox_data ppe_data = {
++              .func_type = NPU_OP_SET,
++              .func_id = PPE_FUNC_SET_WAIT_API,
++              .set_info = {
++                      .data = foe_addr,
++                      .size = entry_size,
++              },
++      };
++      int err;
++
++      ppe_data.set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY
++                                       : PPE_SRAM_SET_ENTRY;
++
++      err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data,
++                                sizeof(struct ppe_mbox_data));
++      if (err)
++              return err;
++
++      ppe_data.set_info.func_id = PPE_SRAM_SET_VAL;
++      ppe_data.set_info.data = hash;
++      ppe_data.set_info.size = sizeof(u32);
++
++      return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data,
++                                 sizeof(struct ppe_mbox_data));
++}
++
++struct airoha_npu *airoha_npu_get(struct device *dev)
++{
++      struct platform_device *pdev;
++      struct device_node *np;
++      struct airoha_npu *npu;
++
++      np = of_parse_phandle(dev->of_node, "airoha,npu", 0);
++      if (!np)
++              return ERR_PTR(-ENODEV);
++
++      pdev = of_find_device_by_node(np);
++      of_node_put(np);
++
++      if (!pdev) {
++              dev_err(dev, "cannot find device node %s\n", np->name);
++              return ERR_PTR(-ENODEV);
++      }
++
++      if (!try_module_get(THIS_MODULE)) {
++              dev_err(dev, "failed to get the device driver module\n");
++              npu = ERR_PTR(-ENODEV);
++              goto error_pdev_put;
++      }
++
++      npu = platform_get_drvdata(pdev);
++      if (!npu) {
++              npu = ERR_PTR(-ENODEV);
++              goto error_module_put;
++      }
++
++      if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER)) {
++              dev_err(&pdev->dev,
++                      "failed to create device link to consumer %s\n",
++                      dev_name(dev));
++              npu = ERR_PTR(-EINVAL);
++              goto error_module_put;
++      }
++
++      return npu;
++
++error_module_put:
++      module_put(THIS_MODULE);
++error_pdev_put:
++      platform_device_put(pdev);
++
++      return npu;
++}
++EXPORT_SYMBOL_GPL(airoha_npu_get);
++
++void airoha_npu_put(struct airoha_npu *npu)
++{
++      module_put(THIS_MODULE);
++      put_device(npu->dev);
++}
++EXPORT_SYMBOL_GPL(airoha_npu_put);
++
++static const struct of_device_id of_airoha_npu_match[] = {
++      { .compatible = "airoha,en7581-npu" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, of_airoha_npu_match);
++
++static const struct regmap_config regmap_config = {
++      .name                   = "npu",
++      .reg_bits               = 32,
++      .val_bits               = 32,
++      .reg_stride             = 4,
++      .disable_locking        = true,
++};
++
++static int airoha_npu_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct reserved_mem *rmem;
++      struct airoha_npu *npu;
++      struct device_node *np;
++      void __iomem *base;
++      int i, irq, err;
++
++      base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      npu = devm_kzalloc(dev, sizeof(*npu), GFP_KERNEL);
++      if (!npu)
++              return -ENOMEM;
++
++      npu->dev = dev;
++      npu->ops.ppe_init = airoha_npu_ppe_init;
++      npu->ops.ppe_deinit = airoha_npu_ppe_deinit;
++      npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries;
++      npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry;
++
++      npu->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);
++      if (IS_ERR(npu->regmap))
++              return PTR_ERR(npu->regmap);
++
++      np = of_parse_phandle(dev->of_node, "memory-region", 0);
++      if (!np)
++              return -ENODEV;
++
++      rmem = of_reserved_mem_lookup(np);
++      of_node_put(np);
++
++      if (!rmem)
++              return -ENODEV;
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0)
++              return irq;
++
++      err = devm_request_irq(dev, irq, airoha_npu_mbox_handler,
++                             IRQF_SHARED, "airoha-npu-mbox", npu);
++      if (err)
++              return err;
++
++      for (i = 0; i < ARRAY_SIZE(npu->cores); i++) {
++              struct airoha_npu_core *core = &npu->cores[i];
++
++              spin_lock_init(&core->lock);
++              core->npu = npu;
++
++              irq = platform_get_irq(pdev, i + 1);
++              if (irq < 0)
++                      return irq;
++
++              err = devm_request_irq(dev, irq, airoha_npu_wdt_handler,
++                                     IRQF_SHARED, "airoha-npu-wdt", core);
++              if (err)
++                      return err;
++
++              INIT_WORK(&core->wdt_work, airoha_npu_wdt_work);
++      }
++
++      err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
++      if (err)
++              return err;
++
++      err = airoha_npu_run_firmware(dev, base, rmem);
++      if (err)
++              return dev_err_probe(dev, err, "failed to run npu firmware\n");
++
++      regmap_write(npu->regmap, REG_CR_NPU_MIB(10),
++                   rmem->base + NPU_EN7581_FIRMWARE_RV32_MAX_SIZE);
++      regmap_write(npu->regmap, REG_CR_NPU_MIB(11), 0x40000); /* SRAM 256K */
++      regmap_write(npu->regmap, REG_CR_NPU_MIB(12), 0);
++      regmap_write(npu->regmap, REG_CR_NPU_MIB(21), 1);
++      msleep(100);
++
++      /* setting booting address */
++      for (i = 0; i < NPU_NUM_CORES; i++)
++              regmap_write(npu->regmap, REG_CR_BOOT_BASE(i), rmem->base);
++      usleep_range(1000, 2000);
++
++      /* enable NPU cores */
++      /* do not start core3 since it is used for WiFi offloading */
++      regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xf7);
++      regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1);
++      msleep(100);
++
++      platform_set_drvdata(pdev, npu);
++
++      return 0;
++}
++
++static void airoha_npu_remove(struct platform_device *pdev)
++{
++      struct airoha_npu *npu = platform_get_drvdata(pdev);
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(npu->cores); i++)
++              cancel_work_sync(&npu->cores[i].wdt_work);
++}
++
++static struct platform_driver airoha_npu_driver = {
++      .probe = airoha_npu_probe,
++      .remove_new = airoha_npu_remove,
++      .driver = {
++              .name = "airoha-npu",
++              .of_match_table = of_airoha_npu_match,
++      },
++};
++module_platform_driver(airoha_npu_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
++MODULE_DESCRIPTION("Airoha Network Processor Unit driver");
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_npu.h
+@@ -0,0 +1,34 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2025 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++
++#define NPU_NUM_CORES         8
++
++struct airoha_npu {
++      struct device *dev;
++      struct regmap *regmap;
++
++      struct airoha_npu_core {
++              struct airoha_npu *npu;
++              /* protect concurrent npu memory accesses */
++              spinlock_t lock;
++              struct work_struct wdt_work;
++      } cores[NPU_NUM_CORES];
++
++      struct {
++              int (*ppe_init)(struct airoha_npu *npu);
++              int (*ppe_deinit)(struct airoha_npu *npu);
++              int (*ppe_flush_sram_entries)(struct airoha_npu *npu,
++                                            dma_addr_t foe_addr,
++                                            int sram_num_entries);
++              int (*ppe_foe_commit_entry)(struct airoha_npu *npu,
++                                          dma_addr_t foe_addr,
++                                          u32 entry_size, u32 hash,
++                                          bool ppe2);
++      } ops;
++};
++
++struct airoha_npu *airoha_npu_get(struct device *dev);
++void airoha_npu_put(struct airoha_npu *npu);
diff --git a/target/linux/airoha/patches-6.6/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch b/target/linux/airoha/patches-6.6/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch
new file mode 100644 (file)
index 0000000..225165f
--- /dev/null
@@ -0,0 +1,1481 @@
+From 00a7678310fe3d3f408513e55d9a0b67f0db380f Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:21 +0100
+Subject: [PATCH 13/15] net: airoha: Introduce flowtable offload support
+
+Introduce netfilter flowtable integration in order to allow airoha_eth
+driver to offload 5-tuple flower rules learned by the PPE module if the
+user accelerates them using a nft configuration similar to the one reported
+below:
+
+table inet filter {
+       flowtable ft {
+               hook ingress priority filter
+               devices = { lan1, lan2, lan3, lan4, eth1 }
+               flags offload;
+       }
+       chain forward {
+               type filter hook forward priority filter; policy accept;
+               meta l4proto { tcp, udp } flow add @ft
+       }
+}
+
+Tested-by: Sayantan Nandy <sayantan.nandy@airoha.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/Makefile      |   3 +-
+ drivers/net/ethernet/airoha/airoha_eth.c  |  60 +-
+ drivers/net/ethernet/airoha/airoha_eth.h  | 250 ++++++
+ drivers/net/ethernet/airoha/airoha_ppe.c  | 901 ++++++++++++++++++++++
+ drivers/net/ethernet/airoha/airoha_regs.h | 107 ++-
+ 5 files changed, 1314 insertions(+), 7 deletions(-)
+ create mode 100644 drivers/net/ethernet/airoha/airoha_ppe.c
+
+--- a/drivers/net/ethernet/airoha/Makefile
++++ b/drivers/net/ethernet/airoha/Makefile
+@@ -3,5 +3,6 @@
+ # Airoha for the Mediatek SoCs built-in ethernet macs
+ #
+-obj-$(CONFIG_NET_AIROHA) += airoha_eth.o
++obj-$(CONFIG_NET_AIROHA) += airoha-eth.o
++airoha-eth-y := airoha_eth.o airoha_ppe.o
+ obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -8,7 +8,6 @@
+ #include <linux/platform_device.h>
+ #include <linux/tcp.h>
+ #include <linux/u64_stats_sync.h>
+-#include <net/dsa.h>
+ #include <net/dst_metadata.h>
+ #include <net/page_pool/helpers.h>
+ #include <net/pkt_cls.h>
+@@ -619,6 +618,7 @@ static int airoha_qdma_rx_process(struct
+       while (done < budget) {
+               struct airoha_queue_entry *e = &q->entry[q->tail];
+               struct airoha_qdma_desc *desc = &q->desc[q->tail];
++              u32 hash, reason, msg1 = le32_to_cpu(desc->msg1);
+               dma_addr_t dma_addr = le32_to_cpu(desc->addr);
+               u32 desc_ctrl = le32_to_cpu(desc->ctrl);
+               struct airoha_gdm_port *port;
+@@ -681,6 +681,15 @@ static int airoha_qdma_rx_process(struct
+                                                 &port->dsa_meta[sptag]->dst);
+               }
++              hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1);
++              if (hash != AIROHA_RXD4_FOE_ENTRY)
++                      skb_set_hash(skb, jhash_1word(hash, 0),
++                                   PKT_HASH_TYPE_L4);
++
++              reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1);
++              if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED)
++                      airoha_ppe_check_skb(eth->ppe, hash);
++
+               napi_gro_receive(&q->napi, skb);
+               done++;
+@@ -1302,6 +1311,10 @@ static int airoha_hw_init(struct platfor
+                       return err;
+       }
++      err = airoha_ppe_init(eth);
++      if (err)
++              return err;
++
+       set_bit(DEV_STATE_INITIALIZED, &eth->state);
+       return 0;
+@@ -2166,6 +2179,47 @@ static int airoha_tc_htb_alloc_leaf_queu
+       return 0;
+ }
++static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port,
++                                   struct flow_block_offload *f)
++{
++      flow_setup_cb_t *cb = airoha_ppe_setup_tc_block_cb;
++      static LIST_HEAD(block_cb_list);
++      struct flow_block_cb *block_cb;
++
++      if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
++              return -EOPNOTSUPP;
++
++      f->driver_block_list = &block_cb_list;
++      switch (f->command) {
++      case FLOW_BLOCK_BIND:
++              block_cb = flow_block_cb_lookup(f->block, cb, port->dev);
++              if (block_cb) {
++                      flow_block_cb_incref(block_cb);
++                      return 0;
++              }
++              block_cb = flow_block_cb_alloc(cb, port->dev, port->dev, NULL);
++              if (IS_ERR(block_cb))
++                      return PTR_ERR(block_cb);
++
++              flow_block_cb_incref(block_cb);
++              flow_block_cb_add(block_cb, f);
++              list_add_tail(&block_cb->driver_list, &block_cb_list);
++              return 0;
++      case FLOW_BLOCK_UNBIND:
++              block_cb = flow_block_cb_lookup(f->block, cb, port->dev);
++              if (!block_cb)
++                      return -ENOENT;
++
++              if (!flow_block_cb_decref(block_cb)) {
++                      flow_block_cb_remove(block_cb, f);
++                      list_del(&block_cb->driver_list);
++              }
++              return 0;
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
+ static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue)
+ {
+       struct net_device *dev = port->dev;
+@@ -2249,6 +2303,9 @@ static int airoha_dev_tc_setup(struct ne
+               return airoha_tc_setup_qdisc_ets(port, type_data);
+       case TC_SETUP_QDISC_HTB:
+               return airoha_tc_setup_qdisc_htb(port, type_data);
++      case TC_SETUP_BLOCK:
++      case TC_SETUP_FT:
++              return airoha_dev_setup_tc_block(port, type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+@@ -2505,6 +2562,7 @@ static void airoha_remove(struct platfor
+       }
+       free_netdev(eth->napi_dev);
++      airoha_ppe_deinit(eth);
+       platform_set_drvdata(pdev, NULL);
+ }
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -12,6 +12,7 @@
+ #include <linux/kernel.h>
+ #include <linux/netdevice.h>
+ #include <linux/reset.h>
++#include <net/dsa.h>
+ #define AIROHA_MAX_NUM_GDM_PORTS      4
+ #define AIROHA_MAX_NUM_QDMA           2
+@@ -44,6 +45,15 @@
+ #define QDMA_METER_IDX(_n)            ((_n) & 0xff)
+ #define QDMA_METER_GROUP(_n)          (((_n) >> 8) & 0x3)
++#define PPE_NUM                               2
++#define PPE1_SRAM_NUM_ENTRIES         (8 * 1024)
++#define PPE_SRAM_NUM_ENTRIES          (2 * PPE1_SRAM_NUM_ENTRIES)
++#define PPE_DRAM_NUM_ENTRIES          (16 * 1024)
++#define PPE_NUM_ENTRIES                       (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES)
++#define PPE_HASH_MASK                 (PPE_NUM_ENTRIES - 1)
++#define PPE_ENTRY_SIZE                        80
++#define PPE_RAM_NUM_ENTRIES_SHIFT(_n) (__ffs((_n) >> 10))
++
+ #define MTK_HDR_LEN                   4
+ #define MTK_HDR_XMIT_TAGGED_TPID_8100 1
+ #define MTK_HDR_XMIT_TAGGED_TPID_88A8 2
+@@ -195,6 +205,224 @@ struct airoha_hw_stats {
+       u64 rx_len[7];
+ };
++enum {
++      PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f,
++};
++
++enum {
++      AIROHA_FOE_STATE_INVALID,
++      AIROHA_FOE_STATE_UNBIND,
++      AIROHA_FOE_STATE_BIND,
++      AIROHA_FOE_STATE_FIN
++};
++
++enum {
++      PPE_PKT_TYPE_IPV4_HNAPT = 0,
++      PPE_PKT_TYPE_IPV4_ROUTE = 1,
++      PPE_PKT_TYPE_BRIDGE = 2,
++      PPE_PKT_TYPE_IPV4_DSLITE = 3,
++      PPE_PKT_TYPE_IPV6_ROUTE_3T = 4,
++      PPE_PKT_TYPE_IPV6_ROUTE_5T = 5,
++      PPE_PKT_TYPE_IPV6_6RD = 7,
++};
++
++#define AIROHA_FOE_MAC_SMAC_ID                GENMASK(20, 16)
++#define AIROHA_FOE_MAC_PPPOE_ID               GENMASK(15, 0)
++
++struct airoha_foe_mac_info_common {
++      u16 vlan1;
++      u16 etype;
++
++      u32 dest_mac_hi;
++
++      u16 vlan2;
++      u16 dest_mac_lo;
++
++      u32 src_mac_hi;
++};
++
++struct airoha_foe_mac_info {
++      struct airoha_foe_mac_info_common common;
++
++      u16 pppoe_id;
++      u16 src_mac_lo;
++};
++
++#define AIROHA_FOE_IB1_UNBIND_PREBIND         BIT(24)
++#define AIROHA_FOE_IB1_UNBIND_PACKETS         GENMASK(23, 8)
++#define AIROHA_FOE_IB1_UNBIND_TIMESTAMP               GENMASK(7, 0)
++
++#define AIROHA_FOE_IB1_BIND_STATIC            BIT(31)
++#define AIROHA_FOE_IB1_BIND_UDP                       BIT(30)
++#define AIROHA_FOE_IB1_BIND_STATE             GENMASK(29, 28)
++#define AIROHA_FOE_IB1_BIND_PACKET_TYPE               GENMASK(27, 25)
++#define AIROHA_FOE_IB1_BIND_TTL                       BIT(24)
++#define AIROHA_FOE_IB1_BIND_TUNNEL_DECAP      BIT(23)
++#define AIROHA_FOE_IB1_BIND_PPPOE             BIT(22)
++#define AIROHA_FOE_IB1_BIND_VPM                       GENMASK(21, 20)
++#define AIROHA_FOE_IB1_BIND_VLAN_LAYER                GENMASK(19, 16)
++#define AIROHA_FOE_IB1_BIND_KEEPALIVE         BIT(15)
++#define AIROHA_FOE_IB1_BIND_TIMESTAMP         GENMASK(14, 0)
++
++#define AIROHA_FOE_IB2_DSCP                   GENMASK(31, 24)
++#define AIROHA_FOE_IB2_PORT_AG                        GENMASK(23, 13)
++#define AIROHA_FOE_IB2_PCP                    BIT(12)
++#define AIROHA_FOE_IB2_MULTICAST              BIT(11)
++#define AIROHA_FOE_IB2_FAST_PATH              BIT(10)
++#define AIROHA_FOE_IB2_PSE_QOS                        BIT(9)
++#define AIROHA_FOE_IB2_PSE_PORT                       GENMASK(8, 5)
++#define AIROHA_FOE_IB2_NBQ                    GENMASK(4, 0)
++
++#define AIROHA_FOE_ACTDP                      GENMASK(31, 24)
++#define AIROHA_FOE_SHAPER_ID                  GENMASK(23, 16)
++#define AIROHA_FOE_CHANNEL                    GENMASK(15, 11)
++#define AIROHA_FOE_QID                                GENMASK(10, 8)
++#define AIROHA_FOE_DPI                                BIT(7)
++#define AIROHA_FOE_TUNNEL                     BIT(6)
++#define AIROHA_FOE_TUNNEL_ID                  GENMASK(5, 0)
++
++struct airoha_foe_bridge {
++      u32 dest_mac_hi;
++
++      u16 src_mac_hi;
++      u16 dest_mac_lo;
++
++      u32 src_mac_lo;
++
++      u32 ib2;
++
++      u32 rsv[5];
++
++      u32 data;
++
++      struct airoha_foe_mac_info l2;
++};
++
++struct airoha_foe_ipv4_tuple {
++      u32 src_ip;
++      u32 dest_ip;
++      union {
++              struct {
++                      u16 dest_port;
++                      u16 src_port;
++              };
++              struct {
++                      u8 protocol;
++                      u8 _pad[3]; /* fill with 0xa5a5a5 */
++              };
++              u32 ports;
++      };
++};
++
++struct airoha_foe_ipv4 {
++      struct airoha_foe_ipv4_tuple orig_tuple;
++
++      u32 ib2;
++
++      struct airoha_foe_ipv4_tuple new_tuple;
++
++      u32 rsv[2];
++
++      u32 data;
++
++      struct airoha_foe_mac_info l2;
++};
++
++struct airoha_foe_ipv4_dslite {
++      struct airoha_foe_ipv4_tuple ip4;
++
++      u32 ib2;
++
++      u8 flow_label[3];
++      u8 priority;
++
++      u32 rsv[4];
++
++      u32 data;
++
++      struct airoha_foe_mac_info l2;
++};
++
++struct airoha_foe_ipv6 {
++      u32 src_ip[4];
++      u32 dest_ip[4];
++
++      union {
++              struct {
++                      u16 dest_port;
++                      u16 src_port;
++              };
++              struct {
++                      u8 protocol;
++                      u8 pad[3];
++              };
++              u32 ports;
++      };
++
++      u32 data;
++
++      u32 ib2;
++
++      struct airoha_foe_mac_info_common l2;
++};
++
++struct airoha_foe_entry {
++      union {
++              struct {
++                      u32 ib1;
++                      union {
++                              struct airoha_foe_bridge bridge;
++                              struct airoha_foe_ipv4 ipv4;
++                              struct airoha_foe_ipv4_dslite dslite;
++                              struct airoha_foe_ipv6 ipv6;
++                              DECLARE_FLEX_ARRAY(u32, d);
++                      };
++              };
++              u8 data[PPE_ENTRY_SIZE];
++      };
++};
++
++struct airoha_flow_data {
++      struct ethhdr eth;
++
++      union {
++              struct {
++                      __be32 src_addr;
++                      __be32 dst_addr;
++              } v4;
++
++              struct {
++                      struct in6_addr src_addr;
++                      struct in6_addr dst_addr;
++              } v6;
++      };
++
++      __be16 src_port;
++      __be16 dst_port;
++
++      struct {
++              struct {
++                      u16 id;
++                      __be16 proto;
++              } hdr[2];
++              u8 num;
++      } vlan;
++      struct {
++              u16 sid;
++              u8 num;
++      } pppoe;
++};
++
++struct airoha_flow_table_entry {
++      struct hlist_node list;
++
++      struct airoha_foe_entry data;
++      u32 hash;
++
++      struct rhash_head node;
++      unsigned long cookie;
++};
++
+ struct airoha_qdma {
+       struct airoha_eth *eth;
+       void __iomem *regs;
+@@ -234,6 +462,19 @@ struct airoha_gdm_port {
+       struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
+ };
++#define AIROHA_RXD4_PPE_CPU_REASON    GENMASK(20, 16)
++#define AIROHA_RXD4_FOE_ENTRY         GENMASK(15, 0)
++
++struct airoha_ppe {
++      struct airoha_eth *eth;
++
++      void *foe;
++      dma_addr_t foe_dma;
++
++      struct hlist_head *foe_flow;
++      u16 foe_check_time[PPE_NUM_ENTRIES];
++};
++
+ struct airoha_eth {
+       struct device *dev;
+@@ -242,6 +483,9 @@ struct airoha_eth {
+       struct airoha_npu __rcu *npu;
++      struct airoha_ppe *ppe;
++      struct rhashtable flow_table;
++
+       struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
+       struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
+@@ -277,4 +521,10 @@ u32 airoha_rmw(void __iomem *base, u32 o
+ #define airoha_qdma_clear(qdma, offset, val)                  \
+       airoha_rmw((qdma)->regs, (offset), (val), 0)
++void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash);
++int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
++                               void *cb_priv);
++int airoha_ppe_init(struct airoha_eth *eth);
++void airoha_ppe_deinit(struct airoha_eth *eth);
++
+ #endif /* AIROHA_ETH_H */
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_ppe.c
+@@ -0,0 +1,901 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2025 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++
++#include <linux/ip.h>
++#include <linux/ipv6.h>
++#include <linux/rhashtable.h>
++#include <net/ipv6.h>
++#include <net/pkt_cls.h>
++
++#include "airoha_npu.h"
++#include "airoha_regs.h"
++#include "airoha_eth.h"
++
++static DEFINE_MUTEX(flow_offload_mutex);
++static DEFINE_SPINLOCK(ppe_lock);
++
++static const struct rhashtable_params airoha_flow_table_params = {
++      .head_offset = offsetof(struct airoha_flow_table_entry, node),
++      .key_offset = offsetof(struct airoha_flow_table_entry, cookie),
++      .key_len = sizeof(unsigned long),
++      .automatic_shrinking = true,
++};
++
++static bool airoha_ppe2_is_enabled(struct airoha_eth *eth)
++{
++      return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK;
++}
++
++static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
++{
++      u16 timestamp = airoha_fe_rr(ppe->eth, REG_FE_FOE_TS);
++
++      return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp);
++}
++
++static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
++{
++      u32 sram_tb_size, sram_num_entries, dram_num_entries;
++      struct airoha_eth *eth = ppe->eth;
++      int i;
++
++      sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry);
++      dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES);
++
++      for (i = 0; i < PPE_NUM; i++) {
++              int p;
++
++              airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
++                           ppe->foe_dma + sram_tb_size);
++
++              airoha_fe_rmw(eth, REG_PPE_BND_AGE0(i),
++                            PPE_BIND_AGE0_DELTA_NON_L4 |
++                            PPE_BIND_AGE0_DELTA_UDP,
++                            FIELD_PREP(PPE_BIND_AGE0_DELTA_NON_L4, 1) |
++                            FIELD_PREP(PPE_BIND_AGE0_DELTA_UDP, 12));
++              airoha_fe_rmw(eth, REG_PPE_BND_AGE1(i),
++                            PPE_BIND_AGE1_DELTA_TCP_FIN |
++                            PPE_BIND_AGE1_DELTA_TCP,
++                            FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP_FIN, 1) |
++                            FIELD_PREP(PPE_BIND_AGE1_DELTA_TCP, 7));
++
++              airoha_fe_rmw(eth, REG_PPE_TB_HASH_CFG(i),
++                            PPE_SRAM_TABLE_EN_MASK |
++                            PPE_SRAM_HASH1_EN_MASK |
++                            PPE_DRAM_TABLE_EN_MASK |
++                            PPE_SRAM_HASH0_MODE_MASK |
++                            PPE_SRAM_HASH1_MODE_MASK |
++                            PPE_DRAM_HASH0_MODE_MASK |
++                            PPE_DRAM_HASH1_MODE_MASK,
++                            FIELD_PREP(PPE_SRAM_TABLE_EN_MASK, 1) |
++                            FIELD_PREP(PPE_SRAM_HASH1_EN_MASK, 1) |
++                            FIELD_PREP(PPE_SRAM_HASH1_MODE_MASK, 1) |
++                            FIELD_PREP(PPE_DRAM_HASH1_MODE_MASK, 3));
++
++              airoha_fe_rmw(eth, REG_PPE_TB_CFG(i),
++                            PPE_TB_CFG_SEARCH_MISS_MASK |
++                            PPE_TB_ENTRY_SIZE_MASK,
++                            FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) |
++                            FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0));
++
++              airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
++
++              for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
++                      airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
++                                    FP0_EGRESS_MTU_MASK |
++                                    FP1_EGRESS_MTU_MASK,
++                                    FIELD_PREP(FP0_EGRESS_MTU_MASK,
++                                               AIROHA_MAX_MTU) |
++                                    FIELD_PREP(FP1_EGRESS_MTU_MASK,
++                                               AIROHA_MAX_MTU));
++      }
++
++      if (airoha_ppe2_is_enabled(eth)) {
++              sram_num_entries =
++                      PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_ENTRIES);
++              airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
++                            PPE_SRAM_TB_NUM_ENTRY_MASK |
++                            PPE_DRAM_TB_NUM_ENTRY_MASK,
++                            FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
++                                       sram_num_entries) |
++                            FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
++                                       dram_num_entries));
++              airoha_fe_rmw(eth, REG_PPE_TB_CFG(1),
++                            PPE_SRAM_TB_NUM_ENTRY_MASK |
++                            PPE_DRAM_TB_NUM_ENTRY_MASK,
++                            FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
++                                       sram_num_entries) |
++                            FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
++                                       dram_num_entries));
++      } else {
++              sram_num_entries =
++                      PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_ENTRIES);
++              airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
++                            PPE_SRAM_TB_NUM_ENTRY_MASK |
++                            PPE_DRAM_TB_NUM_ENTRY_MASK,
++                            FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
++                                       sram_num_entries) |
++                            FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
++                                       dram_num_entries));
++      }
++}
++
++static void airoha_ppe_flow_mangle_eth(const struct flow_action_entry *act, void *eth)
++{
++      void *dest = eth + act->mangle.offset;
++      const void *src = &act->mangle.val;
++
++      if (act->mangle.offset > 8)
++              return;
++
++      if (act->mangle.mask == 0xffff) {
++              src += 2;
++              dest += 2;
++      }
++
++      memcpy(dest, src, act->mangle.mask ? 2 : 4);
++}
++
++static int airoha_ppe_flow_mangle_ports(const struct flow_action_entry *act,
++                                      struct airoha_flow_data *data)
++{
++      u32 val = be32_to_cpu((__force __be32)act->mangle.val);
++
++      switch (act->mangle.offset) {
++      case 0:
++              if ((__force __be32)act->mangle.mask == ~cpu_to_be32(0xffff))
++                      data->dst_port = cpu_to_be16(val);
++              else
++                      data->src_port = cpu_to_be16(val >> 16);
++              break;
++      case 2:
++              data->dst_port = cpu_to_be16(val);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int airoha_ppe_flow_mangle_ipv4(const struct flow_action_entry *act,
++                                     struct airoha_flow_data *data)
++{
++      __be32 *dest;
++
++      switch (act->mangle.offset) {
++      case offsetof(struct iphdr, saddr):
++              dest = &data->v4.src_addr;
++              break;
++      case offsetof(struct iphdr, daddr):
++              dest = &data->v4.dst_addr;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      memcpy(dest, &act->mangle.val, sizeof(u32));
++
++      return 0;
++}
++
++static int airoha_get_dsa_port(struct net_device **dev)
++{
++#if IS_ENABLED(CONFIG_NET_DSA)
++      struct dsa_port *dp = dsa_port_from_netdev(*dev);
++
++      if (IS_ERR(dp))
++              return -ENODEV;
++
++      *dev = dsa_port_to_master(dp);
++      return dp->index;
++#else
++      return -ENODEV;
++#endif
++}
++
++static int airoha_ppe_foe_entry_prepare(struct airoha_foe_entry *hwe,
++                                      struct net_device *dev, int type,
++                                      struct airoha_flow_data *data,
++                                      int l4proto)
++{
++      int dsa_port = airoha_get_dsa_port(&dev);
++      struct airoha_foe_mac_info_common *l2;
++      u32 qdata, ports_pad, val;
++
++      memset(hwe, 0, sizeof(*hwe));
++
++      val = FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, AIROHA_FOE_STATE_BIND) |
++            FIELD_PREP(AIROHA_FOE_IB1_BIND_PACKET_TYPE, type) |
++            FIELD_PREP(AIROHA_FOE_IB1_BIND_UDP, l4proto == IPPROTO_UDP) |
++            FIELD_PREP(AIROHA_FOE_IB1_BIND_VLAN_LAYER, data->vlan.num) |
++            FIELD_PREP(AIROHA_FOE_IB1_BIND_VPM, data->vlan.num) |
++            AIROHA_FOE_IB1_BIND_TTL;
++      hwe->ib1 = val;
++
++      val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f);
++      if (dsa_port >= 0)
++              val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port);
++
++      if (dev) {
++              struct airoha_gdm_port *port = netdev_priv(dev);
++              u8 pse_port;
++
++              pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
++              val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port);
++      }
++
++      /* FIXME: implement QoS support setting pse_port to 2 (loopback)
++       * for uplink and setting qos bit in ib2
++       */
++
++      if (is_multicast_ether_addr(data->eth.h_dest))
++              val |= AIROHA_FOE_IB2_MULTICAST;
++
++      ports_pad = 0xa5a5a500 | (l4proto & 0xff);
++      if (type == PPE_PKT_TYPE_IPV4_ROUTE)
++              hwe->ipv4.orig_tuple.ports = ports_pad;
++      if (type == PPE_PKT_TYPE_IPV6_ROUTE_3T)
++              hwe->ipv6.ports = ports_pad;
++
++      qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f);
++      if (type == PPE_PKT_TYPE_BRIDGE) {
++              hwe->bridge.dest_mac_hi = get_unaligned_be32(data->eth.h_dest);
++              hwe->bridge.dest_mac_lo =
++                      get_unaligned_be16(data->eth.h_dest + 4);
++              hwe->bridge.src_mac_hi =
++                      get_unaligned_be16(data->eth.h_source);
++              hwe->bridge.src_mac_lo =
++                      get_unaligned_be32(data->eth.h_source + 2);
++              hwe->bridge.data = qdata;
++              hwe->bridge.ib2 = val;
++              l2 = &hwe->bridge.l2.common;
++      } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) {
++              hwe->ipv6.data = qdata;
++              hwe->ipv6.ib2 = val;
++              l2 = &hwe->ipv6.l2;
++      } else {
++              hwe->ipv4.data = qdata;
++              hwe->ipv4.ib2 = val;
++              l2 = &hwe->ipv4.l2.common;
++      }
++
++      l2->dest_mac_hi = get_unaligned_be32(data->eth.h_dest);
++      l2->dest_mac_lo = get_unaligned_be16(data->eth.h_dest + 4);
++      if (type <= PPE_PKT_TYPE_IPV4_DSLITE) {
++              l2->src_mac_hi = get_unaligned_be32(data->eth.h_source);
++              hwe->ipv4.l2.src_mac_lo =
++                      get_unaligned_be16(data->eth.h_source + 4);
++      } else {
++              l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, 0xf);
++      }
++
++      if (data->vlan.num) {
++              l2->etype = dsa_port >= 0 ? BIT(dsa_port) : 0;
++              l2->vlan1 = data->vlan.hdr[0].id;
++              if (data->vlan.num == 2)
++                      l2->vlan2 = data->vlan.hdr[1].id;
++      } else if (dsa_port >= 0) {
++              l2->etype = BIT(15) | BIT(dsa_port);
++      } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) {
++              l2->etype = ETH_P_IPV6;
++      } else {
++              l2->etype = ETH_P_IP;
++      }
++
++      return 0;
++}
++
++static int airoha_ppe_foe_entry_set_ipv4_tuple(struct airoha_foe_entry *hwe,
++                                             struct airoha_flow_data *data,
++                                             bool egress)
++{
++      int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
++      struct airoha_foe_ipv4_tuple *t;
++
++      switch (type) {
++      case PPE_PKT_TYPE_IPV4_HNAPT:
++              if (egress) {
++                      t = &hwe->ipv4.new_tuple;
++                      break;
++              }
++              fallthrough;
++      case PPE_PKT_TYPE_IPV4_DSLITE:
++      case PPE_PKT_TYPE_IPV4_ROUTE:
++              t = &hwe->ipv4.orig_tuple;
++              break;
++      default:
++              WARN_ON_ONCE(1);
++              return -EINVAL;
++      }
++
++      t->src_ip = be32_to_cpu(data->v4.src_addr);
++      t->dest_ip = be32_to_cpu(data->v4.dst_addr);
++
++      if (type != PPE_PKT_TYPE_IPV4_ROUTE) {
++              t->src_port = be16_to_cpu(data->src_port);
++              t->dest_port = be16_to_cpu(data->dst_port);
++      }
++
++      return 0;
++}
++
++static int airoha_ppe_foe_entry_set_ipv6_tuple(struct airoha_foe_entry *hwe,
++                                             struct airoha_flow_data *data)
++
++{
++      int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
++      u32 *src, *dest;
++
++      switch (type) {
++      case PPE_PKT_TYPE_IPV6_ROUTE_5T:
++      case PPE_PKT_TYPE_IPV6_6RD:
++              hwe->ipv6.src_port = be16_to_cpu(data->src_port);
++              hwe->ipv6.dest_port = be16_to_cpu(data->dst_port);
++              fallthrough;
++      case PPE_PKT_TYPE_IPV6_ROUTE_3T:
++              src = hwe->ipv6.src_ip;
++              dest = hwe->ipv6.dest_ip;
++              break;
++      default:
++              WARN_ON_ONCE(1);
++              return -EINVAL;
++      }
++
++      ipv6_addr_be32_to_cpu(src, data->v6.src_addr.s6_addr32);
++      ipv6_addr_be32_to_cpu(dest, data->v6.dst_addr.s6_addr32);
++
++      return 0;
++}
++
++static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
++{
++      int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
++      u32 hash, hv1, hv2, hv3;
++
++      switch (type) {
++      case PPE_PKT_TYPE_IPV4_ROUTE:
++      case PPE_PKT_TYPE_IPV4_HNAPT:
++              hv1 = hwe->ipv4.orig_tuple.ports;
++              hv2 = hwe->ipv4.orig_tuple.dest_ip;
++              hv3 = hwe->ipv4.orig_tuple.src_ip;
++              break;
++      case PPE_PKT_TYPE_IPV6_ROUTE_3T:
++      case PPE_PKT_TYPE_IPV6_ROUTE_5T:
++              hv1 = hwe->ipv6.src_ip[3] ^ hwe->ipv6.dest_ip[3];
++              hv1 ^= hwe->ipv6.ports;
++
++              hv2 = hwe->ipv6.src_ip[2] ^ hwe->ipv6.dest_ip[2];
++              hv2 ^= hwe->ipv6.dest_ip[0];
++
++              hv3 = hwe->ipv6.src_ip[1] ^ hwe->ipv6.dest_ip[1];
++              hv3 ^= hwe->ipv6.src_ip[0];
++              break;
++      case PPE_PKT_TYPE_IPV4_DSLITE:
++      case PPE_PKT_TYPE_IPV6_6RD:
++      default:
++              WARN_ON_ONCE(1);
++              return PPE_HASH_MASK;
++      }
++
++      hash = (hv1 & hv2) | ((~hv1) & hv3);
++      hash = (hash >> 24) | ((hash & 0xffffff) << 8);
++      hash ^= hv1 ^ hv2 ^ hv3;
++      hash ^= hash >> 16;
++      hash &= PPE_NUM_ENTRIES - 1;
++
++      return hash;
++}
++
++static struct airoha_foe_entry *
++airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash)
++{
++      if (hash < PPE_SRAM_NUM_ENTRIES) {
++              u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry);
++              struct airoha_eth *eth = ppe->eth;
++              bool ppe2;
++              u32 val;
++              int i;
++
++              ppe2 = airoha_ppe2_is_enabled(ppe->eth) &&
++                     hash >= PPE1_SRAM_NUM_ENTRIES;
++              airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2),
++                           FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) |
++                           PPE_SRAM_CTRL_REQ_MASK);
++              if (read_poll_timeout_atomic(airoha_fe_rr, val,
++                                           val & PPE_SRAM_CTRL_ACK_MASK,
++                                           10, 100, false, eth,
++                                           REG_PPE_RAM_CTRL(ppe2)))
++                      return NULL;
++
++              for (i = 0; i < sizeof(struct airoha_foe_entry) / 4; i++)
++                      hwe[i] = airoha_fe_rr(eth,
++                                            REG_PPE_RAM_ENTRY(ppe2, i));
++      }
++
++      return ppe->foe + hash * sizeof(struct airoha_foe_entry);
++}
++
++static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e,
++                                       struct airoha_foe_entry *hwe)
++{
++      int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1);
++      int len;
++
++      if ((hwe->ib1 ^ e->data.ib1) & AIROHA_FOE_IB1_BIND_UDP)
++              return false;
++
++      if (type > PPE_PKT_TYPE_IPV4_DSLITE)
++              len = offsetof(struct airoha_foe_entry, ipv6.data);
++      else
++              len = offsetof(struct airoha_foe_entry, ipv4.ib2);
++
++      return !memcmp(&e->data.d, &hwe->d, len - sizeof(hwe->ib1));
++}
++
++static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
++                                     struct airoha_foe_entry *e,
++                                     u32 hash)
++{
++      struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
++      u32 ts = airoha_ppe_get_timestamp(ppe);
++      struct airoha_eth *eth = ppe->eth;
++
++      memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1));
++      wmb();
++
++      e->ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP;
++      e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts);
++      hwe->ib1 = e->ib1;
++
++      if (hash < PPE_SRAM_NUM_ENTRIES) {
++              dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe);
++              bool ppe2 = airoha_ppe2_is_enabled(eth) &&
++                          hash >= PPE1_SRAM_NUM_ENTRIES;
++              struct airoha_npu *npu;
++              int err = -ENODEV;
++
++              rcu_read_lock();
++              npu = rcu_dereference(eth->npu);
++              if (npu)
++                      err = npu->ops.ppe_foe_commit_entry(npu, addr,
++                                                          sizeof(*hwe), hash,
++                                                          ppe2);
++              rcu_read_unlock();
++
++              return err;
++      }
++
++      return 0;
++}
++
++static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash)
++{
++      struct airoha_flow_table_entry *e;
++      struct airoha_foe_entry *hwe;
++      struct hlist_node *n;
++      u32 index, state;
++
++      spin_lock_bh(&ppe_lock);
++
++      hwe = airoha_ppe_foe_get_entry(ppe, hash);
++      if (!hwe)
++              goto unlock;
++
++      state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1);
++      if (state == AIROHA_FOE_STATE_BIND)
++              goto unlock;
++
++      index = airoha_ppe_foe_get_entry_hash(hwe);
++      hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) {
++              if (airoha_ppe_foe_compare_entry(e, hwe)) {
++                      airoha_ppe_foe_commit_entry(ppe, &e->data, hash);
++                      e->hash = hash;
++                      break;
++              }
++      }
++unlock:
++      spin_unlock_bh(&ppe_lock);
++}
++
++static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe,
++                                          struct airoha_flow_table_entry *e)
++{
++      u32 hash = airoha_ppe_foe_get_entry_hash(&e->data);
++
++      e->hash = 0xffff;
++
++      spin_lock_bh(&ppe_lock);
++      hlist_add_head(&e->list, &ppe->foe_flow[hash]);
++      spin_unlock_bh(&ppe_lock);
++
++      return 0;
++}
++
++static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe,
++                                           struct airoha_flow_table_entry *e)
++{
++      spin_lock_bh(&ppe_lock);
++
++      hlist_del_init(&e->list);
++      if (e->hash != 0xffff) {
++              e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE;
++              e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE,
++                                        AIROHA_FOE_STATE_INVALID);
++              airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash);
++              e->hash = 0xffff;
++      }
++
++      spin_unlock_bh(&ppe_lock);
++}
++
++static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port,
++                                         struct flow_cls_offload *f)
++{
++      struct flow_rule *rule = flow_cls_offload_flow_rule(f);
++      struct airoha_eth *eth = port->qdma->eth;
++      struct airoha_flow_table_entry *e;
++      struct airoha_flow_data data = {};
++      struct net_device *odev = NULL;
++      struct flow_action_entry *act;
++      struct airoha_foe_entry hwe;
++      int err, i, offload_type;
++      u16 addr_type = 0;
++      u8 l4proto = 0;
++
++      if (rhashtable_lookup(&eth->flow_table, &f->cookie,
++                            airoha_flow_table_params))
++              return -EEXIST;
++
++      if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META))
++              return -EOPNOTSUPP;
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
++              struct flow_match_control match;
++
++              flow_rule_match_control(rule, &match);
++              addr_type = match.key->addr_type;
++              if (flow_rule_has_control_flags(match.mask->flags,
++                                              f->common.extack))
++                      return -EOPNOTSUPP;
++      } else {
++              return -EOPNOTSUPP;
++      }
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
++              struct flow_match_basic match;
++
++              flow_rule_match_basic(rule, &match);
++              l4proto = match.key->ip_proto;
++      } else {
++              return -EOPNOTSUPP;
++      }
++
++      switch (addr_type) {
++      case 0:
++              offload_type = PPE_PKT_TYPE_BRIDGE;
++              if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
++                      struct flow_match_eth_addrs match;
++
++                      flow_rule_match_eth_addrs(rule, &match);
++                      memcpy(data.eth.h_dest, match.key->dst, ETH_ALEN);
++                      memcpy(data.eth.h_source, match.key->src, ETH_ALEN);
++              } else {
++                      return -EOPNOTSUPP;
++              }
++              break;
++      case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
++              offload_type = PPE_PKT_TYPE_IPV4_HNAPT;
++              break;
++      case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
++              offload_type = PPE_PKT_TYPE_IPV6_ROUTE_5T;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      flow_action_for_each(i, act, &rule->action) {
++              switch (act->id) {
++              case FLOW_ACTION_MANGLE:
++                      if (offload_type == PPE_PKT_TYPE_BRIDGE)
++                              return -EOPNOTSUPP;
++
++                      if (act->mangle.htype == FLOW_ACT_MANGLE_HDR_TYPE_ETH)
++                              airoha_ppe_flow_mangle_eth(act, &data.eth);
++                      break;
++              case FLOW_ACTION_REDIRECT:
++                      odev = act->dev;
++                      break;
++              case FLOW_ACTION_CSUM:
++                      break;
++              case FLOW_ACTION_VLAN_PUSH:
++                      if (data.vlan.num == 2 ||
++                          act->vlan.proto != htons(ETH_P_8021Q))
++                              return -EOPNOTSUPP;
++
++                      data.vlan.hdr[data.vlan.num].id = act->vlan.vid;
++                      data.vlan.hdr[data.vlan.num].proto = act->vlan.proto;
++                      data.vlan.num++;
++                      break;
++              case FLOW_ACTION_VLAN_POP:
++                      break;
++              case FLOW_ACTION_PPPOE_PUSH:
++                      break;
++              default:
++                      return -EOPNOTSUPP;
++              }
++      }
++
++      if (!is_valid_ether_addr(data.eth.h_source) ||
++          !is_valid_ether_addr(data.eth.h_dest))
++              return -EINVAL;
++
++      err = airoha_ppe_foe_entry_prepare(&hwe, odev, offload_type,
++                                         &data, l4proto);
++      if (err)
++              return err;
++
++      if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
++              struct flow_match_ports ports;
++
++              if (offload_type == PPE_PKT_TYPE_BRIDGE)
++                      return -EOPNOTSUPP;
++
++              flow_rule_match_ports(rule, &ports);
++              data.src_port = ports.key->src;
++              data.dst_port = ports.key->dst;
++      } else if (offload_type != PPE_PKT_TYPE_BRIDGE) {
++              return -EOPNOTSUPP;
++      }
++
++      if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++              struct flow_match_ipv4_addrs addrs;
++
++              flow_rule_match_ipv4_addrs(rule, &addrs);
++              data.v4.src_addr = addrs.key->src;
++              data.v4.dst_addr = addrs.key->dst;
++              airoha_ppe_foe_entry_set_ipv4_tuple(&hwe, &data, false);
++      }
++
++      if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
++              struct flow_match_ipv6_addrs addrs;
++
++              flow_rule_match_ipv6_addrs(rule, &addrs);
++
++              data.v6.src_addr = addrs.key->src;
++              data.v6.dst_addr = addrs.key->dst;
++              airoha_ppe_foe_entry_set_ipv6_tuple(&hwe, &data);
++      }
++
++      flow_action_for_each(i, act, &rule->action) {
++              if (act->id != FLOW_ACTION_MANGLE)
++                      continue;
++
++              if (offload_type == PPE_PKT_TYPE_BRIDGE)
++                      return -EOPNOTSUPP;
++
++              switch (act->mangle.htype) {
++              case FLOW_ACT_MANGLE_HDR_TYPE_TCP:
++              case FLOW_ACT_MANGLE_HDR_TYPE_UDP:
++                      err = airoha_ppe_flow_mangle_ports(act, &data);
++                      break;
++              case FLOW_ACT_MANGLE_HDR_TYPE_IP4:
++                      err = airoha_ppe_flow_mangle_ipv4(act, &data);
++                      break;
++              case FLOW_ACT_MANGLE_HDR_TYPE_ETH:
++                      /* handled earlier */
++                      break;
++              default:
++                      return -EOPNOTSUPP;
++              }
++
++              if (err)
++                      return err;
++      }
++
++      if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
++              err = airoha_ppe_foe_entry_set_ipv4_tuple(&hwe, &data, true);
++              if (err)
++                      return err;
++      }
++
++      e = kzalloc(sizeof(*e), GFP_KERNEL);
++      if (!e)
++              return -ENOMEM;
++
++      e->cookie = f->cookie;
++      memcpy(&e->data, &hwe, sizeof(e->data));
++
++      err = airoha_ppe_foe_flow_commit_entry(eth->ppe, e);
++      if (err)
++              goto free_entry;
++
++      err = rhashtable_insert_fast(&eth->flow_table, &e->node,
++                                   airoha_flow_table_params);
++      if (err < 0)
++              goto remove_foe_entry;
++
++      return 0;
++
++remove_foe_entry:
++      airoha_ppe_foe_flow_remove_entry(eth->ppe, e);
++free_entry:
++      kfree(e);
++
++      return err;
++}
++
++static int airoha_ppe_flow_offload_destroy(struct airoha_gdm_port *port,
++                                         struct flow_cls_offload *f)
++{
++      struct airoha_eth *eth = port->qdma->eth;
++      struct airoha_flow_table_entry *e;
++
++      e = rhashtable_lookup(&eth->flow_table, &f->cookie,
++                            airoha_flow_table_params);
++      if (!e)
++              return -ENOENT;
++
++      airoha_ppe_foe_flow_remove_entry(eth->ppe, e);
++      rhashtable_remove_fast(&eth->flow_table, &e->node,
++                             airoha_flow_table_params);
++      kfree(e);
++
++      return 0;
++}
++
++static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port,
++                                     struct flow_cls_offload *f)
++{
++      switch (f->command) {
++      case FLOW_CLS_REPLACE:
++              return airoha_ppe_flow_offload_replace(port, f);
++      case FLOW_CLS_DESTROY:
++              return airoha_ppe_flow_offload_destroy(port, f);
++      default:
++              break;
++      }
++
++      return -EOPNOTSUPP;
++}
++
++static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe,
++                                       struct airoha_npu *npu)
++{
++      int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES;
++      struct airoha_foe_entry *hwe = ppe->foe;
++
++      if (airoha_ppe2_is_enabled(ppe->eth))
++              sram_num_entries = sram_num_entries / 2;
++
++      for (i = 0; i < sram_num_entries; i++)
++              memset(&hwe[i], 0, sizeof(*hwe));
++
++      return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma,
++                                             PPE_SRAM_NUM_ENTRIES);
++}
++
++static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth)
++{
++      struct airoha_npu *npu = airoha_npu_get(eth->dev);
++
++      if (IS_ERR(npu)) {
++              request_module("airoha-npu");
++              npu = airoha_npu_get(eth->dev);
++      }
++
++      return npu;
++}
++
++static int airoha_ppe_offload_setup(struct airoha_eth *eth)
++{
++      struct airoha_npu *npu = airoha_ppe_npu_get(eth);
++      int err;
++
++      if (IS_ERR(npu))
++              return PTR_ERR(npu);
++
++      err = npu->ops.ppe_init(npu);
++      if (err)
++              goto error_npu_put;
++
++      airoha_ppe_hw_init(eth->ppe);
++      err = airoha_ppe_flush_sram_entries(eth->ppe, npu);
++      if (err)
++              goto error_npu_put;
++
++      rcu_assign_pointer(eth->npu, npu);
++      synchronize_rcu();
++
++      return 0;
++
++error_npu_put:
++      airoha_npu_put(npu);
++
++      return err;
++}
++
++int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
++                               void *cb_priv)
++{
++      struct flow_cls_offload *cls = type_data;
++      struct net_device *dev = cb_priv;
++      struct airoha_gdm_port *port = netdev_priv(dev);
++      struct airoha_eth *eth = port->qdma->eth;
++      int err = 0;
++
++      if (!tc_can_offload(dev) || type != TC_SETUP_CLSFLOWER)
++              return -EOPNOTSUPP;
++
++      mutex_lock(&flow_offload_mutex);
++
++      if (!eth->npu)
++              err = airoha_ppe_offload_setup(eth);
++      if (!err)
++              err = airoha_ppe_flow_offload_cmd(port, cls);
++
++      mutex_unlock(&flow_offload_mutex);
++
++      return err;
++}
++
++void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash)
++{
++      u16 now, diff;
++
++      if (hash > PPE_HASH_MASK)
++              return;
++
++      now = (u16)jiffies;
++      diff = now - ppe->foe_check_time[hash];
++      if (diff < HZ / 10)
++              return;
++
++      ppe->foe_check_time[hash] = now;
++      airoha_ppe_foe_insert_entry(ppe, hash);
++}
++
++int airoha_ppe_init(struct airoha_eth *eth)
++{
++      struct airoha_ppe *ppe;
++      int foe_size;
++
++      ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL);
++      if (!ppe)
++              return -ENOMEM;
++
++      foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry);
++      ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma,
++                                     GFP_KERNEL);
++      if (!ppe->foe)
++              return -ENOMEM;
++
++      ppe->eth = eth;
++      eth->ppe = ppe;
++
++      ppe->foe_flow = devm_kzalloc(eth->dev,
++                                   PPE_NUM_ENTRIES * sizeof(*ppe->foe_flow),
++                                   GFP_KERNEL);
++      if (!ppe->foe_flow)
++              return -ENOMEM;
++
++      return rhashtable_init(&eth->flow_table, &airoha_flow_table_params);
++}
++
++void airoha_ppe_deinit(struct airoha_eth *eth)
++{
++      struct airoha_npu *npu;
++
++      rcu_read_lock();
++      npu = rcu_dereference(eth->npu);
++      if (npu) {
++              npu->ops.ppe_deinit(npu);
++              airoha_npu_put(npu);
++      }
++      rcu_read_unlock();
++
++      rhashtable_destroy(&eth->flow_table);
++}
+--- a/drivers/net/ethernet/airoha/airoha_regs.h
++++ b/drivers/net/ethernet/airoha/airoha_regs.h
+@@ -15,6 +15,7 @@
+ #define CDM1_BASE                     0x0400
+ #define GDM1_BASE                     0x0500
+ #define PPE1_BASE                     0x0c00
++#define PPE2_BASE                     0x1c00
+ #define CDM2_BASE                     0x1400
+ #define GDM2_BASE                     0x1500
+@@ -36,6 +37,7 @@
+ #define FE_RST_GDM3_MBI_ARB_MASK      BIT(2)
+ #define FE_RST_CORE_MASK              BIT(0)
++#define REG_FE_FOE_TS                 0x0010
+ #define REG_FE_WAN_MAC_H              0x0030
+ #define REG_FE_LAN_MAC_H              0x0040
+@@ -192,11 +194,106 @@
+ #define REG_FE_GDM_RX_ETH_L511_CNT_L(_n)      (GDM_BASE(_n) + 0x198)
+ #define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n)     (GDM_BASE(_n) + 0x19c)
+-#define REG_PPE1_TB_HASH_CFG          (PPE1_BASE + 0x250)
+-#define PPE1_SRAM_TABLE_EN_MASK               BIT(0)
+-#define PPE1_SRAM_HASH1_EN_MASK               BIT(8)
+-#define PPE1_DRAM_TABLE_EN_MASK               BIT(16)
+-#define PPE1_DRAM_HASH1_EN_MASK               BIT(24)
++#define REG_PPE_GLO_CFG(_n)                   (((_n) ? PPE2_BASE : PPE1_BASE) + 0x200)
++#define PPE_GLO_CFG_BUSY_MASK                 BIT(31)
++#define PPE_GLO_CFG_FLOW_DROP_UPDATE_MASK     BIT(9)
++#define PPE_GLO_CFG_PSE_HASH_OFS_MASK         BIT(6)
++#define PPE_GLO_CFG_PPE_BSWAP_MASK            BIT(5)
++#define PPE_GLO_CFG_TTL_DROP_MASK             BIT(4)
++#define PPE_GLO_CFG_IP4_CS_DROP_MASK          BIT(3)
++#define PPE_GLO_CFG_IP4_L4_CS_DROP_MASK               BIT(2)
++#define PPE_GLO_CFG_EN_MASK                   BIT(0)
++
++#define REG_PPE_PPE_FLOW_CFG(_n)              (((_n) ? PPE2_BASE : PPE1_BASE) + 0x204)
++#define PPE_FLOW_CFG_IP6_HASH_GRE_KEY_MASK    BIT(20)
++#define PPE_FLOW_CFG_IP4_HASH_GRE_KEY_MASK    BIT(19)
++#define PPE_FLOW_CFG_IP4_HASH_FLOW_LABEL_MASK BIT(18)
++#define PPE_FLOW_CFG_IP4_NAT_FRAG_MASK                BIT(17)
++#define PPE_FLOW_CFG_IP_PROTO_BLACKLIST_MASK  BIT(16)
++#define PPE_FLOW_CFG_IP4_DSLITE_MASK          BIT(14)
++#define PPE_FLOW_CFG_IP4_NAPT_MASK            BIT(13)
++#define PPE_FLOW_CFG_IP4_NAT_MASK             BIT(12)
++#define PPE_FLOW_CFG_IP6_6RD_MASK             BIT(10)
++#define PPE_FLOW_CFG_IP6_5T_ROUTE_MASK                BIT(9)
++#define PPE_FLOW_CFG_IP6_3T_ROUTE_MASK                BIT(8)
++#define PPE_FLOW_CFG_IP4_UDP_FRAG_MASK                BIT(7)
++#define PPE_FLOW_CFG_IP4_TCP_FRAG_MASK                BIT(6)
++
++#define REG_PPE_IP_PROTO_CHK(_n)              (((_n) ? PPE2_BASE : PPE1_BASE) + 0x208)
++#define PPE_IP_PROTO_CHK_IPV4_MASK            GENMASK(15, 0)
++#define PPE_IP_PROTO_CHK_IPV6_MASK            GENMASK(31, 16)
++
++#define REG_PPE_TB_CFG(_n)                    (((_n) ? PPE2_BASE : PPE1_BASE) + 0x21c)
++#define PPE_SRAM_TB_NUM_ENTRY_MASK            GENMASK(26, 24)
++#define PPE_TB_CFG_KEEPALIVE_MASK             GENMASK(13, 12)
++#define PPE_TB_CFG_AGE_TCP_FIN_MASK           BIT(11)
++#define PPE_TB_CFG_AGE_UDP_MASK                       BIT(10)
++#define PPE_TB_CFG_AGE_TCP_MASK                       BIT(9)
++#define PPE_TB_CFG_AGE_UNBIND_MASK            BIT(8)
++#define PPE_TB_CFG_AGE_NON_L4_MASK            BIT(7)
++#define PPE_TB_CFG_AGE_PREBIND_MASK           BIT(6)
++#define PPE_TB_CFG_SEARCH_MISS_MASK           GENMASK(5, 4)
++#define PPE_TB_ENTRY_SIZE_MASK                        BIT(3)
++#define PPE_DRAM_TB_NUM_ENTRY_MASK            GENMASK(2, 0)
++
++#define REG_PPE_TB_BASE(_n)                   (((_n) ? PPE2_BASE : PPE1_BASE) + 0x220)
++
++#define REG_PPE_BIND_RATE(_n)                 (((_n) ? PPE2_BASE : PPE1_BASE) + 0x228)
++#define PPE_BIND_RATE_L2B_BIND_MASK           GENMASK(31, 16)
++#define PPE_BIND_RATE_BIND_MASK                       GENMASK(15, 0)
++
++#define REG_PPE_BIND_LIMIT0(_n)                       (((_n) ? PPE2_BASE : PPE1_BASE) + 0x22c)
++#define PPE_BIND_LIMIT0_HALF_MASK             GENMASK(29, 16)
++#define PPE_BIND_LIMIT0_QUARTER_MASK          GENMASK(13, 0)
++
++#define REG_PPE_BIND_LIMIT1(_n)                       (((_n) ? PPE2_BASE : PPE1_BASE) + 0x230)
++#define PPE_BIND_LIMIT1_NON_L4_MASK           GENMASK(23, 16)
++#define PPE_BIND_LIMIT1_FULL_MASK             GENMASK(13, 0)
++
++#define REG_PPE_BND_AGE0(_n)                  (((_n) ? PPE2_BASE : PPE1_BASE) + 0x23c)
++#define PPE_BIND_AGE0_DELTA_NON_L4            GENMASK(30, 16)
++#define PPE_BIND_AGE0_DELTA_UDP                       GENMASK(14, 0)
++
++#define REG_PPE_UNBIND_AGE(_n)                        (((_n) ? PPE2_BASE : PPE1_BASE) + 0x238)
++#define PPE_UNBIND_AGE_MIN_PACKETS_MASK               GENMASK(31, 16)
++#define PPE_UNBIND_AGE_DELTA_MASK             GENMASK(7, 0)
++
++#define REG_PPE_BND_AGE1(_n)                  (((_n) ? PPE2_BASE : PPE1_BASE) + 0x240)
++#define PPE_BIND_AGE1_DELTA_TCP_FIN           GENMASK(30, 16)
++#define PPE_BIND_AGE1_DELTA_TCP                       GENMASK(14, 0)
++
++#define REG_PPE_HASH_SEED(_n)                 (((_n) ? PPE2_BASE : PPE1_BASE) + 0x244)
++#define PPE_HASH_SEED                         0x12345678
++
++#define REG_PPE_DFT_CPORT0(_n)                        (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248)
++
++#define REG_PPE_DFT_CPORT1(_n)                        (((_n) ? PPE2_BASE : PPE1_BASE) + 0x24c)
++
++#define REG_PPE_TB_HASH_CFG(_n)                       (((_n) ? PPE2_BASE : PPE1_BASE) + 0x250)
++#define PPE_DRAM_HASH1_MODE_MASK              GENMASK(31, 28)
++#define PPE_DRAM_HASH1_EN_MASK                        BIT(24)
++#define PPE_DRAM_HASH0_MODE_MASK              GENMASK(23, 20)
++#define PPE_DRAM_TABLE_EN_MASK                        BIT(16)
++#define PPE_SRAM_HASH1_MODE_MASK              GENMASK(15, 12)
++#define PPE_SRAM_HASH1_EN_MASK                        BIT(8)
++#define PPE_SRAM_HASH0_MODE_MASK              GENMASK(7, 4)
++#define PPE_SRAM_TABLE_EN_MASK                        BIT(0)
++
++#define REG_PPE_MTU_BASE(_n)                  (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304)
++#define REG_PPE_MTU(_m, _n)                   (REG_PPE_MTU_BASE(_m) + ((_n) << 2))
++#define FP1_EGRESS_MTU_MASK                   GENMASK(29, 16)
++#define FP0_EGRESS_MTU_MASK                   GENMASK(13, 0)
++
++#define REG_PPE_RAM_CTRL(_n)                  (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c)
++#define PPE_SRAM_CTRL_ACK_MASK                        BIT(31)
++#define PPE_SRAM_CTRL_DUAL_SUCESS_MASK                BIT(30)
++#define PPE_SRAM_CTRL_ENTRY_MASK              GENMASK(23, 8)
++#define PPE_SRAM_WR_DUAL_DIRECTION_MASK               BIT(2)
++#define PPE_SRAM_CTRL_WR_MASK                 BIT(1)
++#define PPE_SRAM_CTRL_REQ_MASK                        BIT(0)
++
++#define REG_PPE_RAM_BASE(_n)                  (((_n) ? PPE2_BASE : PPE1_BASE) + 0x320)
++#define REG_PPE_RAM_ENTRY(_m, _n)             (REG_PPE_RAM_BASE(_m) + ((_n) << 2))
+ #define REG_FE_GDM_TX_OK_PKT_CNT_H(_n)                (GDM_BASE(_n) + 0x280)
+ #define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n)               (GDM_BASE(_n) + 0x284)
diff --git a/target/linux/airoha/patches-6.6/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch b/target/linux/airoha/patches-6.6/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch
new file mode 100644 (file)
index 0000000..224fe04
--- /dev/null
@@ -0,0 +1,210 @@
+From 9cd451d414f6e29f507a216fb3b19fa68c011f8c Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:22 +0100
+Subject: [PATCH 14/15] net: airoha: Add loopback support for GDM2
+
+Enable hw redirection for traffic received on GDM2 port to GDM{3,4}.
+This is required to apply Qdisc offloading (HTB or ETS) for traffic to
+and from GDM{3,4} port.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/airoha_eth.c  | 71 ++++++++++++++++++++++-
+ drivers/net/ethernet/airoha/airoha_eth.h  |  7 +++
+ drivers/net/ethernet/airoha/airoha_ppe.c  | 12 ++--
+ drivers/net/ethernet/airoha/airoha_regs.h | 29 +++++++++
+ 4 files changed, 111 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -1589,14 +1589,81 @@ static int airoha_dev_set_macaddr(struct
+       return 0;
+ }
++static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
++{
++      u32 pse_port = port->id == 3 ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4;
++      struct airoha_eth *eth = port->qdma->eth;
++      u32 chan = port->id == 3 ? 4 : 0;
++
++      /* Forward the traffic to the proper GDM port */
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port);
++      airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC);
++
++      /* Enable GDM2 loopback */
++      airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff);
++      airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff);
++      airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2),
++                    LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK,
++                    FIELD_PREP(LPBK_CHAN_MASK, chan) | LPBK_EN_MASK);
++      airoha_fe_rmw(eth, REG_GDM_LEN_CFG(2),
++                    GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
++                    FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
++                    FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
++
++      /* Disable VIP and IFC for GDM2 */
++      airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2));
++      airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2));
++
++      if (port->id == 3) {
++              /* FIXME: handle XSI_PCE1_PORT */
++              airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0),  0x5500);
++              airoha_fe_rmw(eth, REG_FE_WAN_PORT,
++                            WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
++                            FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT));
++              airoha_fe_rmw(eth,
++                            REG_SP_DFT_CPORT(HSGMII_LAN_PCIE0_SRCPORT >> 3),
++                            SP_CPORT_PCIE0_MASK,
++                            FIELD_PREP(SP_CPORT_PCIE0_MASK,
++                                       FE_PSE_PORT_CDM2));
++      } else {
++              /* FIXME: handle XSI_USB_PORT */
++              airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
++                            FC_ID_OF_SRC_PORT24_MASK,
++                            FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2));
++              airoha_fe_rmw(eth, REG_FE_WAN_PORT,
++                            WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
++                            FIELD_PREP(WAN0_MASK, HSGMII_LAN_ETH_SRCPORT));
++              airoha_fe_rmw(eth,
++                            REG_SP_DFT_CPORT(HSGMII_LAN_ETH_SRCPORT >> 3),
++                            SP_CPORT_ETH_MASK,
++                            FIELD_PREP(SP_CPORT_ETH_MASK, FE_PSE_PORT_CDM2));
++      }
++}
++
+ static int airoha_dev_init(struct net_device *dev)
+ {
+       struct airoha_gdm_port *port = netdev_priv(dev);
+       struct airoha_eth *eth = port->qdma->eth;
++      u32 pse_port;
+       airoha_set_macaddr(port, dev->dev_addr);
+-      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+-                                  FE_PSE_PORT_PPE1);
++
++      switch (port->id) {
++      case 3:
++      case 4:
++              /* If GDM2 is active we can't enable loopback */
++              if (!eth->ports[1])
++                      airhoha_set_gdm2_loopback(port);
++              fallthrough;
++      case 2:
++              pse_port = FE_PSE_PORT_PPE2;
++              break;
++      default:
++              pse_port = FE_PSE_PORT_PPE1;
++              break;
++      }
++
++      airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port);
+       return 0;
+ }
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -68,6 +68,13 @@ enum {
+ };
+ enum {
++      HSGMII_LAN_PCIE0_SRCPORT = 0x16,
++      HSGMII_LAN_PCIE1_SRCPORT,
++      HSGMII_LAN_ETH_SRCPORT,
++      HSGMII_LAN_USB_SRCPORT,
++};
++
++enum {
+       XSI_PCIE0_VIP_PORT_MASK = BIT(22),
+       XSI_PCIE1_VIP_PORT_MASK = BIT(23),
+       XSI_USB_VIP_PORT_MASK   = BIT(25),
+--- a/drivers/net/ethernet/airoha/airoha_ppe.c
++++ b/drivers/net/ethernet/airoha/airoha_ppe.c
+@@ -216,7 +216,8 @@ static int airoha_ppe_foe_entry_prepare(
+             AIROHA_FOE_IB1_BIND_TTL;
+       hwe->ib1 = val;
+-      val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f);
++      val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f) |
++            AIROHA_FOE_IB2_PSE_QOS;
+       if (dsa_port >= 0)
+               val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port);
+@@ -224,14 +225,13 @@ static int airoha_ppe_foe_entry_prepare(
+               struct airoha_gdm_port *port = netdev_priv(dev);
+               u8 pse_port;
+-              pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
++              if (dsa_port >= 0)
++                      pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
++              else
++                      pse_port = 2; /* uplink relies on GDM2 loopback */
+               val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port);
+       }
+-      /* FIXME: implement QoS support setting pse_port to 2 (loopback)
+-       * for uplink and setting qos bit in ib2
+-       */
+-
+       if (is_multicast_ether_addr(data->eth.h_dest))
+               val |= AIROHA_FOE_IB2_MULTICAST;
+--- a/drivers/net/ethernet/airoha/airoha_regs.h
++++ b/drivers/net/ethernet/airoha/airoha_regs.h
+@@ -38,6 +38,12 @@
+ #define FE_RST_CORE_MASK              BIT(0)
+ #define REG_FE_FOE_TS                 0x0010
++
++#define REG_FE_WAN_PORT                       0x0024
++#define WAN1_EN_MASK                  BIT(16)
++#define WAN1_MASK                     GENMASK(12, 8)
++#define WAN0_MASK                     GENMASK(4, 0)
++
+ #define REG_FE_WAN_MAC_H              0x0030
+ #define REG_FE_LAN_MAC_H              0x0040
+@@ -126,6 +132,7 @@
+ #define GDM_IP4_CKSUM                 BIT(22)
+ #define GDM_TCP_CKSUM                 BIT(21)
+ #define GDM_UDP_CKSUM                 BIT(20)
++#define GDM_STRIP_CRC                 BIT(16)
+ #define GDM_UCFQ_MASK                 GENMASK(15, 12)
+ #define GDM_BCFQ_MASK                 GENMASK(11, 8)
+ #define GDM_MCFQ_MASK                 GENMASK(7, 4)
+@@ -139,6 +146,16 @@
+ #define GDM_SHORT_LEN_MASK            GENMASK(13, 0)
+ #define GDM_LONG_LEN_MASK             GENMASK(29, 16)
++#define REG_GDM_LPBK_CFG(_n)          (GDM_BASE(_n) + 0x1c)
++#define LPBK_GAP_MASK                 GENMASK(31, 24)
++#define LPBK_LEN_MASK                 GENMASK(23, 10)
++#define LPBK_CHAN_MASK                        GENMASK(8, 4)
++#define LPBK_MODE_MASK                        GENMASK(3, 1)
++#define LPBK_EN_MASK                  BIT(0)
++
++#define REG_GDM_TXCHN_EN(_n)          (GDM_BASE(_n) + 0x24)
++#define REG_GDM_RXCHN_EN(_n)          (GDM_BASE(_n) + 0x28)
++
+ #define REG_FE_CPORT_CFG              (GDM1_BASE + 0x40)
+ #define FE_CPORT_PAD                  BIT(26)
+ #define FE_CPORT_PORT_XFC_MASK                BIT(25)
+@@ -351,6 +368,18 @@
+ #define REG_MC_VLAN_DATA              0x2108
++#define REG_SP_DFT_CPORT(_n)          (0x20e0 + ((_n) << 2))
++#define SP_CPORT_PCIE1_MASK           GENMASK(31, 28)
++#define SP_CPORT_PCIE0_MASK           GENMASK(27, 24)
++#define SP_CPORT_USB_MASK             GENMASK(7, 4)
++#define SP_CPORT_ETH_MASK             GENMASK(7, 4)
++
++#define REG_SRC_PORT_FC_MAP6          0x2298
++#define FC_ID_OF_SRC_PORT27_MASK      GENMASK(28, 24)
++#define FC_ID_OF_SRC_PORT26_MASK      GENMASK(20, 16)
++#define FC_ID_OF_SRC_PORT25_MASK      GENMASK(12, 8)
++#define FC_ID_OF_SRC_PORT24_MASK      GENMASK(4, 0)
++
+ #define REG_CDM5_RX_OQ1_DROP_CNT      0x29d4
+ /* QDMA */
diff --git a/target/linux/airoha/patches-6.6/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch b/target/linux/airoha/patches-6.6/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch
new file mode 100644 (file)
index 0000000..50d7fa1
--- /dev/null
@@ -0,0 +1,291 @@
+From 3fe15c640f3808c3faf235553c67c867d1389e5c Mon Sep 17 00:00:00 2001
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Fri, 28 Feb 2025 11:54:23 +0100
+Subject: [PATCH 15/15] net: airoha: Introduce PPE debugfs support
+
+Similar to PPE support for Mediatek devices, introduce PPE debugfs
+in order to dump binded and unbinded flows.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/ethernet/airoha/Makefile          |   1 +
+ drivers/net/ethernet/airoha/airoha_eth.h      |  14 ++
+ drivers/net/ethernet/airoha/airoha_ppe.c      |  17 +-
+ .../net/ethernet/airoha/airoha_ppe_debugfs.c  | 181 ++++++++++++++++++
+ 4 files changed, 209 insertions(+), 4 deletions(-)
+ create mode 100644 drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
+
+--- a/drivers/net/ethernet/airoha/Makefile
++++ b/drivers/net/ethernet/airoha/Makefile
+@@ -5,4 +5,5 @@
+ obj-$(CONFIG_NET_AIROHA) += airoha-eth.o
+ airoha-eth-y := airoha_eth.o airoha_ppe.o
++airoha-eth-$(CONFIG_DEBUG_FS) += airoha_ppe_debugfs.o
+ obj-$(CONFIG_NET_AIROHA_NPU) += airoha_npu.o
+--- a/drivers/net/ethernet/airoha/airoha_eth.h
++++ b/drivers/net/ethernet/airoha/airoha_eth.h
+@@ -7,6 +7,7 @@
+ #ifndef AIROHA_ETH_H
+ #define AIROHA_ETH_H
++#include <linux/debugfs.h>
+ #include <linux/etherdevice.h>
+ #include <linux/iopoll.h>
+ #include <linux/kernel.h>
+@@ -480,6 +481,8 @@ struct airoha_ppe {
+       struct hlist_head *foe_flow;
+       u16 foe_check_time[PPE_NUM_ENTRIES];
++
++      struct dentry *debugfs_dir;
+ };
+ struct airoha_eth {
+@@ -533,5 +536,16 @@ int airoha_ppe_setup_tc_block_cb(enum tc
+                                void *cb_priv);
+ int airoha_ppe_init(struct airoha_eth *eth);
+ void airoha_ppe_deinit(struct airoha_eth *eth);
++struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
++                                                u32 hash);
++
++#if CONFIG_DEBUG_FS
++int airoha_ppe_debugfs_init(struct airoha_ppe *ppe);
++#else
++static inline int airoha_ppe_debugfs_init(struct airoha_ppe *ppe)
++{
++      return 0;
++}
++#endif
+ #endif /* AIROHA_ETH_H */
+--- a/drivers/net/ethernet/airoha/airoha_ppe.c
++++ b/drivers/net/ethernet/airoha/airoha_ppe.c
+@@ -390,8 +390,8 @@ static u32 airoha_ppe_foe_get_entry_hash
+       return hash;
+ }
+-static struct airoha_foe_entry *
+-airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash)
++struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
++                                                u32 hash)
+ {
+       if (hash < PPE_SRAM_NUM_ENTRIES) {
+               u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry);
+@@ -861,7 +861,7 @@ void airoha_ppe_check_skb(struct airoha_
+ int airoha_ppe_init(struct airoha_eth *eth)
+ {
+       struct airoha_ppe *ppe;
+-      int foe_size;
++      int foe_size, err;
+       ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL);
+       if (!ppe)
+@@ -882,7 +882,15 @@ int airoha_ppe_init(struct airoha_eth *e
+       if (!ppe->foe_flow)
+               return -ENOMEM;
+-      return rhashtable_init(&eth->flow_table, &airoha_flow_table_params);
++      err = rhashtable_init(&eth->flow_table, &airoha_flow_table_params);
++      if (err)
++              return err;
++
++      err = airoha_ppe_debugfs_init(ppe);
++      if (err)
++              rhashtable_destroy(&eth->flow_table);
++
++      return err;
+ }
+ void airoha_ppe_deinit(struct airoha_eth *eth)
+@@ -898,4 +906,5 @@ void airoha_ppe_deinit(struct airoha_eth
+       rcu_read_unlock();
+       rhashtable_destroy(&eth->flow_table);
++      debugfs_remove(eth->ppe->debugfs_dir);
+ }
+--- /dev/null
++++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
+@@ -0,0 +1,181 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2025 AIROHA Inc
++ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
++ */
++
++#include "airoha_eth.h"
++
++static void airoha_debugfs_ppe_print_tuple(struct seq_file *m,
++                                         void *src_addr, void *dest_addr,
++                                         u16 *src_port, u16 *dest_port,
++                                         bool ipv6)
++{
++      __be32 n_addr[IPV6_ADDR_WORDS];
++
++      if (ipv6) {
++              ipv6_addr_cpu_to_be32(n_addr, src_addr);
++              seq_printf(m, "%pI6", n_addr);
++      } else {
++              seq_printf(m, "%pI4h", src_addr);
++      }
++      if (src_port)
++              seq_printf(m, ":%d", *src_port);
++
++      seq_puts(m, "->");
++
++      if (ipv6) {
++              ipv6_addr_cpu_to_be32(n_addr, dest_addr);
++              seq_printf(m, "%pI6", n_addr);
++      } else {
++              seq_printf(m, "%pI4h", dest_addr);
++      }
++      if (dest_port)
++              seq_printf(m, ":%d", *dest_port);
++}
++
++static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
++                                     bool bind)
++{
++      static const char *const ppe_type_str[] = {
++              [PPE_PKT_TYPE_IPV4_HNAPT] = "IPv4 5T",
++              [PPE_PKT_TYPE_IPV4_ROUTE] = "IPv4 3T",
++              [PPE_PKT_TYPE_BRIDGE] = "L2B",
++              [PPE_PKT_TYPE_IPV4_DSLITE] = "DS-LITE",
++              [PPE_PKT_TYPE_IPV6_ROUTE_3T] = "IPv6 3T",
++              [PPE_PKT_TYPE_IPV6_ROUTE_5T] = "IPv6 5T",
++              [PPE_PKT_TYPE_IPV6_6RD] = "6RD",
++      };
++      static const char *const ppe_state_str[] = {
++              [AIROHA_FOE_STATE_INVALID] = "INV",
++              [AIROHA_FOE_STATE_UNBIND] = "UNB",
++              [AIROHA_FOE_STATE_BIND] = "BND",
++              [AIROHA_FOE_STATE_FIN] = "FIN",
++      };
++      struct airoha_ppe *ppe = m->private;
++      int i;
++
++      for (i = 0; i < PPE_NUM_ENTRIES; i++) {
++              const char *state_str, *type_str = "UNKNOWN";
++              void *src_addr = NULL, *dest_addr = NULL;
++              u16 *src_port = NULL, *dest_port = NULL;
++              struct airoha_foe_mac_info_common *l2;
++              unsigned char h_source[ETH_ALEN] = {};
++              unsigned char h_dest[ETH_ALEN];
++              struct airoha_foe_entry *hwe;
++              u32 type, state, ib2, data;
++              bool ipv6 = false;
++
++              hwe = airoha_ppe_foe_get_entry(ppe, i);
++              if (!hwe)
++                      continue;
++
++              state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1);
++              if (!state)
++                      continue;
++
++              if (bind && state != AIROHA_FOE_STATE_BIND)
++                      continue;
++
++              state_str = ppe_state_str[state % ARRAY_SIZE(ppe_state_str)];
++              type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
++              if (type < ARRAY_SIZE(ppe_type_str) && ppe_type_str[type])
++                      type_str = ppe_type_str[type];
++
++              seq_printf(m, "%05x %s %7s", i, state_str, type_str);
++
++              switch (type) {
++              case PPE_PKT_TYPE_IPV4_HNAPT:
++              case PPE_PKT_TYPE_IPV4_DSLITE:
++                      src_port = &hwe->ipv4.orig_tuple.src_port;
++                      dest_port = &hwe->ipv4.orig_tuple.dest_port;
++                      fallthrough;
++              case PPE_PKT_TYPE_IPV4_ROUTE:
++                      src_addr = &hwe->ipv4.orig_tuple.src_ip;
++                      dest_addr = &hwe->ipv4.orig_tuple.dest_ip;
++                      break;
++              case PPE_PKT_TYPE_IPV6_ROUTE_5T:
++                      src_port = &hwe->ipv6.src_port;
++                      dest_port = &hwe->ipv6.dest_port;
++                      fallthrough;
++              case PPE_PKT_TYPE_IPV6_ROUTE_3T:
++              case PPE_PKT_TYPE_IPV6_6RD:
++                      src_addr = &hwe->ipv6.src_ip;
++                      dest_addr = &hwe->ipv6.dest_ip;
++                      ipv6 = true;
++                      break;
++              default:
++                      break;
++              }
++
++              if (src_addr && dest_addr) {
++                      seq_puts(m, " orig=");
++                      airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr,
++                                                     src_port, dest_port, ipv6);
++              }
++
++              switch (type) {
++              case PPE_PKT_TYPE_IPV4_HNAPT:
++              case PPE_PKT_TYPE_IPV4_DSLITE:
++                      src_port = &hwe->ipv4.new_tuple.src_port;
++                      dest_port = &hwe->ipv4.new_tuple.dest_port;
++                      fallthrough;
++              case PPE_PKT_TYPE_IPV4_ROUTE:
++                      src_addr = &hwe->ipv4.new_tuple.src_ip;
++                      dest_addr = &hwe->ipv4.new_tuple.dest_ip;
++                      seq_puts(m, " new=");
++                      airoha_debugfs_ppe_print_tuple(m, src_addr, dest_addr,
++                                                     src_port, dest_port,
++                                                     ipv6);
++                      break;
++              default:
++                      break;
++              }
++
++              if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) {
++                      data = hwe->ipv6.data;
++                      ib2 = hwe->ipv6.ib2;
++                      l2 = &hwe->ipv6.l2;
++              } else {
++                      data = hwe->ipv4.data;
++                      ib2 = hwe->ipv4.ib2;
++                      l2 = &hwe->ipv4.l2.common;
++                      *((__be16 *)&h_source[4]) =
++                              cpu_to_be16(hwe->ipv4.l2.src_mac_lo);
++              }
++
++              *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi);
++              *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo);
++              *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi);
++
++              seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x"
++                            " vlan=%d,%d ib1=%08x ib2=%08x\n",
++                         h_source, h_dest, l2->etype, data,
++                         l2->vlan1, l2->vlan2, hwe->ib1, ib2);
++      }
++
++      return 0;
++}
++
++static int airoha_ppe_debugfs_foe_all_show(struct seq_file *m, void *private)
++{
++      return airoha_ppe_debugfs_foe_show(m, private, false);
++}
++DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_all);
++
++static int airoha_ppe_debugfs_foe_bind_show(struct seq_file *m, void *private)
++{
++      return airoha_ppe_debugfs_foe_show(m, private, true);
++}
++DEFINE_SHOW_ATTRIBUTE(airoha_ppe_debugfs_foe_bind);
++
++int airoha_ppe_debugfs_init(struct airoha_ppe *ppe)
++{
++      ppe->debugfs_dir = debugfs_create_dir("ppe", NULL);
++      debugfs_create_file("entries", 0444, ppe->debugfs_dir, ppe,
++                          &airoha_ppe_debugfs_foe_all_fops);
++      debugfs_create_file("bind", 0444, ppe->debugfs_dir, ppe,
++                          &airoha_ppe_debugfs_foe_bind_fops);
++
++      return 0;
++}
index e98fdad065e59e7734180450b9c1a93cbac6dca0..21896d7fa998a5555dba8e9efe93edfb9d4f44cc 100644 (file)
@@ -84,9 +84,9 @@ change-id: 20250107-airoha-ets-fix-chan-e35ccac76d64
 
 Best regards,
 
---- a/drivers/net/ethernet/mediatek/airoha_eth.c
-+++ b/drivers/net/ethernet/mediatek/airoha_eth.c
-@@ -2846,11 +2846,14 @@ static int airoha_qdma_get_tx_ets_stats(
+--- a/drivers/net/ethernet/airoha/airoha_eth.c
++++ b/drivers/net/ethernet/airoha/airoha_eth.c
+@@ -2064,11 +2064,14 @@ static int airoha_qdma_get_tx_ets_stats(
  static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port,
                                     struct tc_ets_qopt_offload *opt)
  {