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
--- /dev/null
+/* 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
--- /dev/null
+// 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");
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+# 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>;
+ };
+ };
+ };
--- /dev/null
+--- 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/
+