]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
qualcommax: add PPE driver
authorJohn Crispin <john@phrozen.org>
Wed, 11 Mar 2026 18:04:53 +0000 (19:04 +0100)
committerRobert Marko <robimarko@gmail.com>
Sat, 11 Jul 2026 20:26:43 +0000 (22:26 +0200)
PPE switch driver for Qualcomm IPQ platforms, depends on EDMA and
UNIPHY PCS.

Signed-off-by: John Crispin <john@phrozen.org>
[ rework Makefile for external repository, dependency ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
[ bring PPE in-tree ]
Link: https://github.com/openwrt/openwrt/pull/22381
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/qualcommax/config-6.12
target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h [new file with mode: 0644]
target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c [new file with mode: 0644]
target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c [new file with mode: 0644]
target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_vlan.c [new file with mode: 0644]
target/linux/qualcommax/files/include/dt-bindings/net/qualcomm,ipq-ppe.yaml [new file with mode: 0644]
target/linux/qualcommax/patches-6.12/0952-net-ethernet-qca-add-ppe.patch [new file with mode: 0644]

index 43f4e25744e5043ba9acbf3ec4e89e826ecca00f..4ea6486e85e7f9f4de1aa5b2d564c349ddcf5dbe 100644 (file)
@@ -402,6 +402,7 @@ CONFIG_QCA807X_PHY=y
 CONFIG_QCA808X_PHY=y
 # CONFIG_QCM_DISPCC_2290 is not set
 # CONFIG_QCM_GCC_2290 is not set
+# CONFIG_QCOM_80211AX_PPE is not set
 # CONFIG_QCOM_A53PLL is not set
 # CONFIG_QCOM_AOSS_QMP is not set
 CONFIG_QCOM_APCS_IPC=y
diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h
new file mode 100644 (file)
index 0000000..39329df
--- /dev/null
@@ -0,0 +1,545 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later OR MIT */
+
+#ifndef __QCA_PPE_H
+#define __QCA_PPE_H
+
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <net/dsa.h>
+
+#define QCA_PPE_MAX_PORTS      8
+#define QCA_PPE_CPU_PORT       0
+#define QCA_PPE_MAX_BRIDGES    8
+
+
+/* --- Global --- */
+#define PPE_PORT_MUX_CTRL              0x10
+
+/* CPPE (IPQ60xx) PORT_MUX_CTRL bit layout */
+#define   CPPE_PORT3_PCS_SEL           GENMASK(1, 0)
+#define   CPPE_PORT5_PCS_SEL           GENMASK(5, 4)
+#define   CPPE_PORT5_GMAC_SEL          BIT(6)
+#define   CPPE_PCS0_CH4_SEL            BIT(7)
+#define   CPPE_PORT3_PCS0_CH4          1
+#define   CPPE_PORT5_PCS1_CH0          1
+
+/* HPPE (IPQ807x) PORT_MUX_CTRL bit layout */
+#define   HPPE_PORT4_PCS_SEL           BIT(0)
+#define   HPPE_PORT5_PCS_SEL           GENMASK(2, 1)
+#define   HPPE_PORT5_GMAC_SEL          BIT(3)
+#define   HPPE_PORT6_PCS_SEL           BIT(4)
+#define   HPPE_PORT6_GMAC_SEL          BIT(5)
+#define   HPPE_PORT4_PCS0              1
+#define   HPPE_PORT5_PCS0              1
+#define   HPPE_PORT5_PCS1              2
+#define   HPPE_PORT6_PCS2              1
+#define   HPPE_PORT5_GMAC_SEL_GMAC     1
+#define   HPPE_PORT6_GMAC_SEL_GMAC     1
+
+/* --- MAC CSR (base 0x001000) --- */
+#define PPE_MAC_CSR_BASE               0x001000
+
+#define PPE_GMAC_ENABLE(gmac)          (PPE_MAC_CSR_BASE + (gmac) * 0x200)
+#define   PPE_MAC_ENABLE_RXMAC_EN      BIT(0)
+#define   PPE_MAC_ENABLE_TXMAC_EN      BIT(1)
+#define   PPE_MAC_ENABLE_DUPLEX                BIT(4)
+#define   PPE_MAC_ENABLE_RX_FLOW_EN    BIT(5)
+#define   PPE_MAC_ENABLE_TX_FLOW_EN    BIT(6)
+
+#define PPE_LPBK_ENABLE(gmac)          (PPE_MAC_CSR_BASE + (gmac) * 0x200)
+#define   PPE_LPBK_EN                  BIT(0)
+#define   PPE_LPBK_CRC_STRIP_EN        BIT(3)
+
+#define PPE_GMAC_SPEED(gmac)           (PPE_MAC_CSR_BASE + (gmac) * 0x200 + 0x4)
+#define   PPE_GMAC_SPEED_MASK          GENMASK(1, 0)
+
+#define PPE_LPBK_PPS_CTRL(gmac)        (PPE_MAC_CSR_BASE + (gmac) * 0x200 + 0x0c)
+#define   PPE_LPBK_PPS_THRESHOLD       GENMASK(8, 0)
+
+#define PPE_GMAC_CTRL2(gmac)           (PPE_MAC_CSR_BASE + (gmac) * 0x200 + 0x18)
+#define   PPE_GMAC_CTRL2_MAXFR         GENMASK(21, 8)
+#define   PPE_GMAC_CTRL2_CRS_SEL       BIT(6)
+#define   PPE_GMAC_CTRL2_TX_THD        GENMASK(27, 24)
+
+#define PPE_GMAC_DBG_CTRL(gmac)        (PPE_MAC_CSR_BASE + (gmac) * 0x200 + 0x1c)
+#define   PPE_GMAC_DBG_CTRL_HIHG_IPG   GENMASK(15, 8)
+
+#define PPE_GMAC_JUMBO_SIZE(gmac)      (PPE_MAC_CSR_BASE + (gmac) * 0x200 + 0x30)
+
+#define PPE_GMAC_MIB_CTRL(gmac)        (PPE_MAC_CSR_BASE + (gmac) * 0x200 + 0x34)
+#define   PPE_MIB_EN                   BIT(0)
+#define   PPE_MIB_RD_CLR               BIT(2)
+
+#define PPE_GMAC_MIB(gmac, off)        (PPE_MAC_CSR_BASE + (gmac) * 0x200 + (off))
+#define   PPE_MIB_RXBROAD              0x40
+#define   PPE_MIB_RXPAUSE              0x44
+#define   PPE_MIB_RXMULTI              0x48
+#define   PPE_MIB_RXFCSERR             0x4c
+#define   PPE_MIB_RXALIGNERR           0x50
+#define   PPE_MIB_RXRUNT               0x54
+#define   PPE_MIB_RXFRAG               0x58
+#define   PPE_MIB_RXJUMBOFCSERR        0x5c
+#define   PPE_MIB_RXJUMBOALIGNERR      0x60
+#define   PPE_MIB_RXPKT64              0x64
+#define   PPE_MIB_RXPKT65TO127         0x68
+#define   PPE_MIB_RXPKT128TO255        0x6c
+#define   PPE_MIB_RXPKT256TO511        0x70
+#define   PPE_MIB_RXPKT512TO1023       0x74
+#define   PPE_MIB_RXPKT1024TO1518      0x78
+#define   PPE_MIB_RXPKT1519TOX         0x7c
+#define   PPE_MIB_RXTOOLONG            0x80
+#define   PPE_MIB_RXGOODBYTE_L         0x84
+#define   PPE_MIB_RXGOODBYTE_H         0x88
+#define   PPE_MIB_RXBADBYTE_L          0x8c
+#define   PPE_MIB_RXBADBYTE_H          0x90
+#define   PPE_MIB_RXUNI                0x94
+#define   PPE_MIB_TXBROAD              0xa0
+#define   PPE_MIB_TXPAUSE              0xa4
+#define   PPE_MIB_TXMULTI              0xa8
+#define   PPE_MIB_TXUNDERRUN           0xac
+#define   PPE_MIB_TXPKT64              0xb0
+#define   PPE_MIB_TXPKT65TO127         0xb4
+#define   PPE_MIB_TXPKT128TO255        0xb8
+#define   PPE_MIB_TXPKT256TO511        0xbc
+#define   PPE_MIB_TXPKT512TO1023       0xc0
+#define   PPE_MIB_TXPKT1024TO1518      0xc4
+#define   PPE_MIB_TXPKT1519TOX         0xc8
+#define   PPE_MIB_TXBYTE_L             0xcc
+#define   PPE_MIB_TXBYTE_H             0xd0
+#define   PPE_MIB_TXCOLLISIONS         0xd4
+#define   PPE_MIB_TXABORTCOL           0xd8
+#define   PPE_MIB_TXMULTICOL           0xdc
+#define   PPE_MIB_TXSINGLECOL          0xe0
+#define   PPE_MIB_TXEXCESSIVEDEFER     0xe4
+#define   PPE_MIB_TXDEFER              0xe8
+#define   PPE_MIB_TXLATECOL            0xec
+#define   PPE_MIB_TXUNI                0xf0
+
+/* --- XGMAC (base 0x003000) --- */
+#define PPE_MAC_XGMAC_CSR_BASE         0x003000
+
+#define PPE_XGMAC_TX_CONF(xgmac)       (PPE_MAC_XGMAC_CSR_BASE + (xgmac) * 0x4000)
+#define   PPE_XGMAC_TX_ENABLE          BIT(0)
+#define   PPE_XGMAC_JABBER_DISABLE     BIT(16) /* Called JD */
+#define   PPE_XGMAC_SPEED_SELECT       GENMASK(30, 29)
+#define     PPE_XGMAC_SPEED_SELECT_10000 FIELD_PREP_CONST(PPE_XGMAC_SPEED_SELECT, 0x0)
+#define     PPE_XGMAC_SPEED_SELECT_5000        FIELD_PREP_CONST(PPE_XGMAC_SPEED_SELECT, 0x1)
+#define     PPE_XGMAC_SPEED_SELECT_2500        FIELD_PREP_CONST(PPE_XGMAC_SPEED_SELECT, 0x2)
+#define     PPE_XGMAC_SPEED_SELECT_1000        FIELD_PREP_CONST(PPE_XGMAC_SPEED_SELECT, 0x3)
+#define   PPE_XGMAC_USXGMII_SELECT     BIT(31)
+
+#define PPE_XGMAC_RX_CONF(xgmac)       (PPE_MAC_XGMAC_CSR_BASE + (xgmac) * 0x4000 + 0x4)
+#define   PPE_XGMAC_RX_ENABLE          BIT(0)
+#define   PPE_XGMAC_AUTO_CRC_STRIP     BIT(1) /* Called ACS */
+#define   PPE_XGMAC_CRC_STRIP_TYPE     BIT(2) /* Called CST */
+#define   PPE_XGMAC_GMII_MPLS_LAYER_CK BIT(6) /* Called GMPSLCE */
+#define   PPE_XGMAC_WATCHDOG_DISABLE   BIT(7) /* Called WD */
+
+#define PPE_XGMAC_PACKET_FILTER(xgmac) (PPE_MAC_XGMAC_CSR_BASE + (xgmac) * 0x4000 + 0x8)
+#define   PPE_XGMAC_PROMISCUOUS                BIT(0) /* Called PR */
+#define   PPE_XGMAC_PASS_CONTROL_FRAME GENMASK(7, 6) /* Called PCF */
+#define   PPE_XGMAC_RATE_ADAPTATION    BIT(31) /* Called RA */
+
+#define PPE_XGMAC_WATCHDOG_TIMEOUT(xgmac) (PPE_MAC_XGMAC_CSR_BASE + (xgmac) * 0x4000 + 0xc)
+#define   PPE_XGMAC_WATCHDOG_THRESHOLD GENMASK(3, 0)
+#define   PPE_XGMAC_WATCHDOG_ENABLE    BIT(8)
+
+#define PPE_XGMAC_TX_FLOW_CTRL(xgmac)  (PPE_MAC_XGMAC_CSR_BASE + (xgmac) * 0x4000 + 0x70)
+#define   PPE_XGMAC_TX_FLOW_ENABLE     BIT(1)
+#define   PPE_XGMAC_PAUSE_TIME         GENMASK(31, 16)
+
+#define PPE_XGMAC_RX_FLOW_CTRL(xgmac)  (PPE_MAC_XGMAC_CSR_BASE + (xgmac) * 0x4000 + 0x90)
+#define   PPE_XGMAC_RX_FLOW_ENABLE     BIT(0)
+
+/* --- PRX (base 0x00b000) --- */
+#define PPE_PRX_BASE                   0x00b000
+
+#define PPE_PRX_TDM_CTRL               (PPE_PRX_BASE + 0x0)
+#define   PPE_TDM_DEPTH                GENMASK(7, 0)
+#define   PPE_TDM_EN                   BIT(31)
+
+#define PPE_PRX_TDM_CFG(i)             (PPE_PRX_BASE + 0x1000 + (i) * 0x10)
+#define   PPE_TDM_PORT_NUM             GENMASK(3, 0)
+#define   PPE_TDM_DIR                  BIT(4)
+#define   PPE_TDM_VALID                BIT(5)
+
+#define PPE_PRX_MRU_MTU_W1(p)          (PPE_PRX_BASE + 0x3000 + (p) * 0x10 + 0x4)
+#define   PPE_QOS_PCP_GRP              BIT(4)
+#define   PPE_QOS_DSCP_GRP             BIT(5)
+#define   PPE_QOS_PREHEADER_PREC       GENMASK(10, 8)
+#define   PPE_QOS_PCP_PREC             GENMASK(13, 11)
+#define   PPE_QOS_DSCP_PREC            GENMASK(16, 14)
+#define   PPE_QOS_FLOW_PREC            GENMASK(19, 17)
+#define   PPE_QOS_ACL_PREC             GENMASK(22, 20)
+
+/* --- Ingress VLAN (base 0x00f000) --- */
+#define PPE_IVLAN_BASE                 0x00f000
+
+#define PPE_PORT_DEF_VID(port)         (PPE_IVLAN_BASE + 0x10 + (port) * 0x4)
+#define   PPE_PORT_DEF_CVID            GENMASK(27, 16)
+#define   PPE_PORT_DEF_CVID_EN         BIT(28)
+
+#define PPE_PORT_VLAN_CFG(port)                (PPE_IVLAN_BASE + 0x50 + (port) * 0x4)
+#define   PPE_VLAN_XLT_MISS_FWD        GENMASK(6, 5)
+
+#define PPE_XLT_RULE_TBL(idx)          (PPE_IVLAN_BASE + 0x2000 + (idx) * 0x10)
+#define   PPE_XLT_VALID                        BIT(0)
+#define   PPE_XLT_PORT_BMP             GENMASK(8, 1)
+#define   PPE_XLT_CKEY_FMT_0           BIT(31)
+
+#define PPE_XLT_RULE_W1(idx)           (PPE_IVLAN_BASE + 0x2000 + (idx) * 0x10 + 0x4)
+#define   PPE_XLT_CKEY_FMT_1           GENMASK(1, 0)
+#define   PPE_XLT_CKEY_VID_INCL                BIT(2)
+#define   PPE_XLT_CKEY_VID             GENMASK(14, 3)
+
+#define PPE_XLT_ACTION_TBL(idx)                (PPE_IVLAN_BASE + 0x4000 + (idx) * 0x10)
+#define   PPE_XLT_CVID_CMD             GENMASK(16, 15)
+
+#define PPE_XLT_ACTION_W1(idx)         (PPE_IVLAN_BASE + 0x4000 + (idx) * 0x10 + 0x4)
+#define   PPE_XLT_VSI_CMD              BIT(11)
+#define   PPE_XLT_VSI                  GENMASK(16, 12)
+
+/* --- PTX (base 0x020000) --- */
+#define PPE_PTX_BASE                   0x020000
+
+#define PPE_EG_VSI_TAG(vsi)            (PPE_PTX_BASE + (vsi) * 0x4)
+#define   PPE_EG_VSI_TAG_UNMODIFIED    0xaaaa
+
+#define PPE_EG_BRIDGE_CONFIG           (PPE_PTX_BASE + 0x6000)
+#define   PPE_EG_L2_EDIT_EN            BIT(1)
+#define   PPE_EG_QUEUE_CNT_EN          BIT(2)
+
+#define PPE_PORT_EG_VLAN(port)         (PPE_PTX_BASE + 0x420 + (port) * 0x4)
+#define   PPE_PORT_EG_VLAN_CTAG_MODE   GENMASK(2, 1)
+#define   PPE_PORT_EG_VLAN_STAG_MODE   GENMASK(4, 3)
+#define   PPE_PORT_EG_VSI_TAG_EN       BIT(5)
+#define   PPE_PORT_EG_VLAN_TX_CNT_EN   BIT(8)
+
+#define PPE_EG_UNTOUCHED               3
+
+/* --- L2 (base 0x060000) --- */
+#define PPE_L2_BASE                    0x060000
+
+#define PPE_FDB_OP                     (PPE_L2_BASE + 0x8)
+#define PPE_FDB_RD_OP                  (PPE_L2_BASE + 0x10)
+#define PPE_FDB_OP_RSLT                        (PPE_L2_BASE + 0x20)
+#define PPE_FDB_RD_OP_RSLT             (PPE_L2_BASE + 0x30)
+
+#define PPE_AGE_TIMER                  (PPE_L2_BASE + 0x34)
+#define   PPE_AGE_TIMER_MASK           GENMASK(19, 0)
+
+#define PPE_L2_GLOBAL_CONF             (PPE_L2_BASE + 0x38)
+#define   PPE_L2_LRN_EN                BIT(6)
+#define   PPE_L2_AGE_EN                BIT(7)
+
+#define PPE_CST_STATE(port)            (PPE_L2_BASE + 0x100 + (port) * 0x4)
+#define   PPE_STP_DISABLED             0
+#define   PPE_STP_BLOCKING             1
+#define   PPE_STP_LEARNING             2
+#define   PPE_STP_FORWARDING           3
+#define   PPE_STP_STATE_MASK           GENMASK(1, 0)
+
+#define PPE_FDB_RD_RSLT_DATA0          (PPE_L2_BASE + 0x200)
+#define PPE_FDB_RD_RSLT_DATA1          (PPE_L2_BASE + 0x204)
+#define PPE_FDB_RD_RSLT_DATA2          (PPE_L2_BASE + 0x208)
+
+#define PPE_FDB_OP_DATA0               (PPE_L2_BASE + 0x230)
+#define PPE_FDB_OP_DATA1               (PPE_L2_BASE + 0x234)
+#define PPE_FDB_OP_DATA2               (PPE_L2_BASE + 0x238)
+
+#define PPE_FDB_RD_OP_DATA0            (PPE_L2_BASE + 0x260)
+#define PPE_FDB_RD_OP_DATA1            (PPE_L2_BASE + 0x264)
+#define PPE_FDB_RD_OP_DATA2            (PPE_L2_BASE + 0x268)
+
+#define PPE_PORT_BRIDGE_CTRL(port)     (PPE_L2_BASE + 0x300 + (port) * 0x4)
+#define   PPE_BRIDGE_NEW_LRN_EN        BIT(0)
+#define   PPE_BRIDGE_STA_MOVE_EN       BIT(3)
+#define   PPE_BRIDGE_PORT_ISOL         GENMASK(15, 8)
+#define   PPE_PORT_BRIDGE_CTRL_TXMAC_EN        BIT(16)
+
+#define PPE_MC_MTU_CTRL(port)          (PPE_L2_BASE + 0xa00 + (port) * 0x4)
+#define   PPE_MC_MTU_CTRL_TX_CNT_EN    BIT(16)
+
+#define PPE_RFDB_TBL(idx)              (PPE_L2_BASE + 0x1000 + (idx) * 0x8)
+
+#define PPE_APP_CTRL(idx)              (PPE_L2_BASE + 0x1400 + (idx) * 0x10)
+
+#define PPE_VSI_TBL(vsi)               (PPE_L2_BASE + 0x1800 + (vsi) * 0x10)
+#define   PPE_VSI_TBL_MEMBER           GENMASK(7, 0)
+#define   PPE_VSI_TBL_UUC              GENMASK(15, 8)
+#define   PPE_VSI_TBL_UMC              GENMASK(23, 16)
+#define   PPE_VSI_TBL_BC               GENMASK(31, 24)
+#define   PPE_VSI_TBL_NEW_ADDR_LRN_EN  BIT(0)
+#define   PPE_VSI_TBL_STA_MOVE_LRN_EN  BIT(3)
+
+#define PPE_MRU_MTU_CTRL(port)         (PPE_L2_BASE + 0x3000 + (port) * 0x10)
+#define   PPE_MRU_MTU_CTRL_RX_CNT_EN   BIT(0)
+#define   PPE_MRU_MTU_CTRL_TX_CNT_EN   BIT(1)
+
+/* --- L3 (base 0x200000) --- */
+#define PPE_L3_BASE                    0x200000
+
+#define PPE_L3_VP_PORT_TBL(port)       (PPE_L3_BASE + 0x1000 + (port) * 0x10)
+#define   PPE_L3_VP_VSI_VALID          BIT(9)
+#define   PPE_L3_VP_VSI                GENMASK(14, 10)
+
+/* --- Traffic Manager (base 0x400000) --- */
+#define PPE_TM_BASE                    0x400000
+
+#define PPE_TM_TDM_DEPTH               (PPE_TM_BASE + 0x0)
+#define   PPE_TM_TDM_DEPTH_MASK        GENMASK(7, 0)
+
+#define PPE_TM_L0_FLOW_MAP(i)          (PPE_TM_BASE + 0x2000 + (i) * 0x10)
+#define   PPE_L0_SP_ID                 GENMASK(5, 0)
+#define   PPE_L0_C_PRI                 GENMASK(8, 6)
+#define   PPE_L0_E_PRI                 GENMASK(11, 9)
+#define   PPE_L0_C_DRR_WT              GENMASK(21, 12)
+#define   PPE_L0_E_DRR_WT              GENMASK(31, 22)
+
+#define PPE_TM_L0_C_SP(i)              (PPE_TM_BASE + 0x4000 + (i) * 0x10)
+#define PPE_TM_L0_E_SP(i)              (PPE_TM_BASE + 0x6000 + (i) * 0x10)
+#define   PPE_L0_SP_DRR_ID             GENMASK(7, 0)
+
+#define PPE_TM_L0_PORT_MAP(i)          (PPE_TM_BASE + 0x8000 + (i) * 0x10)
+#define   PPE_L0_PORT_NUM              GENMASK(3, 0)
+
+#define PPE_TM_RING_Q_MAP(r)           (PPE_TM_BASE + 0x2a000 + (r) * 0x40)
+
+#define PPE_TM_L1_FLOW_MAP(i)          (PPE_TM_BASE + 0x40000 + (i) * 0x10)
+#define   PPE_L1_SP_ID                 GENMASK(3, 0)
+#define   PPE_L1_C_PRI                 GENMASK(6, 4)
+#define   PPE_L1_E_PRI                 GENMASK(9, 7)
+#define   PPE_L1_C_DRR_WT              GENMASK(19, 10)
+#define   PPE_L1_E_DRR_WT              GENMASK(29, 20)
+
+#define PPE_TM_L1_C_SP(i)              (PPE_TM_BASE + 0x42000 + (i) * 0x10)
+#define PPE_TM_L1_E_SP(i)              (PPE_TM_BASE + 0x44000 + (i) * 0x10)
+#define   PPE_L1_SP_DRR_ID             GENMASK(5, 0)
+
+#define PPE_TM_L1_PORT_MAP(i)          (PPE_TM_BASE + 0x46000 + (i) * 0x10)
+#define   PPE_L1_PORT_NUM              GENMASK(3, 0)
+
+#define PPE_TM_PSCH_TDM(i)             (PPE_TM_BASE + 0x7a000 + (i) * 0x10)
+#define   PPE_PSCH_DES_PORT            GENMASK(3, 0)
+#define   PPE_PSCH_ENS_PORT            GENMASK(7, 4)
+#define   PPE_PSCH_ENS_PORT_BMP        GENMASK(15, 8)
+
+/* --- Buffer Manager (base 0x600000) --- */
+#define PPE_BM_BASE                    0x600000
+
+#define PPE_BM_FC_MODE(i)              (PPE_BM_BASE + 0x100 + (i) * 0x4)
+#define   PPE_BM_FC_EN                 BIT(0)
+
+#define PPE_BM_GROUP_ID(i)             (PPE_BM_BASE + 0x180 + (i) * 0x4)
+
+#define PPE_BM_SHARED_GRP(g)           (PPE_BM_BASE + 0x290 + (g) * 0x4)
+#define   PPE_BM_SHARED_LIMIT          GENMASK(10, 0)
+
+#define PPE_BM_PORT_FC_W0(i)           (PPE_BM_BASE + 0x1000 + (i) * 0x10)
+#define PPE_BM_PORT_FC_W1(i)           (PPE_BM_BASE + 0x1000 + (i) * 0x10 + 0x4)
+#define   PPE_BM_REACT_LIMIT           GENMASK(8, 0)
+#define   PPE_BM_RESUME_FLOOR          GENMASK(17, 9)
+#define   PPE_BM_RESUME_OFF            GENMASK(28, 18)
+#define   PPE_BM_CEILING_LO            GENMASK(31, 29)
+#define   PPE_BM_CEILING_HI            GENMASK(7, 0)
+#define   PPE_BM_WEIGHT                GENMASK(10, 8)
+#define   PPE_BM_DYNAMIC               BIT(11)
+#define   PPE_BM_PREALLOC              GENMASK(22, 12)
+
+/* --- Queue Manager (base 0x800000) --- */
+#define PPE_QM_BASE                    0x800000
+
+#define PPE_QM_UCAST_MAP(i)            (PPE_QM_BASE + 0x10000 + (i) * 0x10)
+#define   PPE_QM_PROFILE_ID            GENMASK(3, 0)
+#define   PPE_QM_QUEUE_ID              GENMASK(11, 4)
+
+#define PPE_QM_UCAST_HASH_MAP(i)       (PPE_QM_BASE + 0x30000 + (i) * 0x10)
+#define   PPE_QM_HASH_CLASS            GENMASK(3, 0)
+
+#define PPE_QM_UCAST_PRI_MAP(i)        (PPE_QM_BASE + 0x42000 + (i) * 0x10)
+#define   PPE_QM_PRI_CLASS             GENMASK(3, 0)
+
+#define PPE_QM_AC_UNI_W0(i)            (PPE_QM_BASE + 0x48000 + (i) * 0x10)
+#define PPE_QM_AC_UNI_W1(i)            (PPE_QM_BASE + 0x48000 + (i) * 0x10 + 0x4)
+#define PPE_QM_AC_UNI_W2(i)            (PPE_QM_BASE + 0x48000 + (i) * 0x10 + 0x8)
+#define PPE_QM_AC_UNI_W3(i)            (PPE_QM_BASE + 0x48000 + (i) * 0x10 + 0xc)
+#define   PPE_AC_EN                    BIT(0)
+#define   PPE_AC_GRP_ID                GENMASK(5, 4)
+#define   PPE_AC_SHARED_DYNAMIC        BIT(17)
+#define   PPE_AC_SHARED_WEIGHT         GENMASK(20, 18)
+#define   PPE_AC_SHARED_CEILING        GENMASK(31, 21)
+#define   PPE_AC_GRN_RESUME_OFF        GENMASK(23, 13)
+
+#define PPE_QM_AC_MUL_W0(i)            (PPE_QM_BASE + 0x4a000 + (i) * 0x10)
+#define PPE_QM_AC_MUL_W1(i)            (PPE_QM_BASE + 0x4a000 + (i) * 0x10 + 0x4)
+#define PPE_QM_AC_MUL_W2(i)            (PPE_QM_BASE + 0x4a000 + (i) * 0x10 + 0x8)
+#define   PPE_AC_MUL_EN                BIT(0)
+#define   PPE_AC_MUL_CEILING           GENMASK(26, 16)
+#define   PPE_AC_MUL_GRN_MAX_LO        GENMASK(31, 27)
+#define   PPE_AC_MUL_GRN_MAX_HI        GENMASK(5, 0)
+#define   PPE_AC_MUL_GRN_RESUME_HI     GENMASK(17, 11)
+
+#define PPE_QM_AC_GRP_W0(g)            (PPE_QM_BASE + 0x4c000 + (g) * 0x10)
+#define PPE_QM_AC_GRP_W1(g)            (PPE_QM_BASE + 0x4c000 + (g) * 0x10 + 0x4)
+#define PPE_QM_AC_GRP_W2(g)            (PPE_QM_BASE + 0x4c000 + (g) * 0x10 + 0x8)
+#define   PPE_AC_GRP_LIMIT             GENMASK(14, 4)
+#define   PPE_AC_GRP_PALLOC            GENMASK(26, 16)
+
+/* --- FDB fields --- */
+#define PPE_FDB_OP_CMD_ID              GENMASK(3, 0)
+#define PPE_FDB_OP_TYPE                        GENMASK(7, 5)
+#define PPE_FDB_OP_HASH_BLOCK          GENMASK(9, 8)
+#define PPE_FDB_OP_MODE                        BIT(10)
+#define PPE_FDB_OP_ENTRY_IDX           GENMASK(21, 11)
+
+#define PPE_FDB_RSLT_CMD_ID            GENMASK(3, 0)
+
+#define PPE_FDB_DATA1_VALID            BIT(16)
+#define PPE_FDB_DATA1_LKP_VALID        BIT(17)
+#define PPE_FDB_DATA1_VSI              GENMASK(22, 18)
+#define PPE_FDB_DATA1_DST_LO           GENMASK(31, 23)
+
+#define PPE_FDB_DATA2_DST_HI           GENMASK(2, 0)
+#define PPE_FDB_DATA2_DST_TYPE         GENMASK(4, 3)
+#define PPE_FDB_DATA2_HIT_AGE          GENMASK(10, 9)
+
+/* --- Constants --- */
+#define PPE_NUM_PORTS                  8
+#define PPE_MAX_SP_PRI                 8
+#define PPE_L0_QUEUES                  300
+#define PPE_L0_UCAST_QUEUES            256
+
+#define PPE_BM_PORTS                   15
+#define PPE_BM_PHY_START               8
+
+#define PPE_VSI_MAX                    32
+#define PPE_VSI_INVALID                        U32_MAX
+#define PPE_DEFAULT_MTU                        1514
+#define PPE_MTU_SHIFT                  16
+#define PPE_MAX_FRAME_SIZE             0x3000
+#define PPE_AGE_UNIT_MS                        8000
+
+#define PPE_FDB_TBL_NUM                        2048
+#define PPE_FDB_OP_ADD                 0
+#define PPE_FDB_OP_DEL                 1
+#define PPE_FDB_OP_GET                 2
+#define PPE_FDB_DST_PORT               2
+#define PPE_FDB_DST_PORTMAP            3
+#define PPE_FDB_AGE_STATIC             3
+#define PPE_FDB_OP_FLUSH               4
+
+#define PPE_XLT_TBL_NUM                        64
+#define PPE_XLT_MISS_FWD_DROP          3
+#define PPE_XLT_CVID_DEL               2
+#define PPE_XLT_CKEY_TAGGED            4
+
+#define PPE_EG_UNTAGGED                        0
+#define PPE_EG_TAGGED                  1
+#define PPE_EG_UNMODIFIED              2
+
+#define PPE_MAX_SERVICE_CODES          256
+#define PPE_MAX_CPU_CODES              256
+#define PPE_MAX_VPORT                  256
+
+#define QM_VP_PORT_OFFSET              0
+#define QM_CPU_CODE_OFFSET             1024
+#define QM_SERVICE_CODE_OFFSET         2048
+
+struct psch_tdm_data {
+       const struct psch_tdm_entry *entries;
+       int num;
+};
+
+struct bm_tdm_data {
+       const struct bm_tdm_entry *entries;
+       int num;
+};
+
+enum ppe_type {
+       PPE_TYPE_IPQ6018,
+       PPE_TYPE_IPQ8074,
+};
+
+struct ppe_data {
+       enum ppe_type type;
+       u8 num_ports;
+       u8 num_gmacs;
+       u8 loopback_port;
+       u8 bm_phy_end;
+       u8 bm_internal_start;
+       u16 bm_group_buf;
+       u16 bm_ceiling;
+       u16 qm_total_buf;
+       u16 qm_ceiling;
+       u16 qm_green_max;
+       const struct psch_tdm_data *psch_tdm;
+       const struct bm_tdm_data *bm_tdm;
+};
+
+struct qca_ppe_bridge_vsi {
+       struct net_device *br_dev;
+       u32 vsi;
+       int refcount;
+};
+
+struct qca_ppe_vlan_entry {
+       struct net_device *br_dev;
+       u16 vid;
+       u32 vsi;
+       u8 ports;
+       u8 pvid_ports;
+       int xlt_idx;
+       int xlt_pvid_idx;
+};
+
+struct qca_ppe_priv {
+       struct dsa_switch ds;
+       struct regmap *regmap;
+       const struct ppe_data *data;
+       struct clk_bulk_data *clks;
+       int num_clks;
+       spinlock_t fdb_lock;
+       DECLARE_BITMAP(vsi_bitmap, PPE_VSI_MAX);
+       DECLARE_BITMAP(xlt_bitmap, PPE_XLT_TBL_NUM);
+       u32 port_vsi[QCA_PPE_MAX_PORTS];
+       struct qca_ppe_bridge_vsi bridges[QCA_PPE_MAX_BRIDGES];
+       struct qca_ppe_vlan_entry vlans[PPE_VSI_MAX];
+       struct net_device *port_br_dev[QCA_PPE_MAX_PORTS];
+       u16 port_pvid[QCA_PPE_MAX_PORTS];
+       struct clk *port_rx_clk[QCA_PPE_MAX_PORTS];
+       struct clk *port_tx_clk[QCA_PPE_MAX_PORTS];
+       struct reset_control *port_rst[QCA_PPE_MAX_PORTS];
+};
+
+extern const struct psch_tdm_data cppe_psch_tdm_data;
+extern const struct psch_tdm_data hppe_psch_tdm_data;
+
+extern const struct bm_tdm_data cppe_bm_tdm_data;
+extern const struct bm_tdm_data hppe_bm_tdm_data;
+
+static inline struct qca_ppe_priv *ds_to_priv(struct dsa_switch *ds)
+{
+       return container_of(ds, struct qca_ppe_priv, ds);
+}
+
+void ppe_scheduler_init(struct qca_ppe_priv *priv);
+
+int ppe_vsi_alloc(struct qca_ppe_priv *priv);
+void ppe_vsi_free(struct qca_ppe_priv *priv, u32 vsi);
+void ppe_vsi_member_set(struct qca_ppe_priv *priv, u32 vsi, u32 portmask);
+
+int qca_ppe_vlan_setup(struct dsa_switch *ds);
+int qca_ppe_port_vlan_filtering(struct dsa_switch *ds, int port,
+                               bool vlan_filtering,
+                               struct netlink_ext_ack *extack);
+int qca_ppe_port_vlan_add(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan,
+                         struct netlink_ext_ack *extack);
+int qca_ppe_port_vlan_del(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan);
+
+#endif
diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c
new file mode 100644 (file)
index 0000000..d7c9c79
--- /dev/null
@@ -0,0 +1,1567 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/pcs/pcs.h>
+#include <linux/pcs/pcs-qca-uniphy.h>
+#include <linux/phy.h>
+#include <linux/phylink.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/if_bridge.h>
+
+#include "qca_ppe.h"
+
+static void ppe_port_gmac_set(struct qca_ppe_priv *priv, int port,
+                            bool tx_en, bool rx_en)
+{
+       int gmac = port - 1;
+       u32 val = 0;
+
+       if (port < 1 || port >= priv->data->num_ports)
+               return;
+
+       if (tx_en)
+               val |= PPE_MAC_ENABLE_TXMAC_EN;
+       if (rx_en)
+               val |= PPE_MAC_ENABLE_RXMAC_EN;
+       regmap_update_bits(priv->regmap, PPE_GMAC_ENABLE(gmac),
+                          PPE_MAC_ENABLE_TXMAC_EN | PPE_MAC_ENABLE_RXMAC_EN,
+                          val);
+}
+
+static void ppe_port_xgmac_set(struct qca_ppe_priv *priv, int port,
+                              bool tx_en, bool rx_en)
+{
+       int xgmac = port - 5;
+
+       if (port < 5 || port >= priv->data->num_ports)
+               return;
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_TX_CONF(xgmac),
+                          PPE_XGMAC_TX_ENABLE,
+                          tx_en ? PPE_XGMAC_TX_ENABLE : 0);
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_RX_CONF(xgmac),
+                          PPE_XGMAC_RX_ENABLE,
+                          rx_en ? PPE_XGMAC_RX_ENABLE : 0);
+}
+
+static void ppe_port_bridge_txmac_set(struct qca_ppe_priv *priv, int port,
+                                     bool enable)
+{
+       regmap_update_bits(priv->regmap, PPE_PORT_BRIDGE_CTRL(port),
+                          PPE_PORT_BRIDGE_CTRL_TXMAC_EN,
+                          enable ? PPE_PORT_BRIDGE_CTRL_TXMAC_EN : 0);
+}
+
+static void ppe_gmac_link_up(struct qca_ppe_priv *priv, int port,
+                            int speed, int duplex,
+                            bool tx_pause, bool rx_pause)
+{
+       int gmac = port - 1;
+       u32 val;
+
+       regmap_read(priv->regmap, PPE_GMAC_SPEED(gmac), &val);
+       val &= ~PPE_GMAC_SPEED_MASK;
+       switch (speed) {
+       case SPEED_100:
+               val |= FIELD_PREP(PPE_GMAC_SPEED_MASK, 1);
+               break;
+       case SPEED_2500:
+       case SPEED_1000:
+               val |= FIELD_PREP(PPE_GMAC_SPEED_MASK, 2);
+               break;
+       }
+       regmap_write(priv->regmap, PPE_GMAC_SPEED(gmac), val);
+
+       val = 0;
+       if (duplex == DUPLEX_FULL)
+               val |= PPE_MAC_ENABLE_DUPLEX;
+       if (tx_pause)
+               val |= PPE_MAC_ENABLE_TX_FLOW_EN;
+       if (rx_pause)
+               val |= PPE_MAC_ENABLE_RX_FLOW_EN;
+       regmap_update_bits(priv->regmap, PPE_GMAC_ENABLE(gmac),
+                          PPE_MAC_ENABLE_DUPLEX | PPE_MAC_ENABLE_TX_FLOW_EN |
+                          PPE_MAC_ENABLE_RX_FLOW_EN, val);
+}
+
+static void ppe_xgmac_link_up(struct qca_ppe_priv *priv, int port,
+                             phy_interface_t interface, int speed,
+                             bool tx_pause, bool rx_pause)
+{
+       int xgmac = port - 5;
+       u32 val;
+
+       switch (speed) {
+       case SPEED_10:
+       case SPEED_100:
+       case SPEED_1000:
+               val = PPE_XGMAC_SPEED_SELECT_1000;
+               break;
+       case SPEED_2500:
+               val = PPE_XGMAC_SPEED_SELECT_2500;
+               break;
+       case SPEED_5000:
+               val = PPE_XGMAC_SPEED_SELECT_5000;
+               break;
+       case SPEED_10000:
+               val = PPE_XGMAC_SPEED_SELECT_10000;
+               break;
+       default:
+               return;
+       }
+
+       if (interface == PHY_INTERFACE_MODE_USXGMII ||
+           interface == PHY_INTERFACE_MODE_10GBASER) {
+               switch (speed) {
+               case SPEED_2500:
+               case SPEED_5000:
+               case SPEED_10000:
+                       val |= PPE_XGMAC_USXGMII_SELECT;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_TX_CONF(xgmac),
+                          PPE_XGMAC_SPEED_SELECT |
+                          PPE_XGMAC_USXGMII_SELECT, val);
+
+       regmap_write_bits(priv->regmap, PPE_XGMAC_RX_CONF(xgmac),
+                          PPE_XGMAC_AUTO_CRC_STRIP |
+                          PPE_XGMAC_CRC_STRIP_TYPE,
+                          PPE_XGMAC_AUTO_CRC_STRIP |
+                          PPE_XGMAC_CRC_STRIP_TYPE);
+
+       regmap_write_bits(priv->regmap, PPE_XGMAC_TX_FLOW_CTRL(xgmac),
+                         PPE_XGMAC_TX_FLOW_ENABLE,
+                         tx_pause ? PPE_XGMAC_RX_FLOW_ENABLE : 0);
+
+       regmap_write_bits(priv->regmap, PPE_XGMAC_RX_FLOW_CTRL(xgmac),
+                         PPE_XGMAC_RX_FLOW_ENABLE,
+                         rx_pause ? PPE_XGMAC_RX_FLOW_ENABLE : 0);
+}
+
+static void ppe_port_cnt_enable(struct qca_ppe_priv *priv, int port)
+{
+       regmap_update_bits(priv->regmap, PPE_MRU_MTU_CTRL(port) + 4,
+                          PPE_MRU_MTU_CTRL_RX_CNT_EN | PPE_MRU_MTU_CTRL_TX_CNT_EN,
+                          PPE_MRU_MTU_CTRL_RX_CNT_EN | PPE_MRU_MTU_CTRL_TX_CNT_EN);
+
+       regmap_update_bits(priv->regmap, PPE_MC_MTU_CTRL(port),
+                          PPE_MC_MTU_CTRL_TX_CNT_EN, PPE_MC_MTU_CTRL_TX_CNT_EN);
+
+       regmap_update_bits(priv->regmap, PPE_PORT_EG_VLAN(port),
+                          PPE_PORT_EG_VLAN_TX_CNT_EN, PPE_PORT_EG_VLAN_TX_CNT_EN);
+}
+
+int ppe_vsi_alloc(struct qca_ppe_priv *priv)
+{
+       int vsi;
+
+       vsi = find_first_zero_bit(priv->vsi_bitmap, PPE_VSI_MAX);
+       if (vsi >= PPE_VSI_MAX)
+               return -ENOSPC;
+
+       set_bit(vsi, priv->vsi_bitmap);
+
+       regmap_write(priv->regmap, PPE_VSI_TBL(vsi), 0);
+       regmap_write(priv->regmap, PPE_VSI_TBL(vsi) + 4,
+                    PPE_VSI_TBL_NEW_ADDR_LRN_EN | PPE_VSI_TBL_STA_MOVE_LRN_EN);
+
+       return vsi;
+}
+
+void ppe_vsi_free(struct qca_ppe_priv *priv, u32 vsi)
+{
+       regmap_write(priv->regmap, PPE_VSI_TBL(vsi), 0);
+       regmap_write(priv->regmap, PPE_VSI_TBL(vsi) + 4, 0);
+       clear_bit(vsi, priv->vsi_bitmap);
+}
+
+void ppe_vsi_member_set(struct qca_ppe_priv *priv, u32 vsi,
+                              u32 portmask)
+{
+       u32 val;
+
+       val = FIELD_PREP(PPE_VSI_TBL_MEMBER, portmask) |
+             FIELD_PREP(PPE_VSI_TBL_UUC, portmask) |
+             FIELD_PREP(PPE_VSI_TBL_UMC, portmask) |
+             FIELD_PREP(PPE_VSI_TBL_BC, portmask);
+       regmap_write(priv->regmap, PPE_VSI_TBL(vsi), val);
+       regmap_write(priv->regmap, PPE_VSI_TBL(vsi) + 4,
+               PPE_VSI_TBL_NEW_ADDR_LRN_EN | PPE_VSI_TBL_STA_MOVE_LRN_EN);
+}
+
+static void ppe_port_vsi_set(struct qca_ppe_priv *priv, int port, u32 vsi)
+{
+       u32 val;
+
+       regmap_read(priv->regmap, PPE_L3_VP_PORT_TBL(port) + 4, &val);
+       val &= ~(PPE_L3_VP_VSI_VALID | PPE_L3_VP_VSI);
+       if (vsi != PPE_VSI_INVALID) {
+               val |= PPE_L3_VP_VSI_VALID;
+               val |= FIELD_PREP(PPE_L3_VP_VSI, vsi);
+       }
+       regmap_write(priv->regmap, PPE_L3_VP_PORT_TBL(port) + 4, val);
+}
+
+static int ppe_fdb_op_wait(struct qca_ppe_priv *priv, u32 rslt_reg,
+                          u32 cmd_id)
+{
+       u32 val;
+       int i;
+
+       for (i = 0; i < 100; i++) {
+               regmap_read(priv->regmap, rslt_reg, &val);
+               if (FIELD_GET(PPE_FDB_RSLT_CMD_ID, val) == cmd_id)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static void ppe_fdb_encode(const unsigned char *addr, int port, u16 vid,
+                          bool is_static, u32 *data0, u32 *data1, u32 *data2)
+{
+       *data0 = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
+
+       *data1 = (addr[0] << 8) | addr[1];
+       *data1 |= PPE_FDB_DATA1_VALID | PPE_FDB_DATA1_LKP_VALID;
+       *data1 |= FIELD_PREP(PPE_FDB_DATA1_VSI, vid);
+       *data1 |= FIELD_PREP(PPE_FDB_DATA1_DST_LO, port);
+
+       *data2 = FIELD_PREP(PPE_FDB_DATA2_DST_TYPE, PPE_FDB_DST_PORT) |
+                FIELD_PREP(PPE_FDB_DATA2_HIT_AGE,
+                           is_static ? PPE_FDB_AGE_STATIC : 2);
+}
+
+static int ppe_fdb_op(struct qca_ppe_priv *priv, const unsigned char *addr,
+                     int port, u16 vid, u32 op_type)
+{
+       u32 data0, data1, data2;
+       int ret;
+
+       ppe_fdb_encode(addr, port, vid, op_type == PPE_FDB_OP_ADD,
+                      &data0, &data1, &data2);
+
+       spin_lock_bh(&priv->fdb_lock);
+
+       regmap_write(priv->regmap, PPE_FDB_OP_DATA0, data0);
+       regmap_write(priv->regmap, PPE_FDB_OP_DATA1, data1);
+       regmap_write(priv->regmap, PPE_FDB_OP_DATA2, data2);
+       regmap_write(priv->regmap, PPE_FDB_OP,
+                    FIELD_PREP(PPE_FDB_OP_TYPE, op_type) |
+                    FIELD_PREP(PPE_FDB_OP_HASH_BLOCK, 3));
+
+       ret = ppe_fdb_op_wait(priv, PPE_FDB_OP_RSLT, 0);
+
+       spin_unlock_bh(&priv->fdb_lock);
+
+       return ret;
+}
+
+static int ppe_fdb_read_entry(struct qca_ppe_priv *priv, u32 index,
+                             unsigned char *addr, u16 *vid, int *port,
+                             bool *is_static)
+{
+       u32 data0, data1, data2, cmd_id, val;
+       int ret;
+
+       cmd_id = index % 15;
+
+       spin_lock_bh(&priv->fdb_lock);
+
+       regmap_write(priv->regmap, PPE_FDB_RD_OP_DATA0, 0);
+       regmap_write(priv->regmap, PPE_FDB_RD_OP_DATA1, 0);
+       regmap_write(priv->regmap, PPE_FDB_RD_OP_DATA2, 0);
+
+       val = FIELD_PREP(PPE_FDB_OP_CMD_ID, cmd_id) |
+             FIELD_PREP(PPE_FDB_OP_TYPE, PPE_FDB_OP_GET) |
+             FIELD_PREP(PPE_FDB_OP_HASH_BLOCK, 3) |
+             PPE_FDB_OP_MODE |
+             FIELD_PREP(PPE_FDB_OP_ENTRY_IDX, index);
+       regmap_write(priv->regmap, PPE_FDB_RD_OP, val);
+
+       ret = ppe_fdb_op_wait(priv, PPE_FDB_RD_OP_RSLT, cmd_id);
+       if (ret)
+               goto unlock;
+
+       regmap_read(priv->regmap, PPE_FDB_RD_RSLT_DATA0, &data0);
+       regmap_read(priv->regmap, PPE_FDB_RD_RSLT_DATA1, &data1);
+       regmap_read(priv->regmap, PPE_FDB_RD_RSLT_DATA2, &data2);
+
+unlock:
+       spin_unlock_bh(&priv->fdb_lock);
+
+       if (ret)
+               return ret;
+
+       if (!(data1 & PPE_FDB_DATA1_VALID))
+               return -ENOENT;
+
+       if (FIELD_GET(PPE_FDB_DATA2_DST_TYPE, data2) != PPE_FDB_DST_PORT)
+               return -ENOENT;
+
+       addr[2] = (data0 >> 24) & 0xff;
+       addr[3] = (data0 >> 16) & 0xff;
+       addr[4] = (data0 >> 8) & 0xff;
+       addr[5] = data0 & 0xff;
+       addr[0] = (data1 >> 8) & 0xff;
+       addr[1] = data1 & 0xff;
+
+       *vid = FIELD_GET(PPE_FDB_DATA1_VSI, data1);
+       *port = FIELD_GET(PPE_FDB_DATA1_DST_LO, data1) |
+               (FIELD_GET(PPE_FDB_DATA2_DST_HI, data2) << 9);
+       *is_static = FIELD_GET(PPE_FDB_DATA2_HIT_AGE, data2) == PPE_FDB_AGE_STATIC;
+
+       return 0;
+}
+
+static int ppe_fdb_flush(struct qca_ppe_priv *priv)
+{
+       int ret;
+
+       spin_lock_bh(&priv->fdb_lock);
+
+       regmap_write(priv->regmap, PPE_FDB_OP,
+               FIELD_PREP(PPE_FDB_OP_TYPE, PPE_FDB_OP_FLUSH));
+
+       ret = ppe_fdb_op_wait(priv, PPE_FDB_OP_RSLT, 0);
+
+       spin_unlock_bh(&priv->fdb_lock);
+
+       return ret;
+}
+
+static void ppe_fdb_encode_mcast(const unsigned char *addr, u32 portmap,
+                                u16 vid, u32 *data0, u32 *data1, u32 *data2)
+{
+       *data0 = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
+
+       *data1 = (addr[0] << 8) | addr[1];
+       *data1 |= PPE_FDB_DATA1_VALID | PPE_FDB_DATA1_LKP_VALID;
+       *data1 |= FIELD_PREP(PPE_FDB_DATA1_VSI, vid);
+       *data1 |= FIELD_PREP(PPE_FDB_DATA1_DST_LO, portmap);
+
+       *data2 = FIELD_PREP(PPE_FDB_DATA2_DST_HI, portmap >> 9) |
+                FIELD_PREP(PPE_FDB_DATA2_DST_TYPE, PPE_FDB_DST_PORTMAP) |
+                FIELD_PREP(PPE_FDB_DATA2_HIT_AGE, PPE_FDB_AGE_STATIC);
+}
+
+static int ppe_fdb_lookup(struct qca_ppe_priv *priv,
+                         const unsigned char *addr, u16 vid, u32 *portmap)
+{
+       u32 data1, data2;
+       int ret;
+
+       spin_lock_bh(&priv->fdb_lock);
+
+       regmap_write(priv->regmap, PPE_FDB_RD_OP_DATA0,
+                    (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]);
+       regmap_write(priv->regmap, PPE_FDB_RD_OP_DATA1,
+                    ((addr[0] << 8) | addr[1]) |
+                    FIELD_PREP(PPE_FDB_DATA1_VSI, vid));
+       regmap_write(priv->regmap, PPE_FDB_RD_OP_DATA2, 0);
+
+       regmap_write(priv->regmap, PPE_FDB_RD_OP,
+                    FIELD_PREP(PPE_FDB_OP_TYPE, PPE_FDB_OP_GET) |
+                    FIELD_PREP(PPE_FDB_OP_HASH_BLOCK, 3));
+
+       ret = ppe_fdb_op_wait(priv, PPE_FDB_RD_OP_RSLT, 0);
+       if (ret)
+               goto out;
+
+       regmap_read(priv->regmap, PPE_FDB_RD_RSLT_DATA1, &data1);
+       regmap_read(priv->regmap, PPE_FDB_RD_RSLT_DATA2, &data2);
+
+       if (!(data1 & PPE_FDB_DATA1_VALID)) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       *portmap = FIELD_GET(PPE_FDB_DATA1_DST_LO, data1) |
+                  (FIELD_GET(PPE_FDB_DATA2_DST_HI, data2) << 9);
+
+out:
+       spin_unlock_bh(&priv->fdb_lock);
+       return ret;
+}
+
+static int ppe_fdb_mcast_op(struct qca_ppe_priv *priv,
+                           const unsigned char *addr, u32 portmap,
+                           u16 vid, u32 op_type)
+{
+       u32 data0, data1, data2;
+       int ret;
+
+       ppe_fdb_encode_mcast(addr, portmap, vid, &data0, &data1, &data2);
+
+       spin_lock_bh(&priv->fdb_lock);
+
+       regmap_write(priv->regmap, PPE_FDB_OP_DATA0, data0);
+       regmap_write(priv->regmap, PPE_FDB_OP_DATA1, data1);
+       regmap_write(priv->regmap, PPE_FDB_OP_DATA2, data2);
+       regmap_write(priv->regmap, PPE_FDB_OP,
+                    FIELD_PREP(PPE_FDB_OP_TYPE, op_type) |
+                    FIELD_PREP(PPE_FDB_OP_HASH_BLOCK, 3));
+
+       ret = ppe_fdb_op_wait(priv, PPE_FDB_OP_RSLT, 0);
+
+       spin_unlock_bh(&priv->fdb_lock);
+
+       return ret;
+}
+
+static enum dsa_tag_protocol
+qca_ppe_get_tag_protocol(struct dsa_switch *ds, int port,
+                            enum dsa_tag_protocol mprot)
+{
+       return DSA_TAG_PROTO_OOB;
+}
+
+static int qca_ppe_setup(struct dsa_switch *ds)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       int num_ports = ds->num_ports;
+       u32 port_mask;
+       u32 val;
+       int i;
+
+       port_mask = BIT(num_ports) - 1;
+
+       for (i = 0; i < num_ports; i++)
+               priv->port_vsi[i] = PPE_VSI_INVALID;
+
+       regmap_write(priv->regmap, PPE_FDB_OP, 0);
+
+       for (i = 0; i < num_ports; i++) {
+               regmap_write(priv->regmap, PPE_CST_STATE(i), PPE_STP_FORWARDING);
+
+               regmap_write(priv->regmap, PPE_MRU_MTU_CTRL(i),
+                            PPE_DEFAULT_MTU | (PPE_DEFAULT_MTU << PPE_MTU_SHIFT));
+
+               if (i >= 1)
+                       regmap_write(priv->regmap, PPE_GMAC_MIB_CTRL(i - 1),
+                                    PPE_MIB_EN);
+
+               val = PPE_BRIDGE_NEW_LRN_EN |
+                     PPE_BRIDGE_STA_MOVE_EN |
+                     FIELD_PREP(PPE_BRIDGE_PORT_ISOL, port_mask);
+               if (dsa_is_cpu_port(ds, i))
+                       val |= PPE_PORT_BRIDGE_CTRL_TXMAC_EN;
+               regmap_update_bits(priv->regmap, PPE_PORT_BRIDGE_CTRL(i),
+                                  PPE_BRIDGE_NEW_LRN_EN |
+                                  PPE_BRIDGE_STA_MOVE_EN |
+                                  PPE_BRIDGE_PORT_ISOL |
+                                  PPE_PORT_BRIDGE_CTRL_TXMAC_EN,
+                                  val);
+
+               ppe_port_cnt_enable(priv, i);
+       }
+
+       qca_ppe_vlan_setup(ds);
+
+       set_bit(0, priv->vsi_bitmap);
+       val = FIELD_PREP(PPE_VSI_TBL_MEMBER,
+                        dsa_user_ports(ds) | BIT(QCA_PPE_CPU_PORT)) |
+             FIELD_PREP(PPE_VSI_TBL_UUC, BIT(QCA_PPE_CPU_PORT)) |
+             FIELD_PREP(PPE_VSI_TBL_UMC, BIT(QCA_PPE_CPU_PORT)) |
+             FIELD_PREP(PPE_VSI_TBL_BC, BIT(QCA_PPE_CPU_PORT));
+       regmap_write(priv->regmap, PPE_VSI_TBL(0), val);
+       regmap_write(priv->regmap, PPE_VSI_TBL(0) + 4,
+               PPE_VSI_TBL_NEW_ADDR_LRN_EN | PPE_VSI_TBL_STA_MOVE_LRN_EN);
+
+       for (i = 1; i < num_ports; i++)
+               ppe_port_vsi_set(priv, i, 0);
+
+       ppe_fdb_flush(priv);
+
+       regmap_update_bits(priv->regmap, PPE_L2_GLOBAL_CONF,
+                          PPE_L2_LRN_EN | PPE_L2_AGE_EN,
+                          PPE_L2_LRN_EN | PPE_L2_AGE_EN);
+
+       ds->ageing_time_min = PPE_AGE_UNIT_MS;
+       ds->ageing_time_max = (unsigned int)min_t(u64,
+               (u64)PPE_AGE_UNIT_MS * PPE_AGE_TIMER_MASK, U32_MAX);
+       ds->assisted_learning_on_cpu_port = true;
+
+       return 0;
+}
+
+static int qca_ppe_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       u32 timer = msecs / PPE_AGE_UNIT_MS;
+
+       regmap_update_bits(priv->regmap, PPE_AGE_TIMER, PPE_AGE_TIMER_MASK,
+                          FIELD_PREP(PPE_AGE_TIMER_MASK, timer));
+
+       return 0;
+}
+
+static int qca_ppe_port_enable(struct dsa_switch *ds, int port,
+                                  struct phy_device *phy)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+
+       ppe_port_bridge_txmac_set(priv, port, true);
+
+       return 0;
+}
+
+static void qca_ppe_port_disable(struct dsa_switch *ds, int port)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+
+       ppe_port_bridge_txmac_set(priv, port, false);
+}
+
+static struct qca_ppe_bridge_vsi *
+bridge_vsi_find(struct qca_ppe_priv *priv, struct net_device *br_dev)
+{
+       int i;
+
+       for (i = 0; i < QCA_PPE_MAX_BRIDGES; i++)
+               if (priv->bridges[i].br_dev == br_dev)
+                       return &priv->bridges[i];
+
+       return NULL;
+}
+
+static struct qca_ppe_bridge_vsi *
+bridge_vsi_alloc(struct qca_ppe_priv *priv, struct net_device *br_dev)
+{
+       int vsi, i;
+
+       vsi = ppe_vsi_alloc(priv);
+       if (vsi < 0)
+               return NULL;
+
+       for (i = 0; i < QCA_PPE_MAX_BRIDGES; i++) {
+               if (priv->bridges[i].br_dev)
+                       continue;
+
+               priv->bridges[i].br_dev = br_dev;
+               priv->bridges[i].vsi = vsi;
+               priv->bridges[i].refcount = 0;
+               return &priv->bridges[i];
+       }
+
+       ppe_vsi_free(priv, vsi);
+       return NULL;
+}
+
+static void bridge_vsi_put(struct qca_ppe_priv *priv,
+                          struct qca_ppe_bridge_vsi *bvsi)
+{
+       bvsi->refcount--;
+       if (bvsi->refcount > 0)
+               return;
+
+       ppe_vsi_free(priv, bvsi->vsi);
+       bvsi->br_dev = NULL;
+       bvsi->vsi = 0;
+}
+
+static void bridge_vsi_members_update(struct qca_ppe_priv *priv,
+                                     struct qca_ppe_bridge_vsi *bvsi)
+{
+       u32 portmask = 0;
+       int i;
+
+       for (i = 0; i < priv->ds.num_ports; i++)
+               if (priv->port_vsi[i] != PPE_VSI_INVALID &&
+                   priv->port_vsi[i] == bvsi->vsi)
+                       portmask |= BIT(i);
+
+       portmask |= BIT(QCA_PPE_CPU_PORT);
+
+       ppe_vsi_member_set(priv, bvsi->vsi, portmask);
+}
+
+static int qca_ppe_port_bridge_join(struct dsa_switch *ds, int port,
+                                       struct dsa_bridge bridge,
+                                       bool *tx_fwd_offload,
+                                       struct netlink_ext_ack *extack)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       struct qca_ppe_bridge_vsi *bvsi;
+
+       bvsi = bridge_vsi_find(priv, bridge.dev);
+       if (!bvsi) {
+               bvsi = bridge_vsi_alloc(priv, bridge.dev);
+               if (!bvsi)
+                       return -ENOSPC;
+       }
+
+       bvsi->refcount++;
+       priv->port_vsi[port] = bvsi->vsi;
+       priv->port_br_dev[port] = bridge.dev;
+
+       ppe_port_vsi_set(priv, port, bvsi->vsi);
+       bridge_vsi_members_update(priv, bvsi);
+
+       return 0;
+}
+
+static void qca_ppe_port_bridge_leave(struct dsa_switch *ds, int port,
+                                         struct dsa_bridge bridge)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       struct qca_ppe_bridge_vsi *bvsi;
+
+       bvsi = bridge_vsi_find(priv, bridge.dev);
+       if (!bvsi)
+               return;
+
+       priv->port_vsi[port] = PPE_VSI_INVALID;
+       priv->port_br_dev[port] = NULL;
+       ppe_port_vsi_set(priv, port, PPE_VSI_INVALID);
+       bridge_vsi_members_update(priv, bvsi);
+       bridge_vsi_put(priv, bvsi);
+}
+
+static int qca_ppe_port_fdb_add(struct dsa_switch *ds, int port,
+                                   const unsigned char *addr, u16 vid,
+                                   struct dsa_db db)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+
+       return ppe_fdb_op(priv, addr, port, vid, PPE_FDB_OP_ADD);
+}
+
+static int qca_ppe_port_fdb_del(struct dsa_switch *ds, int port,
+                                   const unsigned char *addr, u16 vid,
+                                   struct dsa_db db)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+
+       return ppe_fdb_op(priv, addr, port, vid, PPE_FDB_OP_DEL);
+}
+
+static int qca_ppe_port_fdb_dump(struct dsa_switch *ds, int port,
+                                    dsa_fdb_dump_cb_t *cb, void *data)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       unsigned char addr[ETH_ALEN];
+       bool is_static;
+       int fdb_port;
+       u16 vid;
+       u32 i;
+
+       for (i = 0; i < PPE_FDB_TBL_NUM; i++) {
+               if (ppe_fdb_read_entry(priv, i, addr, &vid, &fdb_port,
+                                      &is_static))
+                       continue;
+
+               if (fdb_port != port)
+                       continue;
+
+               if (cb(addr, vid, is_static, data))
+                       break;
+       }
+
+       return 0;
+}
+
+static int qca_ppe_port_mdb_add(struct dsa_switch *ds, int port,
+                                   const struct switchdev_obj_port_mdb *mdb,
+                                   struct dsa_db db)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       u32 portmap;
+       int ret;
+
+       ret = ppe_fdb_lookup(priv, mdb->addr, mdb->vid, &portmap);
+       if (ret)
+               portmap = BIT(QCA_PPE_CPU_PORT);
+
+       portmap |= BIT(port);
+
+       return ppe_fdb_mcast_op(priv, mdb->addr, portmap,
+                               mdb->vid, PPE_FDB_OP_ADD);
+}
+
+static int qca_ppe_port_mdb_del(struct dsa_switch *ds, int port,
+                                   const struct switchdev_obj_port_mdb *mdb,
+                                   struct dsa_db db)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       u32 portmap;
+       int ret;
+
+       ret = ppe_fdb_lookup(priv, mdb->addr, mdb->vid, &portmap);
+       if (ret)
+               return ret;
+
+       portmap &= ~BIT(port);
+
+       if (!portmap || portmap == BIT(QCA_PPE_CPU_PORT))
+               return ppe_fdb_mcast_op(priv, mdb->addr, 0,
+                                       mdb->vid, PPE_FDB_OP_DEL);
+
+       return ppe_fdb_mcast_op(priv, mdb->addr, portmap,
+                               mdb->vid, PPE_FDB_OP_ADD);
+}
+
+static int qca_ppe_fill_available_pcs(struct phylink_config *config,
+                                     struct phylink_pcs **available_pcs,
+                                     unsigned int num_available_pcs)
+{
+       struct dsa_port *dp = dsa_phylink_to_port(config);
+
+       return fwnode_phylink_pcs_parse(of_fwnode_handle(dp->dn), available_pcs,
+                                       &num_available_pcs);
+}
+
+static void qca_ppe_phylink_get_caps(struct dsa_switch *ds, int port,
+                                    struct phylink_config *config)
+{
+       struct dsa_port *dp = dsa_to_port(ds, port);
+       int ret;
+
+       if (port != 0) {
+               ret = fwnode_phylink_pcs_parse(of_fwnode_handle(dp->dn), NULL,
+                                              &config->num_available_pcs);
+               if (ret)
+                       return;
+
+               config->fill_available_pcs = qca_ppe_fill_available_pcs;
+       }
+
+       switch (port) {
+       case 0:
+               config->mac_capabilities =
+                       MAC_1000FD | MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
+
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
+               break;
+       case 1 ... 4:
+               config->mac_capabilities =
+                       MAC_1000FD | MAC_100FD | MAC_10FD |
+                       MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
+
+               __set_bit(PHY_INTERFACE_MODE_QSGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_PSGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_SGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_RGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_RGMII_ID,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_RGMII_RXID,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_RGMII_TXID,
+                         config->supported_interfaces);
+               break;
+       case 5 ... 6:
+               config->mac_capabilities =
+                       MAC_10000FD | MAC_5000FD | MAC_2500FD |
+                       MAC_1000FD | MAC_100FD | MAC_10FD |
+                       MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
+
+               __set_bit(PHY_INTERFACE_MODE_PSGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_SGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_USXGMII,
+                         config->supported_interfaces);
+               __set_bit(PHY_INTERFACE_MODE_10GBASER,
+                         config->supported_interfaces);
+               break;
+       }
+
+       if (port != 0)
+               phy_interface_copy(config->pcs_interfaces,
+                                  config->supported_interfaces);
+}
+
+static void ppe_pcs_set_mux_hppe(struct qca_ppe_priv *priv, int port,
+                                unsigned int mode, phy_interface_t interface)
+{
+       u32 mask, val;
+
+       switch (port) {
+       case 4:
+               mask = HPPE_PORT4_PCS_SEL;
+               if (interface == PHY_INTERFACE_MODE_QSGMII ||
+                   interface == PHY_INTERFACE_MODE_PSGMII)
+                       val = FIELD_PREP(HPPE_PORT4_PCS_SEL,
+                                        HPPE_PORT4_PCS0);
+               break;
+       case 5:
+               mask = HPPE_PORT5_PCS_SEL | HPPE_PORT5_GMAC_SEL;
+               switch (interface) {
+               case PHY_INTERFACE_MODE_QSGMII:
+               case PHY_INTERFACE_MODE_PSGMII:
+                       val = FIELD_PREP(HPPE_PORT5_PCS_SEL,
+                                        HPPE_PORT5_PCS0) |
+                             FIELD_PREP(HPPE_PORT5_GMAC_SEL,
+                                        HPPE_PORT5_GMAC_SEL_GMAC);
+                       break;
+               case PHY_INTERFACE_MODE_SGMII:
+               case PHY_INTERFACE_MODE_1000BASEX:
+                       val = FIELD_PREP(HPPE_PORT5_PCS_SEL,
+                                        HPPE_PORT5_PCS1) |
+                             FIELD_PREP(HPPE_PORT5_GMAC_SEL,
+                                        HPPE_PORT5_GMAC_SEL_GMAC);
+                       break;
+               case PHY_INTERFACE_MODE_2500BASEX:
+                       val = FIELD_PREP(HPPE_PORT5_PCS_SEL,
+                                        HPPE_PORT5_PCS1);
+                       /* In-Band is only supported by XGMAC */
+                       if (!phylink_autoneg_inband(mode))
+                               val |= FIELD_PREP(HPPE_PORT5_GMAC_SEL,
+                                                 HPPE_PORT5_GMAC_SEL_GMAC);
+                       break;
+               case PHY_INTERFACE_MODE_10GBASER:
+               case PHY_INTERFACE_MODE_USXGMII:
+                       val = FIELD_PREP(HPPE_PORT5_PCS_SEL,
+                                        HPPE_PORT5_PCS1);
+                       break;
+               default:
+                       return;
+               }
+               break;
+       case 6:
+               mask = HPPE_PORT6_PCS_SEL | HPPE_PORT6_GMAC_SEL;
+               val = FIELD_PREP(HPPE_PORT6_PCS_SEL, HPPE_PORT6_PCS2);
+
+               switch (interface) {
+               case PHY_INTERFACE_MODE_SGMII:
+               case PHY_INTERFACE_MODE_1000BASEX:
+                       val |= FIELD_PREP(HPPE_PORT6_GMAC_SEL,
+                                         HPPE_PORT6_GMAC_SEL_GMAC);
+                       break;
+               case PHY_INTERFACE_MODE_2500BASEX:
+                       /* In-Band is only supported by XGMAC */
+                       if (!phylink_autoneg_inband(mode))
+                               val |= FIELD_PREP(HPPE_PORT6_GMAC_SEL,
+                                                 HPPE_PORT6_GMAC_SEL_GMAC);
+
+                       break;
+               case PHY_INTERFACE_MODE_10GBASER:
+               case PHY_INTERFACE_MODE_USXGMII:
+                       break;
+               default:
+                       return;
+               }
+               break;
+       default:
+               return;
+       }
+
+       regmap_update_bits(priv->regmap, PPE_PORT_MUX_CTRL, mask, val);
+}
+
+static void ppe_pcs_set_mux_cppe(struct qca_ppe_priv *priv, int port,
+                                unsigned int mode, phy_interface_t interface)
+{
+       u32 mask, val = 0;
+
+       switch (port) {
+       case 5:
+               mask = CPPE_PORT5_PCS_SEL | CPPE_PORT5_GMAC_SEL;
+               switch (interface) {
+               case PHY_INTERFACE_MODE_SGMII:
+               case PHY_INTERFACE_MODE_1000BASEX:
+                       val = FIELD_PREP(CPPE_PORT5_PCS_SEL,
+                                        CPPE_PORT5_PCS1_CH0);
+                       break;
+               case PHY_INTERFACE_MODE_2500BASEX:
+                       val = FIELD_PREP(CPPE_PORT5_PCS_SEL,
+                                        CPPE_PORT5_PCS1_CH0);
+                       /* In-Band is only supported by XGMAC */
+                       if (phylink_autoneg_inband(mode))
+                               val |= CPPE_PORT5_GMAC_SEL;
+                       break;
+               case PHY_INTERFACE_MODE_10GBASER:
+               case PHY_INTERFACE_MODE_USXGMII:
+                       val = FIELD_PREP(CPPE_PORT5_PCS_SEL,
+                                        CPPE_PORT5_PCS1_CH0) |
+                                        CPPE_PORT5_GMAC_SEL;
+                       break;
+               default:
+                       return;
+               }
+               break;
+       default:
+               return;
+       }
+
+       regmap_update_bits(priv->regmap, PPE_PORT_MUX_CTRL, mask, val);
+}
+
+static int qca_ppe_mac_prepare(struct phylink_config *config, unsigned int mode,
+                              phy_interface_t interface)
+{
+       struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct qca_ppe_priv *priv = ds_to_priv(dp->ds);
+       const struct ppe_data *d = priv->data;
+       int port = dp->index;
+
+       if (d->type == PPE_TYPE_IPQ8074)
+               ppe_pcs_set_mux_hppe(priv, port, mode, interface);
+       else
+               ppe_pcs_set_mux_cppe(priv, port, mode, interface);
+
+       return 0;
+}
+
+static void qca_ppe_xgmac_config(struct qca_ppe_priv *priv, int port)
+{
+       int xgmac = port - 5;
+
+       regmap_set_bits(priv->regmap, PPE_XGMAC_TX_CONF(xgmac),
+                       PPE_XGMAC_JABBER_DISABLE);
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_RX_CONF(xgmac),
+                          PPE_XGMAC_GMII_MPLS_LAYER_CK |
+                          PPE_XGMAC_WATCHDOG_DISABLE,
+                          PPE_XGMAC_GMII_MPLS_LAYER_CK);
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_PACKET_FILTER(xgmac),
+                          PPE_XGMAC_PROMISCUOUS |
+                          PPE_XGMAC_PASS_CONTROL_FRAME |
+                          PPE_XGMAC_RATE_ADAPTATION,
+                          PPE_XGMAC_PROMISCUOUS |
+                          FIELD_PREP(PPE_XGMAC_PASS_CONTROL_FRAME, 0x2) |
+                          PPE_XGMAC_RATE_ADAPTATION);
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_WATCHDOG_TIMEOUT(xgmac),
+                          PPE_XGMAC_WATCHDOG_ENABLE |
+                          PPE_XGMAC_WATCHDOG_THRESHOLD,
+                          PPE_XGMAC_WATCHDOG_ENABLE |
+                          FIELD_PREP(PPE_XGMAC_WATCHDOG_THRESHOLD, 0xb));
+
+       regmap_update_bits(priv->regmap, PPE_XGMAC_TX_FLOW_CTRL(xgmac),
+                          PPE_XGMAC_PAUSE_TIME,
+                          FIELD_PREP(PPE_XGMAC_PAUSE_TIME, 0xffff));
+}
+
+static void qca_ppe_mac_config(struct phylink_config *config,
+                                   unsigned int mode,
+                                   const struct phylink_link_state *state)
+{
+       struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct qca_ppe_priv *priv = ds_to_priv(dp->ds);
+       int port = dp->index;
+
+       if (state->interface == PHY_INTERFACE_MODE_USXGMII ||
+           state->interface == PHY_INTERFACE_MODE_10GBASER) {
+               qca_ppe_xgmac_config(priv, port);
+       }
+
+       if (priv->port_rst[port]) {
+               reset_control_assert(priv->port_rst[port]);
+               msleep(150);
+               reset_control_deassert(priv->port_rst[port]);
+       }
+}
+
+static void qca_ppe_mac_link_down(struct phylink_config *config,
+                                 unsigned int mode,
+                                 phy_interface_t interface)
+{
+       struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct qca_ppe_priv *priv = ds_to_priv(dp->ds);
+       int port = dp->index;
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               ppe_port_gmac_set(priv, port, false, false);
+               break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               if (!phylink_autoneg_inband(mode))
+                       ppe_port_gmac_set(priv, port, false, false);
+               else
+                       ppe_port_xgmac_set(priv, port, false, false);
+               break;
+       case PHY_INTERFACE_MODE_10GBASER:
+       case PHY_INTERFACE_MODE_USXGMII:
+               ppe_port_xgmac_set(priv, port, false, false);
+               break;
+       default:
+               return;
+       }
+
+       return;
+}
+
+static void qca_ppe_mac_link_up(struct phylink_config *config,
+                                    struct phy_device *phydev,
+                                    unsigned int mode,
+                                    phy_interface_t interface,
+                                    int speed, int duplex,
+                                    bool tx_pause, bool rx_pause)
+{
+       struct dsa_port *dp = dsa_phylink_to_port(config);
+       struct qca_ppe_priv *priv = ds_to_priv(dp->ds);
+       int port = dp->index;
+       unsigned long rate;
+
+       /* Invalid mode for port < 5 */
+       if ((interface == PHY_INTERFACE_MODE_2500BASEX ||
+            interface == PHY_INTERFACE_MODE_USXGMII ||
+            interface == PHY_INTERFACE_MODE_10GBASER) &&
+            port < 5)
+               return;
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               ppe_gmac_link_up(priv, port, speed, duplex,
+                                tx_pause, rx_pause);
+               break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               if (!phylink_autoneg_inband(mode))
+                       ppe_gmac_link_up(priv, port, speed, duplex,
+                                        tx_pause, rx_pause);
+               else
+                       ppe_xgmac_link_up(priv, port, interface, speed,
+                                         tx_pause, rx_pause);
+               break;
+       case PHY_INTERFACE_MODE_10GBASER:
+       case PHY_INTERFACE_MODE_USXGMII:
+               ppe_xgmac_link_up(priv, port, interface, speed,
+                                 tx_pause, rx_pause);
+               break;
+       default:
+               return;
+       }
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_2500BASEX:
+               switch (speed) {
+               case SPEED_10:
+                       rate = 2500000;
+                       break;
+               case SPEED_100:
+                       rate = 25000000;
+                       break;
+               case SPEED_1000:
+                       rate = 125000000;
+                       break;
+               case SPEED_2500:
+                       rate = 312500000;
+                       break;
+               }
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               switch (speed) {
+               case SPEED_10:
+                       rate = 1250000;
+                       break;
+               case SPEED_100:
+                       rate = 12500000;
+                       break;
+               case SPEED_1000:
+                       rate = 125000000;
+                       break;
+               case SPEED_2500:
+                       rate = 78125000;
+                       break;
+               case SPEED_5000:
+                       rate = 156250000;
+                       break;
+               case SPEED_10000:
+                       rate = 312500000;
+                       break;
+               }
+               break;
+       default:
+               rate = 125000000;
+               break;
+       }
+
+       if (priv->port_rx_clk[port])
+               clk_set_rate(priv->port_rx_clk[port], rate);
+       if (priv->port_tx_clk[port])
+               clk_set_rate(priv->port_tx_clk[port], rate);
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               ppe_port_gmac_set(priv, port, true, true);
+               break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               if (!phylink_autoneg_inband(mode))
+                       ppe_port_gmac_set(priv, port, true, true);
+               else
+                       ppe_port_xgmac_set(priv, port, true, true);
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               ppe_port_xgmac_set(priv, port, true, true);
+               break;
+       default:
+               return;
+       }
+}
+
+static const struct phylink_mac_ops qca_ppe_phylink_mac_ops = {
+       .mac_prepare    = qca_ppe_mac_prepare,
+       .mac_config     = qca_ppe_mac_config,
+       .mac_link_down  = qca_ppe_mac_link_down,
+       .mac_link_up    = qca_ppe_mac_link_up,
+};
+
+struct qca_ppe_mib_desc {
+       unsigned int offset;
+       unsigned int size;
+       const char name[ETH_GSTRING_LEN];
+};
+
+#define MIB32(_off, _name)     { .offset = (_off), .size = 1, .name = _name }
+#define MIB64(_off, _name)     { .offset = (_off), .size = 2, .name = _name }
+
+static const struct qca_ppe_mib_desc qca_ppe_mib[] = {
+       MIB32(PPE_MIB_RXBROAD,          "rx_broadcast"),
+       MIB32(PPE_MIB_RXPAUSE,          "rx_pause"),
+       MIB32(PPE_MIB_RXMULTI,          "rx_multicast"),
+       MIB32(PPE_MIB_RXFCSERR,         "rx_fcs_error"),
+       MIB32(PPE_MIB_RXALIGNERR,       "rx_align_error"),
+       MIB32(PPE_MIB_RXRUNT,           "rx_runt"),
+       MIB32(PPE_MIB_RXFRAG,           "rx_fragment"),
+       MIB32(PPE_MIB_RXJUMBOFCSERR,    "rx_jumbo_fcs_error"),
+       MIB32(PPE_MIB_RXJUMBOALIGNERR,  "rx_jumbo_align_error"),
+       MIB32(PPE_MIB_RXPKT64,          "rx_64byte"),
+       MIB32(PPE_MIB_RXPKT65TO127,     "rx_65_127byte"),
+       MIB32(PPE_MIB_RXPKT128TO255,    "rx_128_255byte"),
+       MIB32(PPE_MIB_RXPKT256TO511,    "rx_256_511byte"),
+       MIB32(PPE_MIB_RXPKT512TO1023,   "rx_512_1023byte"),
+       MIB32(PPE_MIB_RXPKT1024TO1518,  "rx_1024_1518byte"),
+       MIB32(PPE_MIB_RXPKT1519TOX,     "rx_1519_maxbyte"),
+       MIB32(PPE_MIB_RXTOOLONG,        "rx_too_long"),
+       MIB64(PPE_MIB_RXGOODBYTE_L,     "rx_good_bytes"),
+       MIB64(PPE_MIB_RXBADBYTE_L,      "rx_bad_bytes"),
+       MIB32(PPE_MIB_RXUNI,            "rx_unicast"),
+       MIB32(PPE_MIB_TXBROAD,          "tx_broadcast"),
+       MIB32(PPE_MIB_TXPAUSE,          "tx_pause"),
+       MIB32(PPE_MIB_TXMULTI,          "tx_multicast"),
+       MIB32(PPE_MIB_TXUNDERRUN,       "tx_underrun"),
+       MIB32(PPE_MIB_TXPKT64,          "tx_64byte"),
+       MIB32(PPE_MIB_TXPKT65TO127,     "tx_65_127byte"),
+       MIB32(PPE_MIB_TXPKT128TO255,    "tx_128_255byte"),
+       MIB32(PPE_MIB_TXPKT256TO511,    "tx_256_511byte"),
+       MIB32(PPE_MIB_TXPKT512TO1023,   "tx_512_1023byte"),
+       MIB32(PPE_MIB_TXPKT1024TO1518,  "tx_1024_1518byte"),
+       MIB32(PPE_MIB_TXPKT1519TOX,     "tx_1519_maxbyte"),
+       MIB64(PPE_MIB_TXBYTE_L,         "tx_bytes"),
+       MIB32(PPE_MIB_TXCOLLISIONS,     "tx_collisions"),
+       MIB32(PPE_MIB_TXABORTCOL,       "tx_abort_collision"),
+       MIB32(PPE_MIB_TXMULTICOL,       "tx_multi_collision"),
+       MIB32(PPE_MIB_TXSINGLECOL,      "tx_single_collision"),
+       MIB32(PPE_MIB_TXEXCESSIVEDEFER, "tx_excessive_defer"),
+       MIB32(PPE_MIB_TXDEFER,          "tx_defer"),
+       MIB32(PPE_MIB_TXLATECOL,        "tx_late_collision"),
+       MIB32(PPE_MIB_TXUNI,            "tx_unicast"),
+};
+
+static void qca_ppe_get_strings(struct dsa_switch *ds, int port,
+                                   u32 stringset, uint8_t *data)
+{
+       int i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(qca_ppe_mib); i++)
+               ethtool_puts(&data, qca_ppe_mib[i].name);
+}
+
+static int qca_ppe_get_sset_count(struct dsa_switch *ds, int port,
+                                     int sset)
+{
+       if (sset != ETH_SS_STATS)
+               return 0;
+
+       return ARRAY_SIZE(qca_ppe_mib);
+}
+
+static void qca_ppe_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                         uint64_t *data)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       int gmac = port - 1;
+       int i;
+
+       if (port < 1 || port >= ds->num_ports) {
+               memset(data, 0, sizeof(u64) * ARRAY_SIZE(qca_ppe_mib));
+               return;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(qca_ppe_mib); i++) {
+               const struct qca_ppe_mib_desc *mib = &qca_ppe_mib[i];
+               u32 val, hi;
+
+               regmap_read(priv->regmap, PPE_GMAC_MIB(gmac, mib->offset), &val);
+               if (mib->size == 2)
+                       regmap_read(priv->regmap,
+                                   PPE_GMAC_MIB(gmac, mib->offset + 4), &hi);
+
+               data[i] = val;
+               if (mib->size == 2)
+                       data[i] |= (u64)hi << 32;
+       }
+}
+
+static void qca_ppe_port_stp_state_set(struct dsa_switch *ds, int port,
+                                          u8 state)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       u32 stp_state;
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+               stp_state = PPE_STP_DISABLED;
+               break;
+       case BR_STATE_BLOCKING:
+       case BR_STATE_LISTENING:
+               stp_state = PPE_STP_BLOCKING;
+               break;
+       case BR_STATE_LEARNING:
+               stp_state = PPE_STP_LEARNING;
+               break;
+       case BR_STATE_FORWARDING:
+       default:
+               stp_state = PPE_STP_FORWARDING;
+               break;
+       }
+
+       regmap_update_bits(priv->regmap, PPE_CST_STATE(port),
+                          PPE_STP_STATE_MASK, stp_state);
+}
+
+static const struct dsa_switch_ops qca_ppe_ops = {
+       .get_tag_protocol       = qca_ppe_get_tag_protocol,
+       .setup                  = qca_ppe_setup,
+       .set_ageing_time        = qca_ppe_set_ageing_time,
+       .port_enable            = qca_ppe_port_enable,
+       .port_disable           = qca_ppe_port_disable,
+       .port_stp_state_set     = qca_ppe_port_stp_state_set,
+       .port_bridge_join       = qca_ppe_port_bridge_join,
+       .port_bridge_leave      = qca_ppe_port_bridge_leave,
+       .port_fdb_add           = qca_ppe_port_fdb_add,
+       .port_fdb_del           = qca_ppe_port_fdb_del,
+       .port_fdb_dump          = qca_ppe_port_fdb_dump,
+       .port_mdb_add           = qca_ppe_port_mdb_add,
+       .port_mdb_del           = qca_ppe_port_mdb_del,
+       .phylink_get_caps       = qca_ppe_phylink_get_caps,
+       .port_vlan_filtering    = qca_ppe_port_vlan_filtering,
+       .port_vlan_add          = qca_ppe_port_vlan_add,
+       .port_vlan_del          = qca_ppe_port_vlan_del,
+       .get_strings            = qca_ppe_get_strings,
+       .get_sset_count         = qca_ppe_get_sset_count,
+       .get_ethtool_stats      = qca_ppe_get_ethtool_stats,
+};
+
+static void ppe_vsi_init(struct qca_ppe_priv *priv)
+{
+       int i;
+
+       /* All three words must be written back for the HW to latch the entry */
+       for (i = 1; i < priv->data->num_ports; i++) {
+               u32 val[3];
+
+               regmap_read(priv->regmap, PPE_L3_VP_PORT_TBL(i), &val[0]);
+               regmap_read(priv->regmap, PPE_L3_VP_PORT_TBL(i) + 4, &val[1]);
+               regmap_read(priv->regmap, PPE_L3_VP_PORT_TBL(i) + 8, &val[2]);
+
+               val[1] &= ~(PPE_L3_VP_VSI_VALID | PPE_L3_VP_VSI);
+               val[1] |= PPE_L3_VP_VSI_VALID;
+
+               regmap_write(priv->regmap, PPE_L3_VP_PORT_TBL(i), val[0]);
+               regmap_write(priv->regmap, PPE_L3_VP_PORT_TBL(i) + 4, val[1]);
+               regmap_write(priv->regmap, PPE_L3_VP_PORT_TBL(i) + 8, val[2]);
+       }
+}
+
+static void ppe_mac_hw_init(struct qca_ppe_priv *priv)
+{
+       const struct ppe_data *d = priv->data;
+       int lpbk_gmac = d->loopback_port - 1;
+       int gmac;
+
+       for (gmac = 0; gmac < d->num_gmacs; gmac++) {
+               regmap_update_bits(priv->regmap, PPE_GMAC_CTRL2(gmac),
+                                  PPE_GMAC_CTRL2_MAXFR | PPE_GMAC_CTRL2_CRS_SEL |
+                                  PPE_GMAC_CTRL2_TX_THD,
+                                  FIELD_PREP(PPE_GMAC_CTRL2_MAXFR, PPE_MAX_FRAME_SIZE) |
+                                  FIELD_PREP(PPE_GMAC_CTRL2_TX_THD, 1));
+
+               regmap_update_bits(priv->regmap, PPE_GMAC_DBG_CTRL(gmac),
+                                  PPE_GMAC_DBG_CTRL_HIHG_IPG,
+                                  FIELD_PREP(PPE_GMAC_DBG_CTRL_HIHG_IPG, 0xc));
+
+               regmap_write(priv->regmap, PPE_GMAC_JUMBO_SIZE(gmac),
+                            PPE_MAX_FRAME_SIZE);
+       }
+
+       regmap_update_bits(priv->regmap, PPE_LPBK_PPS_CTRL(lpbk_gmac),
+                          PPE_LPBK_PPS_THRESHOLD,
+                          FIELD_PREP(PPE_LPBK_PPS_THRESHOLD, 21));
+       regmap_write(priv->regmap, PPE_LPBK_ENABLE(lpbk_gmac),
+               PPE_LPBK_EN | PPE_LPBK_CRC_STRIP_EN);
+       msleep(100);
+       ppe_port_bridge_txmac_set(priv, d->loopback_port, true);
+}
+
+static void ppe_ctrlpkt_init(struct qca_ppe_priv *priv)
+{
+       /* RFDB_TBL[31]: STP multicast MAC 01:80:c2:00:00:00 */
+       regmap_write(priv->regmap, PPE_RFDB_TBL(31), 0xc2000000);
+       regmap_write(priv->regmap, PPE_RFDB_TBL(31) + 4, 0x00010180);
+
+       /* APP_CTRL[0]: match RFDB profile 31, bypass STP, redirect to CPU */
+       regmap_write(priv->regmap, PPE_APP_CTRL(0), 0x00000003);
+       regmap_write(priv->regmap, PPE_APP_CTRL(0) + 4, 0x00000002);
+       regmap_write(priv->regmap, PPE_APP_CTRL(0) + 8, 0x000093fc);
+}
+
+static int ppe_ipq6018_mux_setup(struct qca_ppe_priv *priv)
+{
+       struct device_node *ports_np, *port_np;
+       struct of_phandle_args pcs_args;
+       int port3_ch = -1;
+       u32 port;
+       int ret;
+
+       ports_np = of_get_child_by_name(priv->ds.dev->of_node, "ports");
+       if (!ports_np)
+               return -ENODEV;
+
+       for_each_available_child_of_node(ports_np, port_np) {
+               ret = of_property_read_u32(port_np, "reg", &port);
+               if (ret)
+                       continue;
+
+               if (port != 3)
+                       continue;
+
+               ret = of_parse_phandle_with_args(port_np, "pcs-handle",
+                                                "#pcs-cells", 0, &pcs_args);
+               if (ret)
+                       continue;
+
+               port3_ch = pcs_args.args[0];
+       }
+
+       of_node_put(ports_np);
+
+       /* FIXME: better investigate this */
+       if (port3_ch == 4)
+               regmap_update_bits(priv->regmap, PPE_PORT_MUX_CTRL,
+                                  CPPE_PORT3_PCS_SEL | CPPE_PCS0_CH4_SEL,
+                                  FIELD_PREP(CPPE_PORT3_PCS_SEL,
+                                             CPPE_PORT3_PCS0_CH4) |
+                                  CPPE_PCS0_CH4_SEL);
+
+       return 0;
+}
+
+static const struct regmap_config ppe_regmap_cfg = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+};
+
+static int qca_ppe_probe(struct platform_device *pdev)
+{
+       const struct ppe_data *data;
+       struct device_node *ports;
+       struct qca_ppe_priv *priv;
+       struct reset_control *rst;
+       struct dsa_switch *ds;
+       void __iomem *base;
+       int ret, i;
+
+       data = of_device_get_match_data(&pdev->dev);
+       if (!data)
+               return -ENODEV;
+
+       ports = of_get_child_by_name(pdev->dev.of_node, "ports");
+       if (!ports)
+               return -ENODEV;
+       of_node_put(ports);
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->data = data;
+
+       priv->num_clks = devm_clk_bulk_get_all(&pdev->dev, &priv->clks);
+       if (priv->num_clks < 0)
+               return priv->num_clks;
+
+       ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+       if (ret)
+               return ret;
+
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return dev_err_probe(&pdev->dev, PTR_ERR(base), "failed to ioremap resource");
+
+       priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &ppe_regmap_cfg);
+       if (IS_ERR(priv->regmap))
+               return dev_err_probe(&pdev->dev, PTR_ERR(priv->regmap), "failed to init regmap");
+
+       rst = devm_reset_control_get(&pdev->dev, "ppe_rst");
+       if (IS_ERR(rst)) {
+               ret = PTR_ERR(rst);
+               goto err_clk;
+       }
+       reset_control_assert(rst);
+       msleep(100);
+       reset_control_deassert(rst);
+       msleep(100);
+
+       spin_lock_init(&priv->fdb_lock);
+
+       ds = &priv->ds;
+       ds->dev = &pdev->dev;
+       ds->num_ports = data->num_ports;
+       ds->ops = &qca_ppe_ops;
+       ds->priv = priv;
+       ds->phylink_mac_ops = &qca_ppe_phylink_mac_ops;
+
+       for (i = 1; i < data->num_ports; i++) {
+               char name[32];
+
+               snprintf(name, sizeof(name), "port%d_rx", i);
+               priv->port_rx_clk[i] = devm_clk_get_optional(&pdev->dev, name);
+               if (IS_ERR(priv->port_rx_clk[i])) {
+                       ret = PTR_ERR(priv->port_rx_clk[i]);
+                       goto err_clk;
+               }
+
+               snprintf(name, sizeof(name), "port%d_tx", i);
+               priv->port_tx_clk[i] = devm_clk_get_optional(&pdev->dev, name);
+               if (IS_ERR(priv->port_tx_clk[i])) {
+                       ret = PTR_ERR(priv->port_tx_clk[i]);
+                       goto err_clk;
+               }
+
+               snprintf(name, sizeof(name), "nss_port%d_rst", i);
+               priv->port_rst[i] = devm_reset_control_get_optional_exclusive(
+                                               &pdev->dev, name);
+               if (IS_ERR(priv->port_rst[i])) {
+                       ret = PTR_ERR(priv->port_rst[i]);
+                       goto err_clk;
+               }
+       }
+
+       ppe_vsi_init(priv);
+
+       ppe_scheduler_init(priv);
+
+       ppe_mac_hw_init(priv);
+       ppe_ctrlpkt_init(priv);
+
+
+       if (data->type == PPE_TYPE_IPQ6018) {
+               ret = ppe_ipq6018_mux_setup(priv);
+               if (ret)
+                       goto err_clk;
+       }
+
+       ret = dsa_register_switch(ds);
+       if (ret)
+               goto err_clk;
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+
+err_clk:
+       clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+       return ret;
+}
+
+static void qca_ppe_remove(struct platform_device *pdev)
+{
+       struct qca_ppe_priv *priv = platform_get_drvdata(pdev);
+
+       dsa_unregister_switch(&priv->ds);
+       clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+}
+
+static const struct ppe_data ipq6018_ppe_data = {
+       .type                   = PPE_TYPE_IPQ6018,
+       .num_ports              = 7,
+       .num_gmacs              = 5,
+       .loopback_port          = 6,
+       .bm_phy_end             = 12,
+       .bm_internal_start      = 13,
+       .bm_group_buf           = 1024,
+       .bm_ceiling             = 216,
+       .qm_total_buf           = 1506,
+       .qm_ceiling             = 216,
+       .qm_green_max           = 144,
+       .psch_tdm               = &cppe_psch_tdm_data,
+       .bm_tdm                 = &cppe_bm_tdm_data,
+};
+
+static const struct ppe_data ipq8074_ppe_data = {
+       .type                   = PPE_TYPE_IPQ8074,
+       .num_ports              = 8,
+       .num_gmacs              = 6,
+       .loopback_port          = 7,
+       .bm_phy_end             = 13,
+       .bm_internal_start      = 14,
+       .bm_group_buf           = 1400,
+       .bm_ceiling             = 250,
+       .qm_total_buf           = 2000,
+       .qm_ceiling             = 400,
+       .qm_green_max           = 250,
+       .psch_tdm               = &hppe_psch_tdm_data,
+       .bm_tdm                 = &hppe_bm_tdm_data,
+};
+
+static const struct of_device_id qca_ppe_of_match[] = {
+       { .compatible = "qualcomm,ipq6018-ppe", .data = &ipq6018_ppe_data },
+       { .compatible = "qualcomm,ipq8074-ppe", .data = &ipq8074_ppe_data },
+       {},
+};
+MODULE_DEVICE_TABLE(of, qca_ppe_of_match);
+
+static struct platform_driver qca_ppe_driver = {
+       .driver = {
+               .name = "qca-ppe",
+               .of_match_table = qca_ppe_of_match,
+       },
+       .probe = qca_ppe_probe,
+       .remove = qca_ppe_remove,
+};
+module_platform_driver(qca_ppe_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for Qualcomm PPE switches");
diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c
new file mode 100644 (file)
index 0000000..481f343
--- /dev/null
@@ -0,0 +1,781 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+#include "qca_ppe.h"
+
+struct psch_tdm_entry {
+       u8 en_port;
+       u8 de_port;
+};
+
+struct bm_tdm_entry {
+       u8 port;
+       u8 dir;
+};
+
+enum psch_tdm_port {
+       TDM_PORT_CPU = 0,
+       TDM_PORT_PHY_1,
+       TDM_PORT_PHY_2,
+       TDM_PORT_PHY_3,
+       TDM_PORT_PHY_4,
+       TDM_PORT_FAB_0,
+       TDM_PORT_FAB_1,
+       TDM_PORT_PHY_7,
+};
+
+enum bm_tdm_dir {
+       TDM_DIR_INGRESS = 0,
+       TDM_DIR_EGRESS,
+};
+
+/* CPPE (IPQ60xx) port scheduler TDM -- 50 entries */
+const struct psch_tdm_entry cppe_psch_tdm[] = {
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_PHY_3, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_FAB_1 },
+       { TDM_PORT_PHY_1, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_4 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_7 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_7, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_2 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_PHY_2, TDM_PORT_FAB_0 },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_4 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_7 },
+       { TDM_PORT_FAB_1, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_7, TDM_PORT_CPU },
+       { TDM_PORT_FAB_0, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_3 },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_4 },
+       { TDM_PORT_PHY_3, TDM_PORT_CPU },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_PHY_1, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_7 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_PHY_7, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_4 },
+       { TDM_PORT_FAB_1, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_2 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_2, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_7 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_4 },
+       { TDM_PORT_PHY_7, TDM_PORT_FAB_1 },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_0 },
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_FAB_0, TDM_PORT_PHY_3 },
+};
+
+/* HPPE (IPQ807x) port scheduler TDM -- 50 entries
+ * Source: ssdk_hppe.c port_schedulerTDM_PORT_CPU_tbl[] */
+const struct psch_tdm_entry hppe_psch_tdm[] = {
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_7 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_7, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_1 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_1, TDM_PORT_CPU },
+       { TDM_PORT_FAB_0, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_2 },
+       { TDM_PORT_FAB_1, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_2, TDM_PORT_CPU },
+       { TDM_PORT_FAB_0, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_7 },
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_PHY_7, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_PHY_3 },
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_PHY_3, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_4 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_4, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_CPU },
+       { TDM_PORT_FAB_1, TDM_PORT_PHY_7 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_7, TDM_PORT_CPU },
+       { TDM_PORT_FAB_0, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_1 },
+       { TDM_PORT_FAB_1, TDM_PORT_FAB_0 },
+       { TDM_PORT_PHY_1, TDM_PORT_CPU },
+       { TDM_PORT_FAB_0, TDM_PORT_FAB_1 },
+       { TDM_PORT_CPU, TDM_PORT_PHY_2 },
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_PHY_2, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_PHY_7 },
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_PHY_7, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_PHY_3 },
+       { TDM_PORT_FAB_1, TDM_PORT_CPU },
+       { TDM_PORT_PHY_3, TDM_PORT_FAB_0 },
+       { TDM_PORT_CPU, TDM_PORT_FAB_1 },
+       { TDM_PORT_FAB_0, TDM_PORT_PHY_4 },
+};
+
+/* CPPE buffer manager TDM -- 98 entries */
+const struct bm_tdm_entry cppe_bm_tdm[] = {
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_2, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_3, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_1, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_2, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_3, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+};
+
+/* HPPE buffer manager TDM -- 96 entries
+ * Source: ssdk_hppe.c port_tdmTDM_PORT_CPU_tbl[] */
+const struct bm_tdm_entry hppe_bm_tdm[] = {
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_3, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_2, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_4, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_3, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_2, TDM_DIR_EGRESS },
+       { TDM_PORT_CPU, TDM_DIR_INGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_0, TDM_DIR_EGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_INGRESS },
+       { TDM_PORT_CPU, TDM_DIR_EGRESS },
+       { TDM_PORT_PHY_7, TDM_DIR_INGRESS },
+       { TDM_PORT_FAB_1, TDM_DIR_EGRESS },
+};
+
+static void ppe_tdm_init(struct qca_ppe_priv *priv)
+{
+       const struct ppe_data *data = priv->data;
+       const struct psch_tdm_entry *psch;
+       const struct bm_tdm_entry *bm;
+       int psch_num, bm_num;
+       u8 prev_de_port;
+       int i;
+
+       psch = data->psch_tdm->entries;
+       psch_num = data->psch_tdm->num;
+
+       bm = data->bm_tdm->entries;
+       bm_num = data->bm_tdm->num;
+
+       /*
+        * The port scheduler TDM is circular with the bitmap
+        * composed of the
+        * NOT (dequeue port (de_port) OR the previous dequeue port)
+        * Each bit correspond to a port from 0 to 7.
+        *
+        * For the first element, we refer to the last dequeue port
+        * (since it's circular).
+        *
+        * Taking an example for the first element:
+        * We dequeue port 6 and the last element dequeue port 3
+        * So the bitmap will be ~(BIT(6) | BIT(3)) = 0xb7
+        *
+        * Example for the second element:
+        * We dequeque port 0 and previously we dequeued port 6
+        * So the bitmap will be ~(BIT(0) | BIT(6)) = 0xbe
+        */
+       prev_de_port = psch[psch_num - 1].de_port;
+       for (i = 0; i < psch_num; i++) {
+               u8 bmp = ~(BIT(prev_de_port) | BIT(psch[i].de_port));
+
+               regmap_write(priv->regmap, PPE_TM_PSCH_TDM(i),
+                            FIELD_PREP(PPE_PSCH_ENS_PORT_BMP, bmp) |
+                            FIELD_PREP(PPE_PSCH_ENS_PORT, psch[i].en_port) |
+                            FIELD_PREP(PPE_PSCH_DES_PORT, psch[i].de_port));
+
+               prev_de_port = BIT(psch[i].de_port);
+       }
+
+       regmap_write(priv->regmap, PPE_TM_TDM_DEPTH,
+                    FIELD_PREP(PPE_TM_TDM_DEPTH_MASK, psch_num));
+
+       for (i = 0; i < bm_num; i++)
+               regmap_write(priv->regmap, PPE_PRX_TDM_CFG(i),
+                            FIELD_PREP(PPE_TDM_PORT_NUM, bm[i].port) |
+                            FIELD_PREP(PPE_TDM_DIR, bm[i].dir) |
+                            PPE_TDM_VALID);
+
+       regmap_write(priv->regmap, PPE_PRX_TDM_CTRL,
+                    FIELD_PREP(PPE_TDM_DEPTH, bm_num) |
+                    PPE_TDM_EN);
+}
+
+static void ppe_bm_init(struct qca_ppe_priv *priv)
+{
+       const struct ppe_data *d = priv->data;
+       int i;
+
+       for (i = 0; i < PPE_BM_PORTS; i++) {
+               bool fc_en = (i < PPE_BM_PHY_START || i > d->bm_phy_end);
+
+               regmap_write(priv->regmap, PPE_BM_FC_MODE(i),
+                            fc_en ? PPE_BM_FC_EN : 0);
+               regmap_write(priv->regmap, PPE_BM_GROUP_ID(i), 0);
+       }
+
+       regmap_write(priv->regmap, PPE_BM_SHARED_GRP(0),
+                    FIELD_PREP(PPE_BM_SHARED_LIMIT, d->bm_group_buf));
+
+       for (i = 0; i < PPE_BM_PORTS; i++) {
+               u16 react;
+               u32 w0, w1;
+
+               if (i < PPE_BM_PHY_START)
+                       react = 100;
+               else if (i >= d->bm_internal_start)
+                       react = 40;
+               else
+                       react = 128;
+
+               w0 = FIELD_PREP(PPE_BM_REACT_LIMIT, react) |
+                    FIELD_PREP(PPE_BM_RESUME_OFF, 36) |
+                    FIELD_PREP(PPE_BM_CEILING_LO, d->bm_ceiling & 0x7);
+               w1 = FIELD_PREP(PPE_BM_CEILING_HI, d->bm_ceiling >> 3) |
+                    FIELD_PREP(PPE_BM_WEIGHT, 4) |
+                    PPE_BM_DYNAMIC;
+
+               regmap_write(priv->regmap, PPE_BM_PORT_FC_W0(i), w0);
+               regmap_write(priv->regmap, PPE_BM_PORT_FC_W1(i), w1);
+       }
+}
+
+static void ppe_qm_map_set(struct qca_ppe_priv *priv, u32 index,
+                           u8 queue_base, u8 profile)
+{
+       regmap_write(priv->regmap, PPE_QM_UCAST_MAP(index),
+                    FIELD_PREP(PPE_QM_PROFILE_ID, profile) |
+                    FIELD_PREP(PPE_QM_QUEUE_ID, queue_base));
+}
+
+static const u8 port_queue_base[PPE_NUM_PORTS] = {
+       0, 144, 160, 176, 192, 208, 224, 240,
+};
+
+static const u8 port_l0_cdrr_num[PPE_NUM_PORTS] = {
+       48, 16, 16, 16, 16, 16, 16, 16,
+};
+
+static void ppe_qm_init(struct qca_ppe_priv *priv)
+{
+       const struct ppe_data *d = priv->data;
+       int i, pri;
+
+       ppe_qm_map_set(priv, QM_SERVICE_CODE_OFFSET + 2, 8, 0);
+       ppe_qm_map_set(priv, QM_SERVICE_CODE_OFFSET + 3, 128, 8);
+       ppe_qm_map_set(priv, QM_SERVICE_CODE_OFFSET + 4, 128, 8);
+       ppe_qm_map_set(priv, QM_SERVICE_CODE_OFFSET + 5, 0, 0);
+       ppe_qm_map_set(priv, QM_SERVICE_CODE_OFFSET + 6, 8, 0);
+       ppe_qm_map_set(priv, QM_SERVICE_CODE_OFFSET + 7, 240, 0);
+
+       for (i = 0; i < PPE_NUM_PORTS; i++)
+               ppe_qm_map_set(priv, QM_VP_PORT_OFFSET + i,
+                               port_queue_base[i], i);
+
+       for (i = 0; i < PPE_NUM_PORTS; i++) {
+               u8 max_pri = port_l0_cdrr_num[i];
+               u8 profile;
+
+               if (max_pri > 16)
+                       max_pri = 1;
+
+               for (pri = 0; pri < 16; pri++) {
+                       u8 cls = (pri >= max_pri) ? max_pri - 1 : pri;
+
+                       if (i == 0) {
+                               profile = 0;
+                               regmap_write(priv->regmap,
+                                            PPE_QM_UCAST_PRI_MAP(profile * 16 + pri),
+                                            FIELD_PREP(PPE_QM_PRI_CLASS, cls));
+                               profile = 15;
+                               regmap_write(priv->regmap,
+                                            PPE_QM_UCAST_PRI_MAP(profile * 16 + pri),
+                                            FIELD_PREP(PPE_QM_PRI_CLASS, cls));
+                       } else {
+                               regmap_write(priv->regmap,
+                                            PPE_QM_UCAST_PRI_MAP(i * 16 + pri),
+                                            FIELD_PREP(PPE_QM_PRI_CLASS, cls));
+                       }
+               }
+       }
+
+       for (i = 0; i < 256; i++) {
+               regmap_write(priv->regmap, PPE_QM_UCAST_HASH_MAP(15 * 256 + i), 0);
+               regmap_write(priv->regmap, PPE_QM_UCAST_HASH_MAP(14 * 256 + i), 0);
+       }
+
+       ppe_qm_map_set(priv, QM_CPU_CODE_OFFSET + 101,
+                       port_queue_base[0] + 0, 0);
+
+       for (i = 0; i < PPE_MAX_SERVICE_CODES; i++) {
+               u32 idx = QM_SERVICE_CODE_OFFSET + (1 << 8) + i;
+
+               if (i == 2 || i == 6)
+                       ppe_qm_map_set(priv, idx, 8, 0);
+               else if (i == 3 || i == 4)
+                       ppe_qm_map_set(priv, idx, 128, 8);
+               else
+                       ppe_qm_map_set(priv, idx, 4, 0);
+       }
+
+       for (i = 0; i < PPE_MAX_CPU_CODES; i++)
+               ppe_qm_map_set(priv, QM_CPU_CODE_OFFSET + (1 << 8) + i, 4, 0);
+
+       for (i = 0; i < PPE_NUM_PORTS; i++)
+               ppe_qm_map_set(priv, QM_VP_PORT_OFFSET + (1 << 8) + i,
+                               port_queue_base[i], i);
+
+       for (i = PPE_NUM_PORTS; i < PPE_MAX_VPORT; i++)
+               ppe_qm_map_set(priv, QM_VP_PORT_OFFSET + (1 << 8) + i, 4, 0);
+
+       for (i = 0; i < PPE_L0_UCAST_QUEUES; i++) {
+               regmap_write(priv->regmap, PPE_QM_AC_UNI_W0(i),
+                            PPE_AC_EN |
+                            PPE_AC_SHARED_DYNAMIC |
+                            FIELD_PREP(PPE_AC_SHARED_WEIGHT, 4) |
+                            FIELD_PREP(PPE_AC_SHARED_CEILING, d->qm_ceiling));
+               regmap_write(priv->regmap, PPE_QM_AC_UNI_W1(i), 0);
+               regmap_write(priv->regmap, PPE_QM_AC_UNI_W2(i), 0);
+               regmap_write(priv->regmap, PPE_QM_AC_UNI_W3(i),
+                            FIELD_PREP(PPE_AC_GRN_RESUME_OFF, 36));
+       }
+
+       for (i = 0; i < PPE_L0_QUEUES - PPE_L0_UCAST_QUEUES; i++) {
+               regmap_write(priv->regmap, PPE_QM_AC_MUL_W0(i),
+                            PPE_AC_MUL_EN |
+                            FIELD_PREP(PPE_AC_MUL_CEILING, d->qm_ceiling) |
+                            FIELD_PREP(PPE_AC_MUL_GRN_MAX_LO, d->qm_green_max & 0x1f));
+               regmap_write(priv->regmap, PPE_QM_AC_MUL_W1(i),
+                            FIELD_PREP(PPE_AC_MUL_GRN_MAX_HI, d->qm_green_max >> 5));
+               regmap_write(priv->regmap, PPE_QM_AC_MUL_W2(i),
+                            FIELD_PREP(PPE_AC_MUL_GRN_RESUME_HI, 36));
+       }
+
+       regmap_write(priv->regmap, PPE_QM_AC_GRP_W0(0), 0);
+       regmap_write(priv->regmap, PPE_QM_AC_GRP_W1(0),
+                    FIELD_PREP(PPE_AC_GRP_LIMIT, d->qm_total_buf));
+       regmap_write(priv->regmap, PPE_QM_AC_GRP_W2(0), 0);
+
+       regmap_update_bits(priv->regmap, PPE_EG_BRIDGE_CONFIG,
+                          PPE_EG_QUEUE_CNT_EN, PPE_EG_QUEUE_CNT_EN);
+}
+
+struct l1_cfg {
+       u8 index;
+       u8 port;
+       u8 pri;
+       u8 drr;
+};
+
+static const struct l1_cfg l1_cfg[] = {
+       {  0, 0, 0,  0 },
+       {  1, 0, 0,  0 },
+       { 36, 1, 0,  8 },
+       { 37, 1, 1,  9 },
+       { 40, 2, 0, 12 },
+       { 41, 2, 1, 13 },
+       { 44, 3, 0, 16 },
+       { 45, 3, 1, 17 },
+       { 48, 4, 0, 20 },
+       { 49, 4, 1, 21 },
+       { 52, 5, 0, 24 },
+       { 53, 5, 1, 25 },
+       { 56, 6, 0, 28 },
+       { 57, 6, 1, 29 },
+       { 60, 7, 0, 32 },
+       { 61, 7, 1, 33 },
+};
+
+static void ppe_l1_scheduler_init(struct qca_ppe_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(l1_cfg); i++) {
+               const struct l1_cfg *c = &l1_cfg[i];
+               u32 sp_idx;
+
+               regmap_write(priv->regmap, PPE_TM_L1_FLOW_MAP(c->index),
+                            FIELD_PREP(PPE_L1_SP_ID, c->port) |
+                            FIELD_PREP(PPE_L1_C_PRI, c->pri) |
+                            FIELD_PREP(PPE_L1_E_PRI, c->pri) |
+                            FIELD_PREP(PPE_L1_C_DRR_WT, 1) |
+                            FIELD_PREP(PPE_L1_E_DRR_WT, 1));
+
+               sp_idx = c->port * 8 + c->pri;
+               regmap_write(priv->regmap, PPE_TM_L1_C_SP(sp_idx),
+                            FIELD_PREP(PPE_L1_SP_DRR_ID, c->drr));
+
+               regmap_write(priv->regmap, PPE_TM_L1_E_SP(sp_idx),
+                            FIELD_PREP(PPE_L1_SP_DRR_ID, c->drr));
+
+               regmap_write(priv->regmap, PPE_TM_L1_PORT_MAP(c->index),
+                            FIELD_PREP(PPE_L1_PORT_NUM, c->port));
+       }
+}
+
+struct l0_cfg {
+       u16 queue;
+       u8 port;
+       u8 sp;
+       u8 cpri;
+       u8 cdrr;
+       u8 epri;
+       u8 edrr;
+};
+
+static const struct l0_cfg l0_port0[] = {
+       {   0, 0, 0, 0, 0, 0, 0 }, {   4, 0, 0, 0, 0, 0, 0 },
+       {   8, 0, 0, 0, 0, 0, 0 }, { 256, 0, 0, 0, 0, 0, 0 },
+       { 260, 0, 0, 0, 0, 0, 0 },
+       {   1, 0, 0, 1, 1, 1, 1 }, {   5, 0, 0, 1, 1, 1, 1 },
+       {   9, 0, 0, 1, 1, 1, 1 }, { 257, 0, 0, 1, 1, 1, 1 },
+       { 261, 0, 0, 1, 1, 1, 1 },
+       {   2, 0, 0, 2, 2, 2, 2 }, {   6, 0, 0, 2, 2, 2, 2 },
+       {  10, 0, 0, 2, 2, 2, 2 }, { 258, 0, 0, 2, 2, 2, 2 },
+       { 262, 0, 0, 2, 2, 2, 2 },
+       {   3, 0, 0, 3, 3, 3, 3 }, {   7, 0, 0, 3, 3, 3, 3 },
+       {  11, 0, 0, 3, 3, 3, 3 }, { 259, 0, 0, 3, 3, 3, 3 },
+       { 263, 0, 0, 3, 3, 3, 3 },
+};
+
+static void ppe_l0_entry_write(struct qca_ppe_priv *priv, const struct l0_cfg *c)
+{
+       u32 sp_idx;
+
+       regmap_write(priv->regmap, PPE_TM_L0_FLOW_MAP(c->queue),
+                    FIELD_PREP(PPE_L0_SP_ID, c->sp) |
+                    FIELD_PREP(PPE_L0_C_PRI, c->cpri) |
+                    FIELD_PREP(PPE_L0_E_PRI, c->epri) |
+                    FIELD_PREP(PPE_L0_C_DRR_WT, 1) |
+                    FIELD_PREP(PPE_L0_E_DRR_WT, 1));
+
+       sp_idx = c->sp * 8 + c->cpri;
+       regmap_write(priv->regmap, PPE_TM_L0_C_SP(sp_idx),
+                    FIELD_PREP(PPE_L0_SP_DRR_ID, c->cdrr));
+
+       sp_idx = c->sp * 8 + c->epri;
+       regmap_write(priv->regmap, PPE_TM_L0_E_SP(sp_idx),
+                    FIELD_PREP(PPE_L0_SP_DRR_ID, c->edrr));
+
+       regmap_write(priv->regmap, PPE_TM_L0_PORT_MAP(c->queue),
+                    FIELD_PREP(PPE_L0_PORT_NUM, c->port));
+}
+
+struct port_l0_params {
+       u16 ucast_base;
+       u8 ucast_count;
+       u16 mcast_base;
+       u8 mcast_count;
+       u8 sp_base;
+       u8 cdrr_base;
+       u8 port;
+};
+
+static const struct port_l0_params port_l0[] = {
+       { 144, 16, 272, 4, 36,  48, 1 },
+       { 160, 16, 276, 4, 40,  64, 2 },
+       { 176, 16, 280, 4, 44,  80, 3 },
+       { 192, 16, 284, 4, 48,  96, 4 },
+       { 208, 16, 288, 4, 52, 112, 5 },
+       { 224, 16, 292, 4, 56, 128, 6 },
+       { 240, 16, 296, 1, 60, 144, 7 },
+};
+
+static void ppe_l0_scheduler_init(struct qca_ppe_priv *priv)
+{
+       int i, j;
+
+       for (i = 0; i < ARRAY_SIZE(l0_port0); i++)
+               ppe_l0_entry_write(priv, &l0_port0[i]);
+
+       for (i = 0; i < ARRAY_SIZE(port_l0); i++) {
+               const struct port_l0_params *p = &port_l0[i];
+               u16 bases[] = { p->ucast_base, p->mcast_base };
+               u8 counts[] = { p->ucast_count, p->mcast_count };
+               int k;
+
+               for (k = 0; k < 2; k++) {
+                       for (j = 0; j < counts[k]; j++) {
+                               struct l0_cfg c = {
+                                       .queue = bases[k] + j,
+                                       .port = p->port,
+                                       .sp = p->sp_base + j / PPE_MAX_SP_PRI,
+                                       .cpri = j % PPE_MAX_SP_PRI,
+                                       .cdrr = p->cdrr_base + j,
+                                       .epri = j % PPE_MAX_SP_PRI,
+                                       .edrr = p->cdrr_base + j,
+                               };
+
+                               ppe_l0_entry_write(priv, &c);
+                       }
+               }
+       }
+}
+
+static void ppe_edma_ring_map_init(struct qca_ppe_priv *priv)
+{
+       int i;
+
+       regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(0), 0xf);
+       for (i = 1; i < 10; i++)
+               regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(0) + i * 4, 0);
+
+       regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(3), 0xf0);
+       for (i = 1; i < 10; i++)
+               regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(3) + i * 4, 0);
+
+       regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(1), 0xf00);
+       for (i = 1; i < 10; i++)
+               regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(1) + i * 4, 0);
+
+       for (i = 0; i < 10; i++)
+               regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(2) + i * 4, 0);
+       regmap_write(priv->regmap, PPE_TM_RING_Q_MAP(2) + 4 * 4, 0xffff);
+}
+
+static void ppe_qos_init(struct qca_ppe_priv *priv)
+{
+       int i;
+       u32 qos_bits;
+
+       qos_bits = FIELD_PREP(PPE_QOS_PREHEADER_PREC, 3) |
+                  FIELD_PREP(PPE_QOS_DSCP_PREC, 1) |
+                  FIELD_PREP(PPE_QOS_FLOW_PREC, 4) |
+                  FIELD_PREP(PPE_QOS_ACL_PREC, 2);
+
+       for (i = 0; i < PPE_NUM_PORTS; i++)
+               regmap_update_bits(priv->regmap, PPE_PRX_MRU_MTU_W1(i),
+                                  PPE_QOS_PCP_GRP | PPE_QOS_DSCP_GRP |
+                                  PPE_QOS_PREHEADER_PREC | PPE_QOS_PCP_PREC |
+                                  PPE_QOS_DSCP_PREC | PPE_QOS_FLOW_PREC |
+                                  PPE_QOS_ACL_PREC,
+                                  qos_bits);
+}
+
+const struct psch_tdm_data cppe_psch_tdm_data = {
+       .entries = cppe_psch_tdm,
+       .num = ARRAY_SIZE(cppe_psch_tdm),
+};
+
+const struct psch_tdm_data hppe_psch_tdm_data = {
+       .entries = hppe_psch_tdm,
+       .num = ARRAY_SIZE(hppe_psch_tdm),
+};
+
+const struct bm_tdm_data cppe_bm_tdm_data = {
+       .entries = cppe_bm_tdm,
+       .num = ARRAY_SIZE(cppe_bm_tdm),
+};
+
+const struct bm_tdm_data hppe_bm_tdm_data = {
+       .entries = hppe_bm_tdm,
+       .num = ARRAY_SIZE(hppe_bm_tdm),
+};
+
+void ppe_scheduler_init(struct qca_ppe_priv *priv)
+{
+       ppe_tdm_init(priv);
+       ppe_bm_init(priv);
+       ppe_qm_init(priv);
+       ppe_l1_scheduler_init(priv);
+       ppe_l0_scheduler_init(priv);
+       ppe_edma_ring_map_init(priv);
+       ppe_qos_init(priv);
+}
diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_vlan.c b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_vlan.c
new file mode 100644 (file)
index 0000000..f2c3e2e
--- /dev/null
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+#include <linux/bitmap.h>
+#include <linux/if_bridge.h>
+
+#include "qca_ppe.h"
+
+static int ppe_xlt_idx_alloc(struct qca_ppe_priv *priv)
+{
+       int idx;
+
+       idx = find_first_zero_bit(priv->xlt_bitmap, PPE_XLT_TBL_NUM);
+       if (idx >= PPE_XLT_TBL_NUM)
+               return -ENOSPC;
+
+       set_bit(idx, priv->xlt_bitmap);
+       return idx;
+}
+
+static void ppe_xlt_rule_set(struct qca_ppe_priv *priv, int idx,
+                            u8 port_bmp, u16 vid, bool untagged)
+{
+       u32 w0, w1;
+
+       w0 = PPE_XLT_VALID | FIELD_PREP(PPE_XLT_PORT_BMP, port_bmp);
+       w1 = 0;
+
+       if (untagged) {
+               w0 |= PPE_XLT_CKEY_FMT_0;
+       } else {
+               w1 |= FIELD_PREP(PPE_XLT_CKEY_FMT_1,
+                                 PPE_XLT_CKEY_TAGGED >> 1);
+               w1 |= PPE_XLT_CKEY_VID_INCL;
+               w1 |= FIELD_PREP(PPE_XLT_CKEY_VID, vid);
+       }
+
+       regmap_write(priv->regmap, PPE_XLT_RULE_TBL(idx), w0);
+       regmap_write(priv->regmap, PPE_XLT_RULE_W1(idx), w1);
+       regmap_write(priv->regmap, PPE_XLT_RULE_TBL(idx) + 8, 0);
+}
+
+static void ppe_xlt_action_set(struct qca_ppe_priv *priv, int idx,
+                              u32 vsi, bool strip_ctag)
+{
+       u32 w0 = 0, w1;
+
+       if (strip_ctag)
+               w0 = FIELD_PREP(PPE_XLT_CVID_CMD, PPE_XLT_CVID_DEL);
+
+       w1 = PPE_XLT_VSI_CMD | FIELD_PREP(PPE_XLT_VSI, vsi);
+
+       regmap_write(priv->regmap, PPE_XLT_ACTION_TBL(idx), w0);
+       regmap_write(priv->regmap, PPE_XLT_ACTION_W1(idx), w1);
+}
+
+static void ppe_xlt_clear(struct qca_ppe_priv *priv, int idx)
+{
+       regmap_write(priv->regmap, PPE_XLT_RULE_TBL(idx), 0);
+       regmap_write(priv->regmap, PPE_XLT_RULE_W1(idx), 0);
+       regmap_write(priv->regmap, PPE_XLT_RULE_TBL(idx) + 8, 0);
+       regmap_write(priv->regmap, PPE_XLT_ACTION_TBL(idx), 0);
+       regmap_write(priv->regmap, PPE_XLT_ACTION_W1(idx), 0);
+}
+
+static void ppe_xlt_idx_free(struct qca_ppe_priv *priv, int *idx)
+{
+       ppe_xlt_clear(priv, *idx);
+       clear_bit(*idx, priv->xlt_bitmap);
+       *idx = -1;
+}
+
+static void ppe_eg_vsi_tag_port_set(struct qca_ppe_priv *priv,
+                                   u32 vsi, int port, u32 mode)
+{
+       regmap_update_bits(priv->regmap, PPE_EG_VSI_TAG(vsi),
+                          0x3 << (port * 2), (mode & 0x3) << (port * 2));
+}
+
+static void ppe_port_def_cvid_set(struct qca_ppe_priv *priv,
+                                 int port, u16 vid, bool enable)
+{
+       regmap_update_bits(priv->regmap, PPE_PORT_DEF_VID(port),
+                          PPE_PORT_DEF_CVID | PPE_PORT_DEF_CVID_EN,
+                          enable ? FIELD_PREP(PPE_PORT_DEF_CVID, vid) |
+                          PPE_PORT_DEF_CVID_EN : 0);
+}
+
+static struct qca_ppe_vlan_entry *
+ppe_vlan_find(struct qca_ppe_priv *priv, struct net_device *br_dev,
+             u16 vid)
+{
+       int i;
+
+       for (i = 0; i < PPE_VSI_MAX; i++)
+               if (priv->vlans[i].br_dev == br_dev &&
+                   priv->vlans[i].vid == vid)
+                       return &priv->vlans[i];
+
+       return NULL;
+}
+
+static struct qca_ppe_vlan_entry *
+ppe_vlan_alloc(struct qca_ppe_priv *priv, struct net_device *br_dev,
+              u16 vid)
+{
+       struct qca_ppe_vlan_entry *entry;
+       int vsi, i;
+
+       vsi = ppe_vsi_alloc(priv);
+       if (vsi < 0)
+               return NULL;
+
+       for (i = 0; i < PPE_VSI_MAX; i++) {
+               if (priv->vlans[i].br_dev)
+                       continue;
+
+               entry = &priv->vlans[i];
+               entry->br_dev = br_dev;
+               entry->vid = vid;
+               entry->vsi = vsi;
+               entry->ports = 0;
+               entry->pvid_ports = 0;
+               entry->xlt_idx = -1;
+               entry->xlt_pvid_idx = -1;
+               return entry;
+       }
+
+       ppe_vsi_free(priv, vsi);
+       return NULL;
+}
+
+static void ppe_vlan_free(struct qca_ppe_priv *priv,
+                         struct qca_ppe_vlan_entry *entry)
+{
+       if (entry->xlt_idx >= 0)
+               ppe_xlt_idx_free(priv, &entry->xlt_idx);
+       if (entry->xlt_pvid_idx >= 0)
+               ppe_xlt_idx_free(priv, &entry->xlt_pvid_idx);
+       ppe_vsi_free(priv, entry->vsi);
+       entry->br_dev = NULL;
+}
+
+static void ppe_vlan_members_update(struct qca_ppe_priv *priv,
+                                   struct qca_ppe_vlan_entry *entry)
+{
+       ppe_vsi_member_set(priv, entry->vsi,
+                          entry->ports | BIT(QCA_PPE_CPU_PORT));
+}
+
+static void ppe_vlan_pvid_update(struct qca_ppe_priv *priv,
+                                struct qca_ppe_vlan_entry *entry)
+{
+       if (!entry->pvid_ports && entry->xlt_pvid_idx >= 0) {
+               ppe_xlt_idx_free(priv, &entry->xlt_pvid_idx);
+               return;
+       }
+
+       if (entry->pvid_ports)
+               ppe_xlt_rule_set(priv, entry->xlt_pvid_idx,
+                                entry->pvid_ports, 0, true);
+}
+
+int qca_ppe_vlan_setup(struct dsa_switch *ds)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       int i;
+
+       for (i = 0; i < ds->num_ports; i++) {
+               u32 mode = dsa_is_user_port(ds, i) ?
+                          PPE_EG_UNMODIFIED : PPE_EG_UNTOUCHED;
+
+               regmap_update_bits(priv->regmap, PPE_PORT_EG_VLAN(i),
+                                  PPE_PORT_EG_VLAN_CTAG_MODE |
+                                  PPE_PORT_EG_VLAN_STAG_MODE,
+                                  FIELD_PREP(PPE_PORT_EG_VLAN_CTAG_MODE, mode) |
+                                  FIELD_PREP(PPE_PORT_EG_VLAN_STAG_MODE, mode));
+       }
+
+       for (i = 0; i < PPE_VSI_MAX; i++) {
+               regmap_write(priv->regmap, PPE_EG_VSI_TAG(i),
+                            PPE_EG_VSI_TAG_UNMODIFIED);
+               priv->vlans[i].xlt_idx = -1;
+               priv->vlans[i].xlt_pvid_idx = -1;
+       }
+
+       regmap_update_bits(priv->regmap, PPE_EG_BRIDGE_CONFIG,
+                          PPE_EG_L2_EDIT_EN, PPE_EG_L2_EDIT_EN);
+
+       ds->configure_vlan_while_not_filtering = false;
+
+       return 0;
+}
+
+int qca_ppe_port_vlan_filtering(struct dsa_switch *ds, int port,
+                               bool vlan_filtering,
+                               struct netlink_ext_ack *extack)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+
+       regmap_update_bits(priv->regmap, PPE_PORT_EG_VLAN(port),
+                          PPE_PORT_EG_VSI_TAG_EN,
+                          vlan_filtering ? PPE_PORT_EG_VSI_TAG_EN : 0);
+
+       regmap_update_bits(priv->regmap, PPE_PORT_VLAN_CFG(port),
+                          PPE_VLAN_XLT_MISS_FWD,
+                          vlan_filtering ?
+                          FIELD_PREP(PPE_VLAN_XLT_MISS_FWD, PPE_XLT_MISS_FWD_DROP) : 0);
+
+       return 0;
+}
+
+int qca_ppe_port_vlan_add(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan,
+                         struct netlink_ext_ack *extack)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       struct net_device *br_dev = priv->port_br_dev[port];
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+       struct qca_ppe_vlan_entry *entry;
+       u16 vid = vlan->vid;
+       int idx;
+
+       if (!br_dev)
+               return 0;
+
+       entry = ppe_vlan_find(priv, br_dev, vid);
+       if (!entry) {
+               entry = ppe_vlan_alloc(priv, br_dev, vid);
+               if (!entry)
+                       return -ENOSPC;
+       }
+
+       entry->ports |= BIT(port);
+
+       if (entry->xlt_idx < 0) {
+               idx = ppe_xlt_idx_alloc(priv);
+               if (idx < 0) {
+                       entry->ports &= ~BIT(port);
+                       if (!entry->ports)
+                               ppe_vlan_free(priv, entry);
+                       return -ENOSPC;
+               }
+               entry->xlt_idx = idx;
+       }
+
+       ppe_xlt_rule_set(priv, entry->xlt_idx,
+                        entry->ports | BIT(QCA_PPE_CPU_PORT), vid, false);
+       ppe_xlt_action_set(priv, entry->xlt_idx, entry->vsi, true);
+
+       ppe_eg_vsi_tag_port_set(priv, entry->vsi, port,
+                               untagged ? PPE_EG_UNTAGGED : PPE_EG_TAGGED);
+
+       if (pvid) {
+               ppe_port_def_cvid_set(priv, port, vid, true);
+               priv->port_pvid[port] = vid;
+               entry->pvid_ports |= BIT(port);
+
+               if (entry->xlt_pvid_idx < 0) {
+                       idx = ppe_xlt_idx_alloc(priv);
+                       if (idx < 0)
+                               return -ENOSPC;
+                       entry->xlt_pvid_idx = idx;
+               }
+               ppe_xlt_rule_set(priv, entry->xlt_pvid_idx,
+                                entry->pvid_ports, 0, true);
+               ppe_xlt_action_set(priv, entry->xlt_pvid_idx, entry->vsi,
+                                  false);
+       } else if (priv->port_pvid[port] == vid) {
+               ppe_port_def_cvid_set(priv, port, 0, false);
+               priv->port_pvid[port] = 0;
+               entry->pvid_ports &= ~BIT(port);
+
+               ppe_vlan_pvid_update(priv, entry);
+       }
+
+       ppe_vlan_members_update(priv, entry);
+
+       return 0;
+}
+
+int qca_ppe_port_vlan_del(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan)
+{
+       struct qca_ppe_priv *priv = ds_to_priv(ds);
+       struct net_device *br_dev = priv->port_br_dev[port];
+       struct qca_ppe_vlan_entry *entry;
+       u16 vid = vlan->vid;
+
+       if (!br_dev)
+               return 0;
+
+       entry = ppe_vlan_find(priv, br_dev, vid);
+       if (!entry)
+               return 0;
+
+       entry->ports &= ~BIT(port);
+       ppe_eg_vsi_tag_port_set(priv, entry->vsi, port, PPE_EG_UNMODIFIED);
+
+       if (priv->port_pvid[port] == vid) {
+               ppe_port_def_cvid_set(priv, port, 0, false);
+               priv->port_pvid[port] = 0;
+               entry->pvid_ports &= ~BIT(port);
+       }
+
+       if (!entry->ports) {
+               ppe_vlan_free(priv, entry);
+               return 0;
+       }
+
+       ppe_xlt_rule_set(priv, entry->xlt_idx,
+                        entry->ports | BIT(QCA_PPE_CPU_PORT), vid, false);
+
+       ppe_vlan_pvid_update(priv, entry);
+
+       ppe_vlan_members_update(priv, entry);
+
+       return 0;
+}
diff --git a/target/linux/qualcommax/files/include/dt-bindings/net/qualcomm,ipq-ppe.yaml b/target/linux/qualcommax/files/include/dt-bindings/net/qualcomm,ipq-ppe.yaml
new file mode 100644 (file)
index 0000000..ddd727d
--- /dev/null
@@ -0,0 +1,309 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/qualcomm,ipq-ppe.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ PPE Ethernet Switch
+
+maintainers:
+  - John Crispin <john@phrozen.org>
+
+description: |
+  The Packet Processing Engine (PPE) in Qualcomm IPQ6018 and IPQ8074 SoCs is a
+  hardware Ethernet switch with integrated MAC, L2 forwarding, FDB, and QoS
+  scheduling. It is driven as a DSA switch, using an EDMA network device as the
+  CPU port conduit and UNIPHY PCS instances for SerDes link management.
+
+  IPQ6018 provides 7 ports (CPU port 0, user ports 1-4 via QSGMII/PSGMII on uniphy0,
+  and port 5 via uniphy1). IPQ8074 provides 8 ports (CPU port 0, user ports
+  1-4 via QSGMII/PSGMII on uniphy0, and ports 5-6 via uniphy1/uniphy2).
+
+$ref: dsa.yaml#
+
+properties:
+  compatible:
+    enum:
+      - qualcomm,ipq6018-ppe
+      - qualcomm,ipq8074-ppe
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 21
+    maxItems: 33
+    description: |
+      PPE common clocks, per-port MAC clocks, NSS infrastructure clocks, and
+      per-port RX/TX clocks. The exact set depends on the SoC variant and
+      the number of active ports.
+
+  clock-names:
+    minItems: 21
+    maxItems: 33
+    items:
+      enum:
+        - cmn_ahb_clk
+        - cmn_sys_clk
+        - port1_mac_clk
+        - port2_mac_clk
+        - port3_mac_clk
+        - port4_mac_clk
+        - port5_mac_clk
+        - port6_mac_clk
+        - nss_ppe_clk
+        - nss_ppe_cfg_clk
+        - nssnoc_ppe_clk
+        - nssnoc_ppe_cfg_clk
+        - nss_ppe_ipe_clk
+        - nss_ppe_btq_clk
+        - gcc_mdio_ahb_clk
+        - gcc_nss_noc_clk
+        - gcc_nssnoc_snoc_clk
+        - gcc_mem_noc_nss_axi_clk
+        - gcc_nss_crypto_clk
+        - gcc_nss_imem_clk
+        - gcc_nss_ptp_ref_clk
+        - gcc_snoc_nssnoc_clk
+        - port1_rx
+        - port1_tx
+        - port2_rx
+        - port2_tx
+        - port3_rx
+        - port3_tx
+        - port4_rx
+        - port4_tx
+        - port5_rx
+        - port5_tx
+        - port6_rx
+        - port6_tx
+
+  resets:
+    minItems: 2
+    maxItems: 7
+    description: |
+      PPE full reset followed by per-port NSS resets. IPQ6018 has 6 resets
+      (PPE + 5 ports), IPQ8074 has 7 (PPE + 6 ports).
+
+  reset-names:
+    minItems: 2
+    maxItems: 7
+    items:
+      enum:
+        - ppe_rst
+        - nss_port1_rst
+        - nss_port2_rst
+        - nss_port3_rst
+        - nss_port4_rst
+        - nss_port5_rst
+        - nss_port6_rst
+
+patternProperties:
+  "^(ethernet-)?ports$":
+    type: object
+    additionalProperties: true
+    patternProperties:
+      "^(ethernet-)?port@[0-7]$":
+        type: object
+        description: PPE switch ports
+
+        $ref: dsa-port.yaml#
+
+        properties:
+          pcs-handle:
+            $ref: /schemas/types.yaml#/definitions/phandle-array
+            items:
+              - items:
+                  - description: Phandle to a UNIPHY PCS instance
+                  - description: Channel index within the UNIPHY
+            description:
+              Reference to the UNIPHY PCS instance and channel that provides
+              SerDes link control for this port. Not used on the CPU port or
+              ports with RGMII PHY connections.
+
+        unevaluatedProperties: false
+
+oneOf:
+  - required:
+      - ports
+  - required:
+      - ethernet-ports
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-ipq8074.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    edma: edma@3ab00000 {
+        compatible = "qualcomm,ipq8074-edma";
+        reg = <0x3ab00000 0xb0000>;
+
+        interrupts = <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 361 IRQ_TYPE_LEVEL_HIGH>,
+                     <GIC_SPI 344 IRQ_TYPE_LEVEL_HIGH>;
+
+        clocks = <&gcc GCC_NSS_EDMA_CLK>,
+                 <&gcc GCC_NSS_EDMA_CFG_CLK>,
+                 <&gcc GCC_NSS_PPE_CLK>,
+                 <&gcc GCC_NSS_PPE_CFG_CLK>,
+                 <&gcc GCC_NSSNOC_PPE_CLK>,
+                 <&gcc GCC_NSSNOC_PPE_CFG_CLK>,
+                 <&gcc GCC_NSS_NOC_CLK>,
+                 <&gcc GCC_NSSNOC_SNOC_CLK>,
+                 <&gcc GCC_MEM_NOC_NSS_AXI_CLK>;
+        clock-names = "nss_edma_clk", "nss_edma_cfg_clk",
+                      "nss_ppe_clk", "nss_ppe_cfg_clk",
+                      "nssnoc_ppe_clk", "nssnoc_ppe_cfg_clk",
+                      "gcc_nss_noc_clk", "gcc_nssnoc_snoc_clk",
+                      "gcc_mem_noc_nss_axi_clk";
+
+        resets = <&gcc GCC_EDMA_HW_RESET>;
+        reset-names = "edma_rst";
+    };
+
+    uniphy1: ethernet-pcs@7a10000 {
+        compatible = "qualcomm,ipq8074-uniphy";
+        reg = <0x07a10000 0x10000>;
+        #pcs-cells = <1>;
+
+        clocks = <&gcc GCC_UNIPHY1_AHB_CLK>,
+                 <&gcc GCC_UNIPHY1_SYS_CLK>,
+                 <&gcc GCC_UNIPHY1_PORT5_RX_CLK>,
+                 <&gcc GCC_UNIPHY1_PORT5_TX_CLK>;
+        clock-names = "ahb", "sys",
+                      "port5_rx", "port5_tx";
+
+        resets = <&gcc GCC_UNIPHY1_SOFT_RESET>,
+                 <&gcc GCC_UNIPHY1_XPCS_RESET>;
+        reset-names = "soft", "xpcs";
+    };
+
+    uniphy2: ethernet-pcs@7a20000 {
+        compatible = "qualcomm,ipq8074-uniphy";
+        reg = <0x07a20000 0x10000>;
+        #pcs-cells = <1>;
+
+        clocks = <&gcc GCC_UNIPHY2_AHB_CLK>,
+                 <&gcc GCC_UNIPHY2_SYS_CLK>,
+                 <&gcc GCC_UNIPHY2_PORT6_RX_CLK>,
+                 <&gcc GCC_UNIPHY2_PORT6_TX_CLK>;
+        clock-names = "ahb", "sys",
+                      "port6_rx", "port6_tx";
+
+        resets = <&gcc GCC_UNIPHY2_SOFT_RESET>,
+                 <&gcc GCC_UNIPHY2_XPCS_RESET>;
+        reset-names = "soft", "xpcs";
+    };
+
+    ppe@3a000000 {
+        compatible = "qualcomm,ipq8074-ppe";
+        reg = <0x3a000000 0x900000>;
+
+        clocks = <&gcc GCC_CMN_12GPLL_AHB_CLK>,
+                 <&gcc GCC_CMN_12GPLL_SYS_CLK>,
+                 <&gcc GCC_PORT1_MAC_CLK>,
+                 <&gcc GCC_PORT2_MAC_CLK>,
+                 <&gcc GCC_PORT3_MAC_CLK>,
+                 <&gcc GCC_PORT4_MAC_CLK>,
+                 <&gcc GCC_PORT5_MAC_CLK>,
+                 <&gcc GCC_PORT6_MAC_CLK>,
+                 <&gcc GCC_NSS_PPE_CLK>,
+                 <&gcc GCC_NSS_PPE_CFG_CLK>,
+                 <&gcc GCC_NSSNOC_PPE_CLK>,
+                 <&gcc GCC_NSSNOC_PPE_CFG_CLK>,
+                 <&gcc GCC_NSS_PPE_IPE_CLK>,
+                 <&gcc GCC_NSS_PPE_BTQ_CLK>,
+                 <&gcc GCC_MDIO_AHB_CLK>,
+                 <&gcc GCC_NSS_NOC_CLK>,
+                 <&gcc GCC_NSSNOC_SNOC_CLK>,
+                 <&gcc GCC_MEM_NOC_NSS_AXI_CLK>,
+                 <&gcc GCC_NSS_CRYPTO_CLK>,
+                 <&gcc GCC_NSS_IMEM_CLK>,
+                 <&gcc GCC_NSS_PTP_REF_CLK>,
+                 <&gcc GCC_NSS_PORT1_RX_CLK>,
+                 <&gcc GCC_NSS_PORT1_TX_CLK>,
+                 <&gcc GCC_NSS_PORT2_RX_CLK>,
+                 <&gcc GCC_NSS_PORT2_TX_CLK>,
+                 <&gcc GCC_NSS_PORT3_RX_CLK>,
+                 <&gcc GCC_NSS_PORT3_TX_CLK>,
+                 <&gcc GCC_NSS_PORT4_RX_CLK>,
+                 <&gcc GCC_NSS_PORT4_TX_CLK>,
+                 <&gcc GCC_NSS_PORT5_RX_CLK>,
+                 <&gcc GCC_NSS_PORT5_TX_CLK>,
+                 <&gcc GCC_NSS_PORT6_RX_CLK>,
+                 <&gcc GCC_NSS_PORT6_TX_CLK>;
+        clock-names = "cmn_ahb_clk", "cmn_sys_clk",
+                      "port1_mac_clk", "port2_mac_clk",
+                      "port3_mac_clk", "port4_mac_clk",
+                      "port5_mac_clk", "port6_mac_clk",
+                      "nss_ppe_clk", "nss_ppe_cfg_clk",
+                      "nssnoc_ppe_clk", "nssnoc_ppe_cfg_clk",
+                      "nss_ppe_ipe_clk", "nss_ppe_btq_clk",
+                      "gcc_mdio_ahb_clk", "gcc_nss_noc_clk",
+                      "gcc_nssnoc_snoc_clk",
+                      "gcc_mem_noc_nss_axi_clk",
+                      "gcc_nss_crypto_clk",
+                      "gcc_nss_imem_clk",
+                      "gcc_nss_ptp_ref_clk",
+                      "port1_rx", "port1_tx",
+                      "port2_rx", "port2_tx",
+                      "port3_rx", "port3_tx",
+                      "port4_rx", "port4_tx",
+                      "port5_rx", "port5_tx",
+                      "port6_rx", "port6_tx";
+
+        resets = <&gcc GCC_PPE_FULL_RESET>,
+                 <&gcc GCC_NSSPORT1_RESET>,
+                 <&gcc GCC_NSSPORT2_RESET>,
+                 <&gcc GCC_NSSPORT3_RESET>,
+                 <&gcc GCC_NSSPORT4_RESET>,
+                 <&gcc GCC_NSSPORT5_RESET>,
+                 <&gcc GCC_NSSPORT6_RESET>;
+        reset-names = "ppe_rst", "nss_port1_rst",
+                      "nss_port2_rst", "nss_port3_rst",
+                      "nss_port4_rst", "nss_port5_rst",
+                      "nss_port6_rst";
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+                ethernet = <&edma>;
+                phy-mode = "internal";
+
+                fixed-link {
+                    speed = <10000>;
+                    full-duplex;
+                };
+            };
+
+            port@5 {
+                reg = <5>;
+                label = "lan";
+                phy-handle = <&phy1>;
+                phy-mode = "sgmii";
+                pcs-handle = <&uniphy1 0>;
+            };
+
+            port@6 {
+                reg = <6>;
+                label = "wan";
+                phy-handle = <&phy2>;
+                phy-mode = "sgmii";
+                pcs-handle = <&uniphy2 0>;
+            };
+        };
+    };
diff --git a/target/linux/qualcommax/patches-6.12/0952-net-ethernet-qca-add-ppe.patch b/target/linux/qualcommax/patches-6.12/0952-net-ethernet-qca-add-ppe.patch
new file mode 100644 (file)
index 0000000..bec1451
--- /dev/null
@@ -0,0 +1,27 @@
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -61,6 +61,13 @@ config QCOM_EMAC
+         low power, Receive-Side Scaling (RSS), and IEEE 1588-2008
+         Precision Clock Synchronization Protocol.
++config QCOM_80211AX_PPE
++      tristate "Qualcomm 802.11ax PPE switch driver"
++      select PHYLINK
++      help
++        Driver for Qualcomm 802.11ax PPE (Packet Processing Engine) switches.
++
++
+ source "drivers/net/ethernet/qualcomm/rmnet/Kconfig"
+ endif # NET_VENDOR_QUALCOMM
+--- a/drivers/net/ethernet/qualcomm/Makefile
++++ b/drivers/net/ethernet/qualcomm/Makefile
+@@ -8,6 +8,8 @@ obj-$(CONFIG_QCA7000_SPI) += qcaspi.o
+ qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o
+ obj-$(CONFIG_QCA7000_UART) += qcauart.o
+ qcauart-objs := qca_uart.o
++obj-$(CONFIG_QCOM_80211AX_PPE) += qca_ppe.o
++qca_ppe-objs := qca_ppe_main.o qca_ppe_vlan.o qca_ppe_scheduler.o
+ obj-y += emac/