// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2025 MediaTek Inc. * * Author: Neal Yen * Author: Weijie Gao */ #include #include #include #include #include #include #include "mtk_eth.h" /* AN8855 Register Definitions */ #define AN8855_SYS_CTRL_REG 0x100050c0 #define AN8855_SW_SYS_RST BIT(31) #define AN8855_PMCR_REG(p) (0x10210000 + (p) * 0x200) #define AN8855_FORCE_MODE_LNK BIT(31) #define AN8855_FORCE_MODE 0xb31593f0 #define AN8855_PORT_CTRL_BASE 0x10208000 #define AN8855_PORT_CTRL_REG(p, r) (AN8855_PORT_CTRL_BASE + (p) * 0x200 + (r)) #define AN8855_PORTMATRIX_REG(p) AN8855_PORT_CTRL_REG(p, 0x44) #define AN8855_PVC(p) AN8855_PORT_CTRL_REG(p, 0x10) #define AN8855_STAG_VPID_S 16 #define AN8855_STAG_VPID_M 0xffff0000 #define AN8855_VLAN_ATTR_S 6 #define AN8855_VLAN_ATTR_M 0xc0 #define VLAN_ATTR_USER 0 #define AN8855_INT_MASK 0x100050F0 #define AN8855_INT_SYS_BIT BIT(15) #define AN8855_RG_CLK_CPU_ICG 0x10005034 #define AN8855_MCU_ENABLE BIT(3) #define AN8855_RG_TIMER_CTL 0x1000a100 #define AN8855_WDOG_ENABLE BIT(25) #define AN8855_CKGCR 0x10213e1c #define AN8855_SCU_BASE 0x10000000 #define AN8855_RG_RGMII_TXCK_C (AN8855_SCU_BASE + 0x1d0) #define AN8855_RG_GPIO_LED_MODE (AN8855_SCU_BASE + 0x0054) #define AN8855_RG_GPIO_LED_SEL(i) (AN8855_SCU_BASE + (0x0058 + ((i) * 4))) #define AN8855_RG_INTB_MODE (AN8855_SCU_BASE + 0x0080) #define AN8855_RG_GDMP_RAM (AN8855_SCU_BASE + 0x10000) #define AN8855_RG_GPIO_L_INV (AN8855_SCU_BASE + 0x0010) #define AN8855_RG_GPIO_CTRL (AN8855_SCU_BASE + 0xa300) #define AN8855_RG_GPIO_DATA (AN8855_SCU_BASE + 0xa304) #define AN8855_RG_GPIO_OE (AN8855_SCU_BASE + 0xa314) #define AN8855_HSGMII_AN_CSR_BASE 0x10220000 #define AN8855_SGMII_REG_AN0 (AN8855_HSGMII_AN_CSR_BASE + 0x000) #define AN8855_SGMII_REG_AN_13 (AN8855_HSGMII_AN_CSR_BASE + 0x034) #define AN8855_SGMII_REG_AN_FORCE_CL37 (AN8855_HSGMII_AN_CSR_BASE + 0x060) #define AN8855_HSGMII_CSR_PCS_BASE 0x10220000 #define AN8855_RG_HSGMII_PCS_CTROL_1 (AN8855_HSGMII_CSR_PCS_BASE + 0xa00) #define AN8855_RG_AN_SGMII_MODE_FORCE (AN8855_HSGMII_CSR_PCS_BASE + 0xa24) #define AN8855_MULTI_SGMII_CSR_BASE 0x10224000 #define AN8855_SGMII_STS_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x018) #define AN8855_MSG_RX_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x100) #define AN8855_MSG_RX_LIK_STS_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x514) #define AN8855_MSG_RX_LIK_STS_2 (AN8855_MULTI_SGMII_CSR_BASE + 0x51c) #define AN8855_PHY_RX_FORCE_CTRL_0 (AN8855_MULTI_SGMII_CSR_BASE + 0x520) #define AN8855_XFI_CSR_PCS_BASE 0x10225000 #define AN8855_RG_USXGMII_AN_CONTROL_0 (AN8855_XFI_CSR_PCS_BASE + 0xbf8) #define AN8855_MULTI_PHY_RA_CSR_BASE 0x10226000 #define AN8855_RG_RATE_ADAPT_CTRL_0 (AN8855_MULTI_PHY_RA_CSR_BASE + 0x000) #define AN8855_RATE_ADP_P0_CTRL_0 (AN8855_MULTI_PHY_RA_CSR_BASE + 0x100) #define AN8855_MII_RA_AN_ENABLE (AN8855_MULTI_PHY_RA_CSR_BASE + 0x300) #define AN8855_QP_DIG_CSR_BASE 0x1022a000 #define AN8855_QP_CK_RST_CTRL_4 (AN8855_QP_DIG_CSR_BASE + 0x310) #define AN8855_QP_DIG_MODE_CTRL_0 (AN8855_QP_DIG_CSR_BASE + 0x324) #define AN8855_QP_DIG_MODE_CTRL_1 (AN8855_QP_DIG_CSR_BASE + 0x330) #define AN8855_QP_PMA_TOP_BASE 0x1022e000 #define AN8855_PON_RXFEDIG_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x100) #define AN8855_PON_RXFEDIG_CTRL_9 (AN8855_QP_PMA_TOP_BASE + 0x124) #define AN8855_SS_LCPLL_PWCTL_SETTING_2 (AN8855_QP_PMA_TOP_BASE + 0x208) #define AN8855_SS_LCPLL_TDC_FLT_2 (AN8855_QP_PMA_TOP_BASE + 0x230) #define AN8855_SS_LCPLL_TDC_FLT_5 (AN8855_QP_PMA_TOP_BASE + 0x23c) #define AN8855_SS_LCPLL_TDC_PCW_1 (AN8855_QP_PMA_TOP_BASE + 0x248) #define AN8855_INTF_CTRL_8 (AN8855_QP_PMA_TOP_BASE + 0x320) #define AN8855_INTF_CTRL_9 (AN8855_QP_PMA_TOP_BASE + 0x324) #define AN8855_PLL_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x400) #define AN8855_PLL_CTRL_2 (AN8855_QP_PMA_TOP_BASE + 0x408) #define AN8855_PLL_CTRL_3 (AN8855_QP_PMA_TOP_BASE + 0x40c) #define AN8855_PLL_CTRL_4 (AN8855_QP_PMA_TOP_BASE + 0x410) #define AN8855_PLL_CK_CTRL_0 (AN8855_QP_PMA_TOP_BASE + 0x414) #define AN8855_RX_DLY_0 (AN8855_QP_PMA_TOP_BASE + 0x614) #define AN8855_RX_CTRL_2 (AN8855_QP_PMA_TOP_BASE + 0x630) #define AN8855_RX_CTRL_5 (AN8855_QP_PMA_TOP_BASE + 0x63c) #define AN8855_RX_CTRL_6 (AN8855_QP_PMA_TOP_BASE + 0x640) #define AN8855_RX_CTRL_7 (AN8855_QP_PMA_TOP_BASE + 0x644) #define AN8855_RX_CTRL_8 (AN8855_QP_PMA_TOP_BASE + 0x648) #define AN8855_RX_CTRL_26 (AN8855_QP_PMA_TOP_BASE + 0x690) #define AN8855_RX_CTRL_42 (AN8855_QP_PMA_TOP_BASE + 0x6d0) #define AN8855_QP_ANA_CSR_BASE 0x1022f000 #define AN8855_RG_QP_RX_DAC_EN (AN8855_QP_ANA_CSR_BASE + 0x00) #define AN8855_RG_QP_RXAFE_RESERVE (AN8855_QP_ANA_CSR_BASE + 0x04) #define AN8855_RG_QP_CDR_LPF_MJV_LIM (AN8855_QP_ANA_CSR_BASE + 0x0c) #define AN8855_RG_QP_CDR_LPF_SETVALUE (AN8855_QP_ANA_CSR_BASE + 0x14) #define AN8855_RG_QP_CDR_PR_CKREF_DIV1 (AN8855_QP_ANA_CSR_BASE + 0x18) #define AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE (AN8855_QP_ANA_CSR_BASE + 0x1c) #define AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF (AN8855_QP_ANA_CSR_BASE + 0x20) #define AN8855_RG_QP_TX_MODE_16B_EN (AN8855_QP_ANA_CSR_BASE + 0x28) #define AN8855_RG_QP_PLL_IPLL_DIG_PWR_SEL (AN8855_QP_ANA_CSR_BASE + 0x3c) #define AN8855_RG_QP_PLL_SDM_ORD (AN8855_QP_ANA_CSR_BASE + 0x40) #define AN8855_ETHER_SYS_BASE 0x1028c800 #define RG_GPHY_AFE_PWD (AN8855_ETHER_SYS_BASE + 0x40) #define AN8855_PKG_SEL 0x10000094 #define PAG_SEL_AN8855H 0x2 /* PHY LED Register bitmap of define */ #define PHY_LED_CTRL_SELECT 0x3e8 #define PHY_SINGLE_LED_ON_CTRL(i) (0x3e0 + ((i) * 2)) #define PHY_SINGLE_LED_BLK_CTRL(i) (0x3e1 + ((i) * 2)) #define PHY_SINGLE_LED_ON_DUR(i) (0x3e9 + ((i) * 2)) #define PHY_SINGLE_LED_BLK_DUR(i) (0x3ea + ((i) * 2)) #define PHY_PMA_CTRL 0x340 #define PHY_DEV1F 0x1f #define PHY_LED_ON_CTRL(i) (0x24 + ((i) * 2)) #define LED_ON_EN BIT(15) #define LED_ON_POL BIT(14) #define LED_ON_EVT_MASK 0x7f /* LED ON Event */ #define LED_ON_EVT_FORCE BIT(6) #define LED_ON_EVT_LINK_HD BIT(5) #define LED_ON_EVT_LINK_FD BIT(4) #define LED_ON_EVT_LINK_DOWN BIT(3) #define LED_ON_EVT_LINK_10M BIT(2) #define LED_ON_EVT_LINK_100M BIT(1) #define LED_ON_EVT_LINK_1000M BIT(0) #define PHY_LED_BLK_CTRL(i) (0x25 + ((i) * 2)) #define LED_BLK_EVT_MASK 0x3ff /* LED Blinking Event */ #define LED_BLK_EVT_FORCE BIT(9) #define LED_BLK_EVT_10M_RX_ACT BIT(5) #define LED_BLK_EVT_10M_TX_ACT BIT(4) #define LED_BLK_EVT_100M_RX_ACT BIT(3) #define LED_BLK_EVT_100M_TX_ACT BIT(2) #define LED_BLK_EVT_1000M_RX_ACT BIT(1) #define LED_BLK_EVT_1000M_TX_ACT BIT(0) #define PHY_LED_BCR (0x21) #define LED_BCR_EXT_CTRL BIT(15) #define LED_BCR_CLK_EN BIT(3) #define LED_BCR_TIME_TEST BIT(2) #define LED_BCR_MODE_MASK 3 #define LED_BCR_MODE_DISABLE 0 #define PHY_LED_ON_DUR 0x22 #define LED_ON_DUR_MASK 0xffff #define PHY_LED_BLK_DUR 0x23 #define LED_BLK_DUR_MASK 0xffff #define PHY_LED_BLINK_DUR_CTRL 0x720 /* Definition of LED */ #define LED_ON_EVENT (LED_ON_EVT_LINK_1000M | \ LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M |\ LED_ON_EVT_LINK_HD | LED_ON_EVT_LINK_FD) #define LED_BLK_EVENT (LED_BLK_EVT_1000M_TX_ACT | \ LED_BLK_EVT_1000M_RX_ACT | \ LED_BLK_EVT_100M_TX_ACT | \ LED_BLK_EVT_100M_RX_ACT | \ LED_BLK_EVT_10M_TX_ACT | \ LED_BLK_EVT_10M_RX_ACT) #define LED_FREQ AIR_LED_BLK_DUR_64M #define AN8855_NUM_PHYS 5 #define AN8855_NUM_PORTS 6 #define AN8855_PHY_ADDR(base, addr) (((base) + (addr)) & 0x1f) /* PHY LED Register bitmap of define */ #define PHY_LED_CTRL_SELECT 0x3e8 #define PHY_SINGLE_LED_ON_CTRL(i) (0x3e0 + ((i) * 2)) #define PHY_SINGLE_LED_BLK_CTRL(i) (0x3e1 + ((i) * 2)) #define PHY_SINGLE_LED_ON_DUR(i) (0x3e9 + ((i) * 2)) #define PHY_SINGLE_LED_BLK_DUR(i) (0x3ea + ((i) * 2)) /* AN8855 LED */ enum an8855_led_blk_dur { AIR_LED_BLK_DUR_32M, AIR_LED_BLK_DUR_64M, AIR_LED_BLK_DUR_128M, AIR_LED_BLK_DUR_256M, AIR_LED_BLK_DUR_512M, AIR_LED_BLK_DUR_1024M, AIR_LED_BLK_DUR_LAST }; enum an8855_led_polarity { LED_LOW, LED_HIGH, }; enum an8855_led_mode { AN8855_LED_MODE_DISABLE, AN8855_LED_MODE_USER_DEFINE, AN8855_LED_MODE_LAST }; enum phy_led_idx { P0_LED0, P0_LED1, P0_LED2, P0_LED3, P1_LED0, P1_LED1, P1_LED2, P1_LED3, P2_LED0, P2_LED1, P2_LED2, P2_LED3, P3_LED0, P3_LED1, P3_LED2, P3_LED3, P4_LED0, P4_LED1, P4_LED2, P4_LED3, PHY_LED_MAX }; struct an8855_led_cfg { u16 en; u8 phy_led_idx; u16 pol; u16 on_cfg; u16 blk_cfg; u8 led_freq; }; struct an8855_switch_priv { struct mtk_eth_switch_priv epriv; struct mii_dev *mdio_bus; u32 phy_base; }; /* AN8855 Reference Board */ static const struct an8855_led_cfg led_cfg[] = { /************************************************************************* * Enable, LED idx, LED Polarity, LED ON event, LED Blink event LED Freq ************************************************************************* */ /* GPIO0 */ {1, P4_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO1 */ {1, P4_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO2 */ {1, P0_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO3 */ {1, P0_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO4 */ {1, P1_LED0, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO5 */ {1, P1_LED1, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO6 */ {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO7 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO8 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO9 */ {1, P2_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO10 */ {1, P2_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO11 */ {1, P3_LED0, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO12 */ {1, P3_LED1, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO13 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO14 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO15 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO16 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO17 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO18 */ {0, PHY_LED_MAX, LED_HIGH, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO19 */ {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, /* GPIO20 */ {0, PHY_LED_MAX, LED_LOW, LED_ON_EVENT, LED_BLK_EVENT, LED_FREQ}, }; static int __an8855_reg_read(struct mtk_eth_priv *priv, u8 phy_base, u32 reg, u32 *data) { int ret, low_word, high_word; ret = mtk_mii_write(priv, phy_base, 0x1f, 0x4); if (ret) return ret; ret = mtk_mii_write(priv, phy_base, 0x10, 0); if (ret) return ret; ret = mtk_mii_write(priv, phy_base, 0x15, ((reg >> 16) & 0xFFFF)); if (ret) return ret; ret = mtk_mii_write(priv, phy_base, 0x16, (reg & 0xFFFF)); if (ret) return ret; low_word = mtk_mii_read(priv, phy_base, 0x18); if (low_word < 0) return low_word; high_word = mtk_mii_read(priv, phy_base, 0x17); if (high_word < 0) return high_word; ret = mtk_mii_write(priv, phy_base, 0x1f, 0); if (ret) return ret; ret = mtk_mii_write(priv, phy_base, 0x10, 0); if (ret) return ret; if (data) *data = ((u32)high_word << 16) | (low_word & 0xffff); return 0; } static int an8855_reg_read(struct an8855_switch_priv *priv, u32 reg, u32 *data) { return __an8855_reg_read(priv->epriv.eth, priv->phy_base, reg, data); } static int an8855_reg_write(struct an8855_switch_priv *priv, u32 reg, u32 data) { int ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0x4); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x11, ((reg >> 16) & 0xFFFF)); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x12, (reg & 0xFFFF)); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x13, ((data >> 16) & 0xFFFF)); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x14, (data & 0xFFFF)); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x1f, 0); if (ret) return ret; ret = mtk_mii_write(priv->epriv.eth, priv->phy_base, 0x10, 0); if (ret) return ret; return 0; } static int an8855_phy_cl45_read(struct an8855_switch_priv *priv, int port, int devad, int regnum, u16 *data) { u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port); *data = mtk_mmd_ind_read(priv->epriv.eth, phy_addr, devad, regnum); return 0; } static int an8855_phy_cl45_write(struct an8855_switch_priv *priv, int port, int devad, int regnum, u16 data) { u16 phy_addr = AN8855_PHY_ADDR(priv->phy_base, port); mtk_mmd_ind_write(priv->epriv.eth, phy_addr, devad, regnum, data); return 0; } static int an8855_port_sgmii_init(struct an8855_switch_priv *priv, u32 port) { u32 val = 0; if (port != 5) { printf("an8855: port %d is not a SGMII port\n", port); return -EINVAL; } /* PLL */ an8855_reg_read(priv, AN8855_QP_DIG_MODE_CTRL_1, &val); val &= ~(0x3 << 2); val |= (0x1 << 2); an8855_reg_write(priv, AN8855_QP_DIG_MODE_CTRL_1, val); /* PLL - LPF */ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); val &= ~(0x3 << 0); val |= (0x1 << 0); val &= ~(0x7 << 2); val |= (0x5 << 2); val &= ~GENMASK(7, 6); val &= ~(0x7 << 8); val |= (0x3 << 8); val |= BIT(29); val &= ~GENMASK(13, 12); an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); /* PLL - ICO */ an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val); val |= BIT(2); an8855_reg_write(priv, AN8855_PLL_CTRL_4, val); an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); val &= ~BIT(14); an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); /* PLL - CHP */ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); val &= ~(0xf << 16); val |= (0x6 << 16); an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); /* PLL - PFD */ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); val &= ~(0x3 << 20); val |= (0x1 << 20); val &= ~(0x3 << 24); val |= (0x1 << 24); val &= ~BIT(26); an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); /* PLL - POSTDIV */ an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); val |= BIT(22); val &= ~BIT(27); val &= ~BIT(28); an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); /* PLL - SDM */ an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val); val &= ~GENMASK(4, 3); an8855_reg_write(priv, AN8855_PLL_CTRL_4, val); an8855_reg_read(priv, AN8855_PLL_CTRL_2, &val); val &= ~BIT(30); an8855_reg_write(priv, AN8855_PLL_CTRL_2, val); an8855_reg_read(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, &val); val &= ~(0x3 << 16); val |= (0x1 << 16); an8855_reg_write(priv, AN8855_SS_LCPLL_PWCTL_SETTING_2, val); an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_2, 0x7a000000); an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_PCW_1, 0x7a000000); an8855_reg_read(priv, AN8855_SS_LCPLL_TDC_FLT_5, &val); val &= ~BIT(24); an8855_reg_write(priv, AN8855_SS_LCPLL_TDC_FLT_5, val); an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val); val &= ~BIT(8); an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val); /* PLL - SS */ an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val); val &= ~GENMASK(15, 0); an8855_reg_write(priv, AN8855_PLL_CTRL_3, val); an8855_reg_read(priv, AN8855_PLL_CTRL_4, &val); val &= ~GENMASK(1, 0); an8855_reg_write(priv, AN8855_PLL_CTRL_4, val); an8855_reg_read(priv, AN8855_PLL_CTRL_3, &val); val &= ~GENMASK(31, 16); an8855_reg_write(priv, AN8855_PLL_CTRL_3, val); /* PLL - TDC */ an8855_reg_read(priv, AN8855_PLL_CK_CTRL_0, &val); val &= ~BIT(9); an8855_reg_write(priv, AN8855_PLL_CK_CTRL_0, val); an8855_reg_read(priv, AN8855_RG_QP_PLL_SDM_ORD, &val); val |= BIT(3); val |= BIT(4); an8855_reg_write(priv, AN8855_RG_QP_PLL_SDM_ORD, val); an8855_reg_read(priv, AN8855_RG_QP_RX_DAC_EN, &val); val &= ~(0x3 << 16); val |= (0x2 << 16); an8855_reg_write(priv, AN8855_RG_QP_RX_DAC_EN, val); /* TCL Disable (only for Co-SIM) */ an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_0, &val); val &= ~BIT(12); an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_0, val); /* TX Init */ an8855_reg_read(priv, AN8855_RG_QP_TX_MODE_16B_EN, &val); val &= ~BIT(0); val &= ~(0xffff << 16); val |= (0x4 << 16); an8855_reg_write(priv, AN8855_RG_QP_TX_MODE_16B_EN, val); /* RX Control */ an8855_reg_read(priv, AN8855_RG_QP_RXAFE_RESERVE, &val); val |= BIT(11); an8855_reg_write(priv, AN8855_RG_QP_RXAFE_RESERVE, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, &val); val &= ~(0x3 << 4); val |= (0x1 << 4); an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_MJV_LIM, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, &val); val &= ~(0xf << 25); val |= (0x1 << 25); val &= ~(0x7 << 29); val |= (0x3 << 29); an8855_reg_write(priv, AN8855_RG_QP_CDR_LPF_SETVALUE, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val); val &= ~(0x1f << 8); val |= (0xf << 8); an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val); val &= ~(0x3f << 0); val |= (0x19 << 0); val &= ~BIT(6); an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, &val); val &= ~(0x7f << 6); val |= (0x21 << 6); val &= ~(0x3 << 16); val |= (0x2 << 16); val &= ~BIT(13); an8855_reg_write(priv, AN8855_RG_QP_CDR_FORCE_IBANDLPF_R_OFF, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, &val); val &= ~BIT(30); an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_KBAND_DIV_PCIE, val); an8855_reg_read(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, &val); val &= ~(0x7 << 24); val |= (0x4 << 24); an8855_reg_write(priv, AN8855_RG_QP_CDR_PR_CKREF_DIV1, val); an8855_reg_read(priv, AN8855_PLL_CTRL_0, &val); val |= BIT(0); an8855_reg_write(priv, AN8855_PLL_CTRL_0, val); an8855_reg_read(priv, AN8855_RX_CTRL_26, &val); val &= ~BIT(23); val |= BIT(26); an8855_reg_write(priv, AN8855_RX_CTRL_26, val); an8855_reg_read(priv, AN8855_RX_DLY_0, &val); val &= ~(0xff << 0); val |= (0x6f << 0); val |= GENMASK(13, 8); an8855_reg_write(priv, AN8855_RX_DLY_0, val); an8855_reg_read(priv, AN8855_RX_CTRL_42, &val); val &= ~(0x1fff << 0); val |= (0x150 << 0); an8855_reg_write(priv, AN8855_RX_CTRL_42, val); an8855_reg_read(priv, AN8855_RX_CTRL_2, &val); val &= ~(0x1fff << 16); val |= (0x150 << 16); an8855_reg_write(priv, AN8855_RX_CTRL_2, val); an8855_reg_read(priv, AN8855_PON_RXFEDIG_CTRL_9, &val); val &= ~(0x7 << 0); val |= (0x1 << 0); an8855_reg_write(priv, AN8855_PON_RXFEDIG_CTRL_9, val); an8855_reg_read(priv, AN8855_RX_CTRL_8, &val); val &= ~(0xfff << 16); val |= (0x200 << 16); val &= ~(0x7fff << 14); val |= (0xfff << 14); an8855_reg_write(priv, AN8855_RX_CTRL_8, val); /* Frequency memter */ an8855_reg_read(priv, AN8855_RX_CTRL_5, &val); val &= ~(0xfffff << 10); val |= (0x10 << 10); an8855_reg_write(priv, AN8855_RX_CTRL_5, val); an8855_reg_read(priv, AN8855_RX_CTRL_6, &val); val &= ~(0xfffff << 0); val |= (0x64 << 0); an8855_reg_write(priv, AN8855_RX_CTRL_6, val); an8855_reg_read(priv, AN8855_RX_CTRL_7, &val); val &= ~(0xfffff << 0); val |= (0x2710 << 0); an8855_reg_write(priv, AN8855_RX_CTRL_7, val); /* PCS Init */ an8855_reg_read(priv, AN8855_RG_HSGMII_PCS_CTROL_1, &val); val &= ~BIT(30); an8855_reg_write(priv, AN8855_RG_HSGMII_PCS_CTROL_1, val); /* Rate Adaption */ an8855_reg_read(priv, AN8855_RATE_ADP_P0_CTRL_0, &val); val &= ~BIT(31); an8855_reg_write(priv, AN8855_RATE_ADP_P0_CTRL_0, val); an8855_reg_read(priv, AN8855_RG_RATE_ADAPT_CTRL_0, &val); val |= BIT(0); val |= BIT(4); val |= GENMASK(27, 26); an8855_reg_write(priv, AN8855_RG_RATE_ADAPT_CTRL_0, val); /* Disable AN */ an8855_reg_read(priv, AN8855_SGMII_REG_AN0, &val); val &= ~BIT(12); an8855_reg_write(priv, AN8855_SGMII_REG_AN0, val); /* Force Speed */ an8855_reg_read(priv, AN8855_SGMII_STS_CTRL_0, &val); val |= BIT(2); val |= GENMASK(5, 4); an8855_reg_write(priv, AN8855_SGMII_STS_CTRL_0, val); /* bypass flow control to MAC */ an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_0, 0x01010107); an8855_reg_write(priv, AN8855_MSG_RX_LIK_STS_2, 0x00000EEF); return 0; } static void an8855_led_set_usr_def(struct an8855_switch_priv *priv, u8 entity, enum an8855_led_polarity pol, u16 on_evt, u16 blk_evt, u8 led_freq) { u32 cl45_data; if (pol == LED_HIGH) on_evt |= LED_ON_POL; else on_evt &= ~LED_ON_POL; /* LED on event */ an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_SINGLE_LED_ON_CTRL(entity % 4), on_evt | LED_ON_EN); /* LED blink event */ an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_SINGLE_LED_BLK_CTRL(entity % 4), blk_evt); /* LED freq */ switch (led_freq) { case AIR_LED_BLK_DUR_32M: cl45_data = 0x30e; break; case AIR_LED_BLK_DUR_64M: cl45_data = 0x61a; break; case AIR_LED_BLK_DUR_128M: cl45_data = 0xc35; break; case AIR_LED_BLK_DUR_256M: cl45_data = 0x186a; break; case AIR_LED_BLK_DUR_512M: cl45_data = 0x30d4; break; case AIR_LED_BLK_DUR_1024M: cl45_data = 0x61a8; break; default: cl45_data = 0; break; } an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_SINGLE_LED_BLK_DUR(entity % 4), cl45_data); an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_SINGLE_LED_ON_DUR(entity % 4), (cl45_data >> 1)); /* Disable DATA & BAD_SSD for port LED blink behavior */ cl45_data = mtk_mmd_ind_read(priv->epriv.eth, (entity / 4), 0x1e, PHY_PMA_CTRL); cl45_data &= ~BIT(0); cl45_data &= ~BIT(15); an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_PMA_CTRL, cl45_data); } static int an8855_led_set_mode(struct an8855_switch_priv *priv, u8 mode) { u16 cl45_data; an8855_phy_cl45_read(priv, 0, 0x1f, PHY_LED_BCR, &cl45_data); switch (mode) { case AN8855_LED_MODE_DISABLE: cl45_data &= ~LED_BCR_EXT_CTRL; cl45_data &= ~LED_BCR_MODE_MASK; cl45_data |= LED_BCR_MODE_DISABLE; break; case AN8855_LED_MODE_USER_DEFINE: cl45_data |= LED_BCR_EXT_CTRL; cl45_data |= LED_BCR_CLK_EN; break; default: printf("an8855: LED mode%d is not supported!\n", mode); return -EINVAL; } an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BCR, cl45_data); return 0; } static int an8855_led_set_state(struct an8855_switch_priv *priv, u8 entity, u8 state) { u16 cl45_data = 0; /* Change to per port contorl */ an8855_phy_cl45_read(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT, &cl45_data); if (state == 1) cl45_data |= (1 << (entity % 4)); else cl45_data &= ~(1 << (entity % 4)); an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_LED_CTRL_SELECT, cl45_data); /* LED enable setting */ an8855_phy_cl45_read(priv, (entity / 4), 0x1e, PHY_SINGLE_LED_ON_CTRL(entity % 4), &cl45_data); if (state == 1) cl45_data |= LED_ON_EN; else cl45_data &= ~LED_ON_EN; an8855_phy_cl45_write(priv, (entity / 4), 0x1e, PHY_SINGLE_LED_ON_CTRL(entity % 4), cl45_data); return 0; } static int an8855_led_init(struct an8855_switch_priv *priv) { u32 val, id, tmp_id = 0; int ret; ret = an8855_led_set_mode(priv, AN8855_LED_MODE_USER_DEFINE); if (ret) { printf("an8855: led_set_mode failed with %d!\n", ret); return ret; } for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { ret = an8855_led_set_state(priv, led_cfg[id].phy_led_idx, led_cfg[id].en); if (ret != 0) { printf("an8855: led_set_state failed with %d!\n", ret); return ret; } if (led_cfg[id].en == 1) { an8855_led_set_usr_def(priv, led_cfg[id].phy_led_idx, led_cfg[id].pol, led_cfg[id].on_cfg, led_cfg[id].blk_cfg, led_cfg[id].led_freq); } } /* Setting for System LED & Loop LED */ an8855_reg_write(priv, AN8855_RG_GPIO_OE, 0x0); an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x0); an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, 0); an8855_reg_write(priv, AN8855_RG_GPIO_CTRL, 0x1001); an8855_reg_read(priv, AN8855_RG_GPIO_DATA, &val); val |= GENMASK(3, 1); val &= ~(BIT(0)); val &= ~(BIT(6)); an8855_reg_write(priv, AN8855_RG_GPIO_DATA, val); an8855_reg_read(priv, AN8855_RG_GPIO_OE, &val); val |= 0x41; an8855_reg_write(priv, AN8855_RG_GPIO_OE, val); /* Mapping between GPIO & LED */ val = 0; for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { /* Skip GPIO6, due to GPIO6 does not support PORT LED */ if (id == 6) continue; if (led_cfg[id].en == 1) { if (id < 7) val |= led_cfg[id].phy_led_idx << ((id % 4) * 8); else val |= led_cfg[id].phy_led_idx << (((id - 1) % 4) * 8); } if (id < 7) tmp_id = id; else tmp_id = id - 1; if ((tmp_id % 4) == 0x3) { an8855_reg_write(priv, AN8855_RG_GPIO_LED_SEL(tmp_id / 4), val); val = 0; } } /* Turn on LAN LED mode */ val = 0; for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { if (led_cfg[id].en == 1) val |= 0x1 << id; } an8855_reg_write(priv, AN8855_RG_GPIO_LED_MODE, val); /* Force clear blink pulse for per port LED */ an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0x1f); udelay(1000); an8855_phy_cl45_write(priv, 0, 0x1f, PHY_LED_BLINK_DUR_CTRL, 0); return 0; } static void an8855_port_isolation(struct an8855_switch_priv *priv) { u32 i; for (i = 0; i < AN8855_NUM_PORTS; i++) { /* Set port matrix mode */ if (i != 5) an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x20); else an8855_reg_write(priv, AN8855_PORTMATRIX_REG(i), 0x1f); /* Set port mode to user port */ an8855_reg_write(priv, AN8855_PVC(i), (0x8100 << AN8855_STAG_VPID_S) | (VLAN_ATTR_USER << AN8855_VLAN_ATTR_S)); } } static void an8855_mac_control(struct mtk_eth_switch_priv *swpriv, bool enable) { struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv; u32 pmcr = AN8855_FORCE_MODE_LNK; if (enable) pmcr = AN8855_FORCE_MODE; an8855_reg_write(priv, AN8855_PMCR_REG(5), pmcr); } static int an8855_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) { struct an8855_switch_priv *priv = bus->priv; if (devad < 0) return mtk_mii_read(priv->epriv.eth, addr, reg); return mtk_mmd_ind_read(priv->epriv.eth, addr, devad, reg); } static int an8855_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, u16 val) { struct an8855_switch_priv *priv = bus->priv; if (devad < 0) return mtk_mii_write(priv->epriv.eth, addr, reg, val); return mtk_mmd_ind_write(priv->epriv.eth, addr, devad, reg, val); } static int an8855_mdio_register(struct an8855_switch_priv *priv) { struct mii_dev *mdio_bus = mdio_alloc(); int ret; if (!mdio_bus) return -ENOMEM; mdio_bus->read = an8855_mdio_read; mdio_bus->write = an8855_mdio_write; snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name); mdio_bus->priv = priv; ret = mdio_register(mdio_bus); if (ret) { mdio_free(mdio_bus); return ret; } priv->mdio_bus = mdio_bus; return 0; } static int an8855_setup(struct mtk_eth_switch_priv *swpriv) { struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv; u16 phy_addr, phy_val; u32 i, id, val = 0; int ret; priv->phy_base = 1; /* Turn off PHYs */ for (i = 0; i < AN8855_NUM_PHYS; i++) { phy_addr = AN8855_PHY_ADDR(priv->phy_base, i); phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR); phy_val |= BMCR_PDOWN; mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val); } /* Force MAC link down before reset */ an8855_reg_write(priv, AN8855_PMCR_REG(5), AN8855_FORCE_MODE_LNK); /* Switch soft reset */ an8855_reg_write(priv, AN8855_SYS_CTRL_REG, AN8855_SW_SYS_RST); mdelay(100); an8855_reg_read(priv, AN8855_PKG_SEL, &val); if ((val & 0x7) == PAG_SEL_AN8855H) { /* Release power down */ an8855_reg_write(priv, RG_GPHY_AFE_PWD, 0x0); /* Invert for LED activity change */ an8855_reg_read(priv, AN8855_RG_GPIO_L_INV, &val); for (id = 0; id < ARRAY_SIZE(led_cfg); id++) { if (led_cfg[id].pol == LED_HIGH && led_cfg[id].en == 1) val |= 0x1 << id; } an8855_reg_write(priv, AN8855_RG_GPIO_L_INV, (val | 0x1)); /* MCU NOP CMD */ an8855_reg_write(priv, AN8855_RG_GDMP_RAM, 0x846); an8855_reg_write(priv, AN8855_RG_GDMP_RAM + 4, 0x4a); /* Enable MCU */ an8855_reg_read(priv, AN8855_RG_CLK_CPU_ICG, &val); an8855_reg_write(priv, AN8855_RG_CLK_CPU_ICG, val | AN8855_MCU_ENABLE); udelay(1000); /* Disable MCU watchdog */ an8855_reg_read(priv, AN8855_RG_TIMER_CTL, &val); an8855_reg_write(priv, AN8855_RG_TIMER_CTL, (val & (~AN8855_WDOG_ENABLE))); /* LED settings for T830 reference board */ ret = an8855_led_init(priv); if (ret < 0) { printf("an8855: an8855_led_init failed with %d\n", ret); return ret; } } switch (priv->epriv.phy_interface) { case PHY_INTERFACE_MODE_2500BASEX: an8855_port_sgmii_init(priv, 5); break; default: break; } an8855_reg_read(priv, AN8855_CKGCR, &val); val &= ~(0x3); an8855_reg_write(priv, AN8855_CKGCR, val); /* Enable port isolation to block inter-port communication */ an8855_port_isolation(priv); /* Turn on PHYs */ for (i = 0; i < AN8855_NUM_PHYS; i++) { phy_addr = AN8855_PHY_ADDR(priv->phy_base, i); phy_val = mtk_mii_read(priv->epriv.eth, phy_addr, MII_BMCR); phy_val &= ~BMCR_PDOWN; mtk_mii_write(priv->epriv.eth, phy_addr, MII_BMCR, phy_val); } return an8855_mdio_register(priv); } static int an8855_cleanup(struct mtk_eth_switch_priv *swpriv) { struct an8855_switch_priv *priv = (struct an8855_switch_priv *)swpriv; mdio_unregister(priv->mdio_bus); return 0; } static int an8855_detect(struct mtk_eth_priv *priv) { int ret; u32 val; ret = __an8855_reg_read(priv, 1, 0x10005000, &val); if (ret) return ret; if (val == 0x8855) return 0; return -ENODEV; } MTK_ETH_SWITCH(an8855) = { .name = "an8855", .desc = "Airoha AN8855", .priv_size = sizeof(struct an8855_switch_priv), .reset_wait_time = 100, .detect = an8855_detect, .setup = an8855_setup, .cleanup = an8855_cleanup, .mac_control = an8855_mac_control, };