From: Christian Marangi Date: Tue, 4 Mar 2025 22:54:31 +0000 (+0100) Subject: airoha: backport upstream patch for Flow Offload support for AN7581 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F18166%2Fhead;p=thirdparty%2Fopenwrt.git airoha: backport upstream patch for Flow Offload support for AN7581 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 # tested on Quantum W1700k Tested-by: Andrew LaMarche Link: https://github.com/openwrt/openwrt/pull/18166 Signed-off-by: Christian Marangi --- diff --git a/target/linux/airoha/an7581/config-6.6 b/target/linux/airoha/an7581/config-6.6 index 00e09ff83d4..600f1e45b47 100644 --- a/target/linux/airoha/an7581/config-6.6 +++ b/target/linux/airoha/an7581/config-6.6 @@ -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 diff --git a/target/linux/airoha/patches-6.6/045-v6.14-net-airoha-Fix-wrong-GDM4-register-definition.patch b/target/linux/airoha/patches-6.6/045-v6.14-net-airoha-Fix-wrong-GDM4-register-definition.patch index e14dd3e6d5f..f17242a6c5c 100644 --- a/target/linux/airoha/patches-6.6/045-v6.14-net-airoha-Fix-wrong-GDM4-register-definition.patch +++ b/target/linux/airoha/patches-6.6/045-v6.14-net-airoha-Fix-wrong-GDM4-register-definition.patch @@ -23,8 +23,6 @@ Signed-off-by: Jakub Kicinski 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 - diff --git a/target/linux/airoha/patches-6.6/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch b/target/linux/airoha/patches-6.6/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch index b381612c39c..9c2443ce698 100644 --- a/target/linux/airoha/patches-6.6/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch +++ b/target/linux/airoha/patches-6.6/046-v6.15-net-airoha-Fix-TSO-support-for-header-cloned-skbs.patch @@ -22,11 +22,9 @@ Signed-off-by: Jakub Kicinski 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 - diff --git a/target/linux/airoha/patches-6.6/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch b/target/linux/airoha/patches-6.6/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch index 4a53e64aa30..f20e2034c22 100644 --- a/target/linux/airoha/patches-6.6/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch +++ b/target/linux/airoha/patches-6.6/047-v6.13-net-airoha-Reset-BQL-stopping-the-netdevice.patch @@ -14,11 +14,9 @@ Signed-off-by: Andrew Lunn 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 index 00000000000..255bbd2ed9d --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-01-v6.15-net-airoha-Move-airoha_eth-driver-in-a-dedicated-fol.patch @@ -0,0 +1,6825 @@ +From fb3dda82fd38ca42140f29b3082324dcdc128293 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 - ð->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, ð->qdma[i]); ++ if (err) ++ return err; ++ } ++ ++ set_bit(DEV_STATE_INITIALIZED, ð->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 = ð->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(ð->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(ð->qdma[i]); ++error_hw_cleanup: ++ for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) ++ airoha_hw_cleanup(ð->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(ð->qdma[i]); ++ airoha_hw_cleanup(ð->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 "); ++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 +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#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 - ð->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, ð->qdma[i]); +- if (err) +- return err; +- } +- +- set_bit(DEV_STATE_INITIALIZED, ð->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 = ð->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(ð->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(ð->qdma[i]); +-error_hw_cleanup: +- for (i = 0; i < ARRAY_SIZE(eth->qdma); i++) +- airoha_hw_cleanup(ð->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(ð->qdma[i]); +- airoha_hw_cleanup(ð->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 "); +-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 index 00000000000..85391281a25 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-02-v6.15-net-airoha-Move-definitions-in-airoha_eth.h.patch @@ -0,0 +1,538 @@ +From b38f4ff0ceacd6ce8d333a8dc90f405a040968d3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 + */ +-#include +-#include +-#include +-#include + #include + #include + #include +-#include + #include + #include + #include +@@ -18,35 +13,7 @@ + #include + #include + +-#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 ++ */ ++ ++#ifndef AIROHA_ETH_H ++#define AIROHA_ETH_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 index 00000000000..bf24638ec91 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-03-v6.15-net-airoha-Move-reg-write-utility-routines-in-airoha.patch @@ -0,0 +1,101 @@ +From e0758a8694fbaffdc72940774db295585e951119 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..3b2b8bfe5dc --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-04-v6.15-net-airoha-Move-register-definitions-in-airoha_regs..patch @@ -0,0 +1,1361 @@ +From ec663d9a82bf4d16721f6b1fc29df4892ba6c088 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 + #include + ++#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 ++ */ ++ ++#ifndef AIROHA_REGS_H ++#define AIROHA_REGS_H ++ ++#include ++ ++/* 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 index 00000000000..61e889f3d2a --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-05-v6.15-net-airoha-Move-DSA-tag-in-DMA-descriptor.patch @@ -0,0 +1,287 @@ +From af3cf757d5c99011b9b94ea8d78aeaccc0153fdc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 index 00000000000..fdf12c250c7 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-06-v6.15-net-dsa-mt7530-Enable-Rx-sptag-for-EN7581-SoC.patch @@ -0,0 +1,46 @@ +From ab667db1e6014634c6607ebdddc16c1b8394a935 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..af4fa78c909 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-07-v6.15-net-airoha-Enable-support-for-multiple-net_devices.patch @@ -0,0 +1,144 @@ +From 80369686737fe07c233a1152da0b84372dabdcd6 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Christian Marangi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + 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(ð->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 + #include + +-#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 index 00000000000..8bc0f856577 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-08-v6.15-net-airoha-Move-REG_GDM_FWD_CFG-initialization-in-ai.patch @@ -0,0 +1,77 @@ +From 67fde5d58cd43d129a979e918ec9cd5d2e2fbcfb Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..11f879de1c4 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-09-v6.15-net-airoha-Rename-airoha_set_gdm_port_fwd_cfg-in-air.patch @@ -0,0 +1,120 @@ +From c28b8375f6d02ef3b5e8c51234cc3f6d47d9fb7f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..41c56223040 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-12-v6.15-net-airoha-Introduce-Airoha-NPU-support.patch @@ -0,0 +1,627 @@ +From 23290c7bc190def4e1ca61610992d9b7c32e33f3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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, ®map_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 "); ++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 ++ */ ++ ++#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 index 00000000000..225165fca5a --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-13-v6.15-net-airoha-Introduce-flowtable-offload-support.patch @@ -0,0 +1,1481 @@ +From 00a7678310fe3d3f408513e55d9a0b67f0db380f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Paolo Abeni +--- + 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 + #include + #include +-#include + #include + #include + #include +@@ -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, ð->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 + #include + #include ++#include + + #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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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(ð->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(ð->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(ð->flow_table, &f->cookie, ++ airoha_flow_table_params); ++ if (!e) ++ return -ENOENT; ++ ++ airoha_ppe_foe_flow_remove_entry(eth->ppe, e); ++ rhashtable_remove_fast(ð->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(ð->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(ð->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 index 00000000000..224fe04f447 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-14-v6.15-net-airoha-Add-loopback-support-for-GDM2.patch @@ -0,0 +1,210 @@ +From 9cd451d414f6e29f507a216fb3b19fa68c011f8c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 index 00000000000..50d7fa12668 --- /dev/null +++ b/target/linux/airoha/patches-6.6/048-15-v6.15-net-airoha-Introduce-PPE-debugfs-support.patch @@ -0,0 +1,291 @@ +From 3fe15c640f3808c3faf235553c67c867d1389e5c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +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 +Signed-off-by: Paolo Abeni +--- + 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 + #include + #include + #include +@@ -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(ð->flow_table, &airoha_flow_table_params); ++ err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); ++ if (err) ++ return err; ++ ++ err = airoha_ppe_debugfs_init(ppe); ++ if (err) ++ rhashtable_destroy(ð->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(ð->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 ++ */ ++ ++#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; ++} diff --git a/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch b/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch index e98fdad065e..21896d7fa99 100644 --- a/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch +++ b/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch @@ -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) {