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