| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2025 MediaTek Inc. |
| * |
| * Author: Neal Yen <neal.yen@mediatek.com> |
| * Author: Weijie Gao <weijie.gao@mediatek.com> |
| */ |
| |
| #include <phy.h> |
| #include <miiphy.h> |
| #include <linux/bitops.h> |
| #include <linux/delay.h> |
| #include <linux/mdio.h> |
| #include <linux/mii.h> |
| #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, |
| }; |