// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2018 Microsemi Corporation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mscc_xfer.h" #include "mscc_miim.h" #define ANA_AC_RAM_CTRL_RAM_INIT 0x94358 #define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x94370 #define ANA_CL_PORT_VLAN_CFG(x) (0x24018 + 0xc8 * (x)) #define ANA_CL_PORT_VLAN_CFG_AWARE_ENA BIT(19) #define ANA_CL_PORT_VLAN_CFG_POP_CNT(x) ((x) << 17) #define ANA_L2_COMMON_FWD_CFG 0x8a2a8 #define ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA BIT(6) #define ASM_CFG_STAT_CFG 0x3508 #define ASM_CFG_PORT(x) (0x36c4 + 0x4 * (x)) #define ASM_CFG_PORT_NO_PREAMBLE_ENA BIT(8) #define ASM_CFG_PORT_INJ_FORMAT_CFG(x) ((x) << 1) #define ASM_RAM_CTRL_RAM_INIT 0x39b8 #define DEV_DEV_CFG_DEV_RST_CTRL 0x0 #define DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(x) ((x) << 20) #define DEV_MAC_CFG_MAC_ENA 0x1c #define DEV_MAC_CFG_MAC_ENA_RX_ENA BIT(4) #define DEV_MAC_CFG_MAC_ENA_TX_ENA BIT(0) #define DEV_MAC_CFG_MAC_IFG 0x34 #define DEV_MAC_CFG_MAC_IFG_TX_IFG(x) ((x) << 8) #define DEV_MAC_CFG_MAC_IFG_RX_IFG2(x) ((x) << 4) #define DEV_MAC_CFG_MAC_IFG_RX_IFG1(x) (x) #define DEV_PCS1G_CFG_PCS1G_CFG 0x40 #define DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA BIT(0) #define DEV_PCS1G_CFG_PCS1G_MODE 0x44 #define DEV_PCS1G_CFG_PCS1G_SD 0x48 #define DEV_PCS1G_CFG_PCS1G_ANEG 0x4c #define DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(x) ((x) << 16) #define DSM_RAM_CTRL_RAM_INIT 0x8 #define HSIO_ANA_SERDES1G_DES_CFG 0xac #define HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(x) ((x) << 1) #define HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(x) ((x) << 5) #define HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(x) ((x) << 8) #define HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(x) ((x) << 13) #define HSIO_ANA_SERDES1G_IB_CFG 0xb0 #define HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(x) (x) #define HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(x) ((x) << 6) #define HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP BIT(9) #define HSIO_ANA_SERDES1G_IB_CFG_ENA_DETLEV BIT(11) #define HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM BIT(13) #define HSIO_ANA_SERDES1G_IB_CFG_DET_LEV(x) ((x) << 19) #define HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(x) ((x) << 24) #define HSIO_ANA_SERDES1G_OB_CFG 0xb4 #define HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(x) (x) #define HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(x) ((x) << 4) #define HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(x) ((x) << 10) #define HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(x) ((x) << 13) #define HSIO_ANA_SERDES1G_OB_CFG_SLP(x) ((x) << 17) #define HSIO_ANA_SERDES1G_SER_CFG 0xb8 #define HSIO_ANA_SERDES1G_COMMON_CFG 0xbc #define HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE BIT(0) #define HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE BIT(18) #define HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST BIT(31) #define HSIO_ANA_SERDES1G_PLL_CFG 0xc0 #define HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA BIT(7) #define HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(x) ((x) << 8) #define HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2 BIT(21) #define HSIO_DIG_SERDES1G_DFT_CFG0 0xc8 #define HSIO_DIG_SERDES1G_TP_CFG 0xd4 #define HSIO_DIG_SERDES1G_MISC_CFG 0xdc #define HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST BIT(0) #define HSIO_MCB_SERDES1G_CFG 0xe8 #define HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT BIT(31) #define HSIO_MCB_SERDES1G_CFG_ADDR(x) (x) #define HSIO_ANA_SERDES6G_DES_CFG 0x11c #define HSIO_ANA_SERDES6G_DES_CFG_SWAP_ANA BIT(0) #define HSIO_ANA_SERDES6G_DES_CFG_BW_ANA(x) ((x) << 1) #define HSIO_ANA_SERDES6G_DES_CFG_SWAP_HYST BIT(4) #define HSIO_ANA_SERDES6G_DES_CFG_BW_HYST(x) ((x) << 5) #define HSIO_ANA_SERDES6G_DES_CFG_CPMD_SEL(x) ((x) << 8) #define HSIO_ANA_SERDES6G_DES_CFG_MBTR_CTRL(x) ((x) << 10) #define HSIO_ANA_SERDES6G_DES_CFG_PHS_CTRL(x) ((x) << 13) #define HSIO_ANA_SERDES6G_IB_CFG 0x120 #define HSIO_ANA_SERDES6G_IB_CFG_REG_ENA BIT(0) #define HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA BIT(1) #define HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA BIT(2) #define HSIO_ANA_SERDES6G_IB_CFG_CAL_ENA(x) ((x) << 3) #define HSIO_ANA_SERDES6G_IB_CFG_CONCUR BIT(4) #define HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA BIT(5) #define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(x) ((x) << 7) #define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(x) ((x) << 9) #define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(x) ((x) << 11) #define HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(x) ((x) << 13) #define HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(x) ((x) << 15) #define HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(x) ((x) << 18) #define HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(x) ((x) << 20) #define HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(x) ((x) << 24) #define HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL BIT(28) #define HSIO_ANA_SERDES6G_IB_CFG_SOFSI(x) ((x) << 29) #define HSIO_ANA_SERDES6G_IB_CFG1 0x124 #define HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET BIT(4) #define HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP BIT(5) #define HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID BIT(6) #define HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP BIT(7) #define HSIO_ANA_SERDES6G_IB_CFG1_SCALY(x) ((x) << 8) #define HSIO_ANA_SERDES6G_IB_CFG1_TSDET(x) ((x) << 12) #define HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(x) ((x) << 17) #define HSIO_ANA_SERDES6G_IB_CFG2 0x128 #define HSIO_ANA_SERDES6G_IB_CFG2_UREG(x) (x) #define HSIO_ANA_SERDES6G_IB_CFG2_UMAX(x) ((x) << 3) #define HSIO_ANA_SERDES6G_IB_CFG2_TCALV(x) ((x) << 5) #define HSIO_ANA_SERDES6G_IB_CFG2_OCALS(x) ((x) << 10) #define HSIO_ANA_SERDES6G_IB_CFG2_OINFS(x) ((x) << 16) #define HSIO_ANA_SERDES6G_IB_CFG2_OINFI(x) ((x) << 22) #define HSIO_ANA_SERDES6G_IB_CFG2_TINFV(x) ((x) << 27) #define HSIO_ANA_SERDES6G_IB_CFG3 0x12c #define HSIO_ANA_SERDES6G_IB_CFG3_INI_OFFSET(x) (x) #define HSIO_ANA_SERDES6G_IB_CFG3_INI_LP(x) ((x) << 6) #define HSIO_ANA_SERDES6G_IB_CFG3_INI_MID(x) ((x) << 12) #define HSIO_ANA_SERDES6G_IB_CFG3_INI_HP(x) ((x) << 18) #define HSIO_ANA_SERDES6G_IB_CFG4 0x130 #define HSIO_ANA_SERDES6G_IB_CFG4_MAX_OFFSET(x) (x) #define HSIO_ANA_SERDES6G_IB_CFG4_MAX_LP(x) ((x) << 6) #define HSIO_ANA_SERDES6G_IB_CFG4_MAX_MID(x) ((x) << 12) #define HSIO_ANA_SERDES6G_IB_CFG4_MAX_HP(x) ((x) << 18) #define HSIO_ANA_SERDES6G_IB_CFG5 0x134 #define HSIO_ANA_SERDES6G_IB_CFG4_MIN_OFFSET(x) (x) #define HSIO_ANA_SERDES6G_IB_CFG4_MIN_LP(x) ((x) << 6) #define HSIO_ANA_SERDES6G_IB_CFG4_MIN_MID(x) ((x) << 12) #define HSIO_ANA_SERDES6G_IB_CFG4_MIN_HP(x) ((x) << 18) #define HSIO_ANA_SERDES6G_OB_CFG 0x138 #define HSIO_ANA_SERDES6G_OB_CFG_RESISTOR_CTRL(x) (x) #define HSIO_ANA_SERDES6G_OB_CFG_SR(x) ((x) << 4) #define HSIO_ANA_SERDES6G_OB_CFG_SR_H BIT(8) #define HSIO_ANA_SERDES6G_OB_CFG_SEL_RCTRL BIT(9) #define HSIO_ANA_SERDES6G_OB_CFG_R_COR BIT(10) #define HSIO_ANA_SERDES6G_OB_CFG_POST1(x) ((x) << 11) #define HSIO_ANA_SERDES6G_OB_CFG_R_ADJ_PDR BIT(16) #define HSIO_ANA_SERDES6G_OB_CFG_R_ADJ_MUX BIT(17) #define HSIO_ANA_SERDES6G_OB_CFG_PREC(x) ((x) << 18) #define HSIO_ANA_SERDES6G_OB_CFG_POST0(x) ((x) << 23) #define HSIO_ANA_SERDES6G_OB_CFG_POL BIT(29) #define HSIO_ANA_SERDES6G_OB_CFG_ENA1V_MODE(x) ((x) << 30) #define HSIO_ANA_SERDES6G_OB_CFG_IDLE BIT(31) #define HSIO_ANA_SERDES6G_OB_CFG1 0x13c #define HSIO_ANA_SERDES6G_OB_CFG1_LEV(x) (x) #define HSIO_ANA_SERDES6G_OB_CFG1_ENA_CAS(x) ((x) << 6) #define HSIO_ANA_SERDES6G_SER_CFG 0x140 #define HSIO_ANA_SERDES6G_COMMON_CFG 0x144 #define HSIO_ANA_SERDES6G_COMMON_CFG_IF_MODE(x) (x) #define HSIO_ANA_SERDES6G_COMMON_CFG_QRATE(x) (x << 2) #define HSIO_ANA_SERDES6G_COMMON_CFG_ENA_LANE BIT(14) #define HSIO_ANA_SERDES6G_COMMON_CFG_SYS_RST BIT(16) #define HSIO_ANA_SERDES6G_PLL_CFG 0x148 #define HSIO_ANA_SERDES6G_PLL_CFG_ROT_FRQ BIT(0) #define HSIO_ANA_SERDES6G_PLL_CFG_ROT_DIR BIT(1) #define HSIO_ANA_SERDES6G_PLL_CFG_RB_DATA_SEL BIT(2) #define HSIO_ANA_SERDES6G_PLL_CFG_FSM_OOR_RECAL_ENA BIT(3) #define HSIO_ANA_SERDES6G_PLL_CFG_FSM_FORCE_SET_ENA BIT(4) #define HSIO_ANA_SERDES6G_PLL_CFG_FSM_ENA BIT(5) #define HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(x) ((x) << 6) #define HSIO_ANA_SERDES6G_PLL_CFG_ENA_ROT BIT(14) #define HSIO_ANA_SERDES6G_PLL_CFG_DIV4 BIT(15) #define HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(x) ((x) << 16) #define HSIO_DIG_SERDES6G_MISC_CFG 0x108 #define HSIO_DIG_SERDES6G_MISC_CFG_LANE_RST BIT(0) #define HSIO_MCB_SERDES6G_CFG 0x168 #define HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT BIT(31) #define HSIO_MCB_SERDES6G_CFG_ADDR(x) (x) #define HSIO_HW_CFGSTAT_HW_CFG 0x16c #define LRN_COMMON_ACCESS_CTRL 0x0 #define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT BIT(0) #define LRN_COMMON_MAC_ACCESS_CFG0 0x4 #define LRN_COMMON_MAC_ACCESS_CFG1 0x8 #define LRN_COMMON_MAC_ACCESS_CFG2 0xc #define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(x) (x) #define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(x) ((x) << 12) #define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD BIT(15) #define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED BIT(16) #define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY BIT(23) #define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(x) ((x) << 24) #define QFWD_SYSTEM_SWITCH_PORT_MODE(x) (0x4 * (x)) #define QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA BIT(17) #define QS_XTR_GRP_CFG(x) (0x0 + 4 * (x)) #define QS_INJ_GRP_CFG(x) (0x24 + (x) * 4) #define QSYS_SYSTEM_RESET_CFG 0xf0 #define QSYS_CALCFG_CAL_AUTO(x) (0x3d4 + 4 * (x)) #define QSYS_CALCFG_CAL_CTRL 0x3e8 #define QSYS_CALCFG_CAL_CTRL_CAL_MODE(x) ((x) << 11) #define QSYS_RAM_CTRL_RAM_INIT 0x3ec #define REW_RAM_CTRL_RAM_INIT 0x53528 #define VOP_RAM_CTRL_RAM_INIT 0x43638 #define XTR_VALID_BYTES(x) (4 - ((x) & 3)) #define MAC_VID 0 #define CPU_PORT 53 #define IFH_LEN 7 #define JR2_BUF_CELL_SZ 60 #define ETH_ALEN 6 #define PGID_BROADCAST 510 #define PGID_UNICAST 511 static const char * const regs_names[] = { "port0", "port1", "port2", "port3", "port4", "port5", "port6", "port7", "port8", "port9", "port10", "port11", "port12", "port13", "port14", "port15", "port16", "port17", "port18", "port19", "port20", "port21", "port22", "port23", "port24", "port25", "port26", "port27", "port28", "port29", "port30", "port31", "port32", "port33", "port34", "port35", "port36", "port37", "port38", "port39", "port40", "port41", "port42", "port43", "port44", "port45", "port46", "port47", "ana_ac", "ana_cl", "ana_l2", "asm", "hsio", "lrn", "qfwd", "qs", "qsys", "rew", }; #define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1 #define MAX_PORT 48 enum jr2_ctrl_regs { ANA_AC = MAX_PORT, ANA_CL, ANA_L2, ASM, HSIO, LRN, QFWD, QS, QSYS, REW, }; #define JR2_MIIM_BUS_COUNT 3 struct jr2_phy_port_t { size_t phy_addr; struct mii_dev *bus; u8 serdes_index; u8 phy_mode; }; struct jr2_private { void __iomem *regs[REGS_NAMES_COUNT]; struct mii_dev *bus[JR2_MIIM_BUS_COUNT]; struct jr2_phy_port_t ports[MAX_PORT]; }; static const unsigned long jr2_regs_qs[] = { [MSCC_QS_XTR_RD] = 0x8, [MSCC_QS_XTR_FLUSH] = 0x18, [MSCC_QS_XTR_DATA_PRESENT] = 0x1c, [MSCC_QS_INJ_WR] = 0x2c, [MSCC_QS_INJ_CTRL] = 0x34, }; static struct mscc_miim_dev miim[JR2_MIIM_BUS_COUNT]; static int miim_count = -1; static void jr2_cpu_capture_setup(struct jr2_private *priv) { /* ASM: No preamble and IFH prefix on CPU injected frames */ writel(ASM_CFG_PORT_NO_PREAMBLE_ENA | ASM_CFG_PORT_INJ_FORMAT_CFG(1), priv->regs[ASM] + ASM_CFG_PORT(CPU_PORT)); /* Set Manual injection via DEVCPU_QS registers for CPU queue 0 */ writel(0x5, priv->regs[QS] + QS_INJ_GRP_CFG(0)); /* Set Manual extraction via DEVCPU_QS registers for CPU queue 0 */ writel(0x7, priv->regs[QS] + QS_XTR_GRP_CFG(0)); /* Enable CPU port for any frame transfer */ setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(CPU_PORT), QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA); /* Send a copy to CPU when found as forwarding entry */ setbits_le32(priv->regs[ANA_L2] + ANA_L2_COMMON_FWD_CFG, ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA); } static void jr2_port_init(struct jr2_private *priv, int port) { void __iomem *regs = priv->regs[port]; /* Enable PCS */ writel(DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA, regs + DEV_PCS1G_CFG_PCS1G_CFG); /* Disable Signal Detect */ writel(0, regs + DEV_PCS1G_CFG_PCS1G_SD); /* Enable MAC RX and TX */ writel(DEV_MAC_CFG_MAC_ENA_RX_ENA | DEV_MAC_CFG_MAC_ENA_TX_ENA, regs + DEV_MAC_CFG_MAC_ENA); /* Clear sgmii_mode_ena */ writel(0, regs + DEV_PCS1G_CFG_PCS1G_MODE); /* * Clear sw_resolve_ena(bit 0) and set adv_ability to * something meaningful just in case */ writel(DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(0x20), regs + DEV_PCS1G_CFG_PCS1G_ANEG); /* Set MAC IFG Gaps */ writel(DEV_MAC_CFG_MAC_IFG_TX_IFG(4) | DEV_MAC_CFG_MAC_IFG_RX_IFG1(5) | DEV_MAC_CFG_MAC_IFG_RX_IFG2(1), regs + DEV_MAC_CFG_MAC_IFG); /* Set link speed and release all resets */ writel(DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(2), regs + DEV_DEV_CFG_DEV_RST_CTRL); /* Make VLAN aware for CPU traffic */ writel(ANA_CL_PORT_VLAN_CFG_AWARE_ENA | ANA_CL_PORT_VLAN_CFG_POP_CNT(1) | MAC_VID, priv->regs[ANA_CL] + ANA_CL_PORT_VLAN_CFG(port)); /* Enable CPU port for any frame transfer */ setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(port), QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA); } static void serdes6g_write(void __iomem *base, u32 addr) { u32 data; writel(HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT | HSIO_MCB_SERDES6G_CFG_ADDR(addr), base + HSIO_MCB_SERDES6G_CFG); do { data = readl(base + HSIO_MCB_SERDES6G_CFG); } while (data & HSIO_MCB_SERDES6G_CFG_WR_ONE_SHOT); } static void serdes6g_setup(void __iomem *base, uint32_t addr, phy_interface_t interface) { u32 ib_if_mode = 0; u32 ib_qrate = 0; u32 ib_cal_ena = 0; u32 ib1_tsdet = 0; u32 ob_lev = 0; u32 ob_ena_cas = 0; u32 ob_ena1v_mode = 0; u32 des_bw_ana = 0; u32 pll_fsm_ctrl_data = 0; switch (interface) { case PHY_INTERFACE_MODE_SGMII: ib_if_mode = 1; ib_qrate = 1; ib_cal_ena = 1; ib1_tsdet = 3; ob_lev = 48; ob_ena_cas = 2; ob_ena1v_mode = 1; des_bw_ana = 3; pll_fsm_ctrl_data = 60; break; case PHY_INTERFACE_MODE_QSGMII: ib_if_mode = 3; ib1_tsdet = 16; ob_lev = 24; des_bw_ana = 5; pll_fsm_ctrl_data = 120; break; default: pr_err("Interface not supported\n"); return; } if (interface == PHY_INTERFACE_MODE_QSGMII) writel(0xfff, base + HSIO_HW_CFGSTAT_HW_CFG); writel(HSIO_ANA_SERDES6G_COMMON_CFG_IF_MODE(3), base + HSIO_ANA_SERDES6G_COMMON_CFG); writel(HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(120) | HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(3), base + HSIO_ANA_SERDES6G_PLL_CFG); writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | HSIO_ANA_SERDES6G_IB_CFG_CONCUR | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(7) | HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), base + HSIO_ANA_SERDES6G_IB_CFG); writel(HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET | HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP | HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID | HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP | HSIO_ANA_SERDES6G_IB_CFG1_SCALY(15) | HSIO_ANA_SERDES6G_IB_CFG1_TSDET(3) | HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(8), base + HSIO_ANA_SERDES6G_IB_CFG1); writel(HSIO_DIG_SERDES6G_MISC_CFG_LANE_RST, base + HSIO_DIG_SERDES6G_MISC_CFG); serdes6g_write(base, addr); writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | HSIO_ANA_SERDES6G_IB_CFG_CONCUR | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(0) | HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), base + HSIO_ANA_SERDES6G_IB_CFG); writel(HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET | HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP | HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID | HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP | HSIO_ANA_SERDES6G_IB_CFG1_SCALY(15) | HSIO_ANA_SERDES6G_IB_CFG1_TSDET(16) | HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(8), base + HSIO_ANA_SERDES6G_IB_CFG1); writel(0x0, base + HSIO_ANA_SERDES6G_SER_CFG); writel(HSIO_ANA_SERDES6G_COMMON_CFG_IF_MODE(ib_if_mode) | HSIO_ANA_SERDES6G_COMMON_CFG_QRATE(ib_qrate) | HSIO_ANA_SERDES6G_COMMON_CFG_ENA_LANE | HSIO_ANA_SERDES6G_COMMON_CFG_SYS_RST, base + HSIO_ANA_SERDES6G_COMMON_CFG); writel(HSIO_DIG_SERDES6G_MISC_CFG_LANE_RST, base + HSIO_DIG_SERDES6G_MISC_CFG); writel(HSIO_ANA_SERDES6G_OB_CFG_RESISTOR_CTRL(1) | HSIO_ANA_SERDES6G_OB_CFG_SR(7) | HSIO_ANA_SERDES6G_OB_CFG_SR_H | HSIO_ANA_SERDES6G_OB_CFG_ENA1V_MODE(ob_ena1v_mode) | HSIO_ANA_SERDES6G_OB_CFG_POL, base + HSIO_ANA_SERDES6G_OB_CFG); writel(HSIO_ANA_SERDES6G_OB_CFG1_LEV(ob_lev) | HSIO_ANA_SERDES6G_OB_CFG1_ENA_CAS(ob_ena_cas), base + HSIO_ANA_SERDES6G_OB_CFG1); writel(HSIO_ANA_SERDES6G_DES_CFG_BW_ANA(des_bw_ana) | HSIO_ANA_SERDES6G_DES_CFG_BW_HYST(5) | HSIO_ANA_SERDES6G_DES_CFG_MBTR_CTRL(2) | HSIO_ANA_SERDES6G_DES_CFG_PHS_CTRL(6), base + HSIO_ANA_SERDES6G_DES_CFG); writel(HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(pll_fsm_ctrl_data) | HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(3), base + HSIO_ANA_SERDES6G_PLL_CFG); serdes6g_write(base, addr); /* set pll_fsm_ena = 1 */ writel(HSIO_ANA_SERDES6G_PLL_CFG_FSM_ENA | HSIO_ANA_SERDES6G_PLL_CFG_FSM_CTRL_DATA(pll_fsm_ctrl_data) | HSIO_ANA_SERDES6G_PLL_CFG_ENA_OFFS(3), base + HSIO_ANA_SERDES6G_PLL_CFG); serdes6g_write(base, addr); /* wait 20ms for pll bringup */ mdelay(20); /* start IB calibration by setting ib_cal_ena and clearing lane_rst */ writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | HSIO_ANA_SERDES6G_IB_CFG_CAL_ENA(ib_cal_ena) | HSIO_ANA_SERDES6G_IB_CFG_CONCUR | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(0) | HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), base + HSIO_ANA_SERDES6G_IB_CFG); writel(0x0, base + HSIO_DIG_SERDES6G_MISC_CFG); serdes6g_write(base, addr); /* wait 60 for calibration */ mdelay(60); /* set ib_tsdet and ib_reg_pat_sel_offset back to correct values */ writel(HSIO_ANA_SERDES6G_IB_CFG_REG_ENA | HSIO_ANA_SERDES6G_IB_CFG_EQZ_ENA | HSIO_ANA_SERDES6G_IB_CFG_SAM_ENA | HSIO_ANA_SERDES6G_IB_CFG_CAL_ENA(ib_cal_ena) | HSIO_ANA_SERDES6G_IB_CFG_CONCUR | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_ENA | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_OFF(0) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_LP(2) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_MID(1) | HSIO_ANA_SERDES6G_IB_CFG_REG_PAT_SEL_HP(1) | HSIO_ANA_SERDES6G_IB_CFG_SIG_DET_CLK_SEL(7) | HSIO_ANA_SERDES6G_IB_CFG_TERM_MODE_SEL(1) | HSIO_ANA_SERDES6G_IB_CFG_ICML_ADJ(5) | HSIO_ANA_SERDES6G_IB_CFG_RTRM_ADJ(13) | HSIO_ANA_SERDES6G_IB_CFG_VBULK_SEL | HSIO_ANA_SERDES6G_IB_CFG_SOFSI(1), base + HSIO_ANA_SERDES6G_IB_CFG); writel(HSIO_ANA_SERDES6G_IB_CFG1_FILT_OFFSET | HSIO_ANA_SERDES6G_IB_CFG1_FILT_LP | HSIO_ANA_SERDES6G_IB_CFG1_FILT_MID | HSIO_ANA_SERDES6G_IB_CFG1_FILT_HP | HSIO_ANA_SERDES6G_IB_CFG1_SCALY(15) | HSIO_ANA_SERDES6G_IB_CFG1_TSDET(ib1_tsdet) | HSIO_ANA_SERDES6G_IB_CFG1_TJTAG(8), base + HSIO_ANA_SERDES6G_IB_CFG1); serdes6g_write(base, addr); } static void serdes1g_write(void __iomem *base, u32 addr) { u32 data; writel(HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT | HSIO_MCB_SERDES1G_CFG_ADDR(addr), base + HSIO_MCB_SERDES1G_CFG); do { data = readl(base + HSIO_MCB_SERDES1G_CFG); } while (data & HSIO_MCB_SERDES1G_CFG_WR_ONE_SHOT); } static void serdes1g_setup(void __iomem *base, uint32_t addr, phy_interface_t interface) { writel(0x0, base + HSIO_ANA_SERDES1G_SER_CFG); writel(0x0, base + HSIO_DIG_SERDES1G_TP_CFG); writel(0x0, base + HSIO_DIG_SERDES1G_DFT_CFG0); writel(HSIO_ANA_SERDES1G_OB_CFG_RESISTOR_CTRL(1) | HSIO_ANA_SERDES1G_OB_CFG_VCM_CTRL(4) | HSIO_ANA_SERDES1G_OB_CFG_CMM_BIAS_CTRL(2) | HSIO_ANA_SERDES1G_OB_CFG_AMP_CTRL(12) | HSIO_ANA_SERDES1G_OB_CFG_SLP(3), base + HSIO_ANA_SERDES1G_OB_CFG); writel(HSIO_ANA_SERDES1G_IB_CFG_RESISTOR_CTRL(13) | HSIO_ANA_SERDES1G_IB_CFG_EQ_GAIN(2) | HSIO_ANA_SERDES1G_IB_CFG_ENA_OFFSET_COMP | HSIO_ANA_SERDES1G_IB_CFG_ENA_DETLEV | HSIO_ANA_SERDES1G_IB_CFG_ENA_CMV_TERM | HSIO_ANA_SERDES1G_IB_CFG_DET_LEV(3) | HSIO_ANA_SERDES1G_IB_CFG_ACJTAG_HYST(1), base + HSIO_ANA_SERDES1G_IB_CFG); writel(HSIO_ANA_SERDES1G_DES_CFG_BW_HYST(7) | HSIO_ANA_SERDES1G_DES_CFG_BW_ANA(6) | HSIO_ANA_SERDES1G_DES_CFG_MBTR_CTRL(2) | HSIO_ANA_SERDES1G_DES_CFG_PHS_CTRL(6), base + HSIO_ANA_SERDES1G_DES_CFG); writel(HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST, base + HSIO_DIG_SERDES1G_MISC_CFG); writel(HSIO_ANA_SERDES1G_PLL_CFG_FSM_ENA | HSIO_ANA_SERDES1G_PLL_CFG_FSM_CTRL_DATA(0xc8) | HSIO_ANA_SERDES1G_PLL_CFG_ENA_RC_DIV2, base + HSIO_ANA_SERDES1G_PLL_CFG); writel(HSIO_ANA_SERDES1G_COMMON_CFG_IF_MODE | HSIO_ANA_SERDES1G_COMMON_CFG_ENA_LANE | HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST, base + HSIO_ANA_SERDES1G_COMMON_CFG); serdes1g_write(base, addr); setbits_le32(base + HSIO_ANA_SERDES1G_COMMON_CFG, HSIO_ANA_SERDES1G_COMMON_CFG_SYS_RST); serdes1g_write(base, addr); clrbits_le32(base + HSIO_DIG_SERDES1G_MISC_CFG, HSIO_DIG_SERDES1G_MISC_CFG_LANE_RST); serdes1g_write(base, addr); } static int ram_init(u32 val, void __iomem *addr) { writel(val, addr); if (wait_for_bit_le32(addr, BIT(1), false, 2000, false)) { printf("Timeout in memory reset, reg = 0x%08x\n", val); return 1; } return 0; } static int jr2_switch_init(struct jr2_private *priv) { /* Initialize memories */ ram_init(0x3, priv->regs[QSYS] + QSYS_RAM_CTRL_RAM_INIT); ram_init(0x3, priv->regs[ASM] + ASM_RAM_CTRL_RAM_INIT); ram_init(0x3, priv->regs[ANA_AC] + ANA_AC_RAM_CTRL_RAM_INIT); ram_init(0x3, priv->regs[REW] + REW_RAM_CTRL_RAM_INIT); /* Reset counters */ writel(0x1, priv->regs[ANA_AC] + ANA_AC_STAT_GLOBAL_CFG_PORT_RESET); writel(0x1, priv->regs[ASM] + ASM_CFG_STAT_CFG); /* Enable switch-core and queue system */ writel(0x1, priv->regs[QSYS] + QSYS_SYSTEM_RESET_CFG); return 0; } static void jr2_switch_config(struct jr2_private *priv) { writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(0)); writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(1)); writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(2)); writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO(3)); writel(readl(priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL) | QSYS_CALCFG_CAL_CTRL_CAL_MODE(8), priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL); } static int jr2_initialize(struct jr2_private *priv) { int ret, i; /* Initialize switch memories, enable core */ ret = jr2_switch_init(priv); if (ret) return ret; jr2_switch_config(priv); for (i = 0; i < MAX_PORT; i++) jr2_port_init(priv, i); jr2_cpu_capture_setup(priv); return 0; } static inline int jr2_vlant_wait_for_completion(struct jr2_private *priv) { if (wait_for_bit_le32(priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL, LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, false, 2000, false)) return -ETIMEDOUT; return 0; } static int jr2_mac_table_add(struct jr2_private *priv, const unsigned char mac[ETH_ALEN], int pgid) { u32 macl = 0, mach = 0; /* * Set the MAC address to handle and the vlan associated in a format * understood by the hardware. */ mach |= MAC_VID << 16; mach |= ((u32)mac[0]) << 8; mach |= ((u32)mac[1]) << 0; macl |= ((u32)mac[2]) << 24; macl |= ((u32)mac[3]) << 16; macl |= ((u32)mac[4]) << 8; macl |= ((u32)mac[5]) << 0; writel(mach, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG0); writel(macl, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG1); writel(LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(pgid) | LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(0x3) | LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY | LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(0) | LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD | LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG2); writel(LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT, priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL); return jr2_vlant_wait_for_completion(priv); } static int jr2_write_hwaddr(struct udevice *dev) { struct jr2_private *priv = dev_get_priv(dev); struct eth_pdata *pdata = dev_get_platdata(dev); return jr2_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); } static void serdes_setup(struct jr2_private *priv) { size_t mask; int i = 0; for (i = 0; i < MAX_PORT; ++i) { if (!priv->ports[i].bus || priv->ports[i].serdes_index == 0xff) continue; mask = BIT(priv->ports[i].serdes_index); if (priv->ports[i].serdes_index < SERDES1G_MAX) { serdes1g_setup(priv->regs[HSIO], mask, priv->ports[i].phy_mode); } else { mask >>= SERDES6G(0); serdes6g_setup(priv->regs[HSIO], mask, priv->ports[i].phy_mode); } } } static int jr2_start(struct udevice *dev) { struct jr2_private *priv = dev_get_priv(dev); struct eth_pdata *pdata = dev_get_platdata(dev); const unsigned char mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int ret; ret = jr2_initialize(priv); if (ret) return ret; /* Set MAC address tables entries for CPU redirection */ ret = jr2_mac_table_add(priv, mac, PGID_BROADCAST); if (ret) return ret; ret = jr2_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); if (ret) return ret; serdes_setup(priv); return 0; } static void jr2_stop(struct udevice *dev) { } static int jr2_send(struct udevice *dev, void *packet, int length) { struct jr2_private *priv = dev_get_priv(dev); u32 ifh[IFH_LEN]; u32 *buf = packet; memset(ifh, '\0', IFH_LEN); /* Set DST PORT_MASK */ ifh[0] = htonl(0); ifh[1] = htonl(0x1FFFFF); ifh[2] = htonl(~0); /* Set DST_MODE to INJECT and UPDATE_FCS */ ifh[5] = htonl(0x4c0); return mscc_send(priv->regs[QS], jr2_regs_qs, ifh, IFH_LEN, buf, length); } static int jr2_recv(struct udevice *dev, int flags, uchar **packetp) { struct jr2_private *priv = dev_get_priv(dev); u32 *rxbuf = (u32 *)net_rx_packets[0]; int byte_cnt = 0; byte_cnt = mscc_recv(priv->regs[QS], jr2_regs_qs, rxbuf, IFH_LEN, false); *packetp = net_rx_packets[0]; return byte_cnt; } static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size) { int i = 0; for (i = 0; i < JR2_MIIM_BUS_COUNT; ++i) if (miim[i].miim_base == base && miim[i].miim_size == size) return miim[i].bus; return NULL; } static void add_port_entry(struct jr2_private *priv, size_t index, size_t phy_addr, struct mii_dev *bus, u8 serdes_index, u8 phy_mode) { priv->ports[index].phy_addr = phy_addr; priv->ports[index].bus = bus; priv->ports[index].serdes_index = serdes_index; priv->ports[index].phy_mode = phy_mode; } static int jr2_probe(struct udevice *dev) { struct jr2_private *priv = dev_get_priv(dev); int i; int ret; struct resource res; fdt32_t faddr; phys_addr_t addr_base; unsigned long addr_size; ofnode eth_node, node, mdio_node; size_t phy_addr; struct mii_dev *bus; struct ofnode_phandle_args phandle; struct phy_device *phy; if (!priv) return -EINVAL; /* Get registers and map them to the private structure */ for (i = 0; i < ARRAY_SIZE(regs_names); i++) { priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]); if (!priv->regs[i]) { debug ("Error can't get regs base addresses for %s\n", regs_names[i]); return -ENOMEM; } } /* Initialize miim buses */ memset(&miim, 0x0, sizeof(struct mscc_miim_dev) * JR2_MIIM_BUS_COUNT); /* iterate all the ports and find out on which bus they are */ i = 0; eth_node = dev_read_first_subnode(dev); for (node = ofnode_first_subnode(eth_node); ofnode_valid(node); node = ofnode_next_subnode(node)) { if (ofnode_read_resource(node, 0, &res)) return -ENOMEM; i = res.start; ret = ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, &phandle); if (ret) continue; /* Get phy address on mdio bus */ if (ofnode_read_resource(phandle.node, 0, &res)) return -ENOMEM; phy_addr = res.start; /* Get mdio node */ mdio_node = ofnode_get_parent(phandle.node); if (ofnode_read_resource(mdio_node, 0, &res)) return -ENOMEM; faddr = cpu_to_fdt32(res.start); addr_base = ofnode_translate_address(mdio_node, &faddr); addr_size = res.end - res.start; /* If the bus is new then create a new bus */ if (!get_mdiobus(addr_base, addr_size)) priv->bus[miim_count] = mscc_mdiobus_init(miim, &miim_count, addr_base, addr_size); /* Connect mdio bus with the port */ bus = get_mdiobus(addr_base, addr_size); /* Get serdes info */ ret = ofnode_parse_phandle_with_args(node, "phys", NULL, 3, 0, &phandle); if (ret) return -ENOMEM; add_port_entry(priv, i, phy_addr, bus, phandle.args[1], phandle.args[2]); } for (i = 0; i < MAX_PORT; i++) { if (!priv->ports[i].bus) continue; phy = phy_connect(priv->ports[i].bus, priv->ports[i].phy_addr, dev, PHY_INTERFACE_MODE_NONE); if (phy) board_phy_config(phy); } return 0; } static int jr2_remove(struct udevice *dev) { struct jr2_private *priv = dev_get_priv(dev); int i; for (i = 0; i < JR2_MIIM_BUS_COUNT; i++) { mdio_unregister(priv->bus[i]); mdio_free(priv->bus[i]); } return 0; } static const struct eth_ops jr2_ops = { .start = jr2_start, .stop = jr2_stop, .send = jr2_send, .recv = jr2_recv, .write_hwaddr = jr2_write_hwaddr, }; static const struct udevice_id mscc_jr2_ids[] = { {.compatible = "mscc,vsc7454-switch" }, { /* Sentinel */ } }; U_BOOT_DRIVER(jr2) = { .name = "jr2-switch", .id = UCLASS_ETH, .of_match = mscc_jr2_ids, .probe = jr2_probe, .remove = jr2_remove, .ops = &jr2_ops, .priv_auto = sizeof(struct jr2_private), .platdata_auto = sizeof(struct eth_pdata), };