/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2018-2021 Beijing WangXun Technology Co., Ltd. */ #include "ngbe_phy_yt.h" #define YT_PHY_RST_WAIT_PERIOD 5 s32 ngbe_read_phy_reg_yt(struct ngbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data) { mdi_reg_t reg; mdi_reg_22_t reg22; reg.device_type = device_type; reg.addr = reg_addr; ngbe_mdi_map_register(®, ®22); /* Read MII reg according to media type */ if (hw->phy.media_type == ngbe_media_type_fiber) { ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, reg22.device_type, YT_SMI_PHY_SDS); ngbe_read_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data); ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, reg22.device_type, 0); } else { ngbe_read_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data); } return 0; } s32 ngbe_write_phy_reg_yt(struct ngbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data) { mdi_reg_t reg; mdi_reg_22_t reg22; reg.device_type = device_type; reg.addr = reg_addr; ngbe_mdi_map_register(®, ®22); /* Write MII reg according to media type */ if (hw->phy.media_type == ngbe_media_type_fiber) { ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, reg22.device_type, YT_SMI_PHY_SDS); ngbe_write_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data); ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, reg22.device_type, 0); } else { ngbe_write_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data); } return 0; } s32 ngbe_read_phy_reg_ext_yt(struct ngbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data) { ngbe_write_phy_reg_mdi(hw, 0x1E, device_type, reg_addr); ngbe_read_phy_reg_mdi(hw, 0x1F, device_type, phy_data); return 0; } s32 ngbe_write_phy_reg_ext_yt(struct ngbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data) { ngbe_write_phy_reg_mdi(hw, 0x1E, device_type, reg_addr); ngbe_write_phy_reg_mdi(hw, 0x1F, device_type, phy_data); return 0; } s32 ngbe_read_phy_reg_sds_ext_yt(struct ngbe_hw *hw, u32 reg_addr, u32 device_type, u16 *phy_data) { ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, device_type, YT_SMI_PHY_SDS); ngbe_read_phy_reg_ext_yt(hw, reg_addr, device_type, phy_data); ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, device_type, 0); return 0; } s32 ngbe_write_phy_reg_sds_ext_yt(struct ngbe_hw *hw, u32 reg_addr, u32 device_type, u16 phy_data) { ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, device_type, YT_SMI_PHY_SDS); ngbe_write_phy_reg_ext_yt(hw, reg_addr, device_type, phy_data); ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, device_type, 0); return 0; } s32 ngbe_init_phy_yt(struct ngbe_hw *hw) { rte_spinlock_init(&hw->phy_lock); if (hw->lsc) { rte_spinlock_lock(&hw->phy_lock); /* close sds area register */ ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, 0, 0); /* enable interrupts */ ngbe_write_phy_reg_mdi(hw, YT_INTR, 0, YT_INTR_ENA_MASK | YT_SDS_INTR_ENA_MASK); rte_spinlock_unlock(&hw->phy_lock); } hw->phy.set_phy_power(hw, false); return 0; } s32 ngbe_setup_phy_link_yt(struct ngbe_hw *hw, u32 speed, bool autoneg_wait_to_complete) { u16 value_r4 = 0; u16 value_r9 = 0; u16 value; UNREFERENCED_PARAMETER(autoneg_wait_to_complete); hw->phy.autoneg_advertised = 0; /* check chip_mode first */ rte_spinlock_lock(&hw->phy_lock); ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &value); rte_spinlock_unlock(&hw->phy_lock); if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(0)) { /* UTP to rgmii */ if (!hw->mac.autoneg) { switch (speed) { case NGBE_LINK_SPEED_1GB_FULL: value = YT_BCR_SPEED_SELECT1; break; case NGBE_LINK_SPEED_100M_FULL: value = YT_BCR_SPEED_SELECT0; break; case NGBE_LINK_SPEED_10M_FULL: value = 0; break; default: value = YT_BCR_SPEED_SELECT0 | YT_BCR_SPEED_SELECT1; DEBUGOUT("unknown speed = 0x%x.", speed); break; } /* duplex full */ value |= YT_BCR_DUPLEX | YT_BCR_RESET; rte_spinlock_lock(&hw->phy_lock); ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, value); rte_spinlock_unlock(&hw->phy_lock); goto skip_an; } rte_spinlock_lock(&hw->phy_lock); /*disable 100/10base-T Self-negotiation ability*/ ngbe_read_phy_reg_mdi(hw, YT_ANA, 0, &value); value &= ~(YT_ANA_100BASET_FULL | YT_ANA_100BASET_HALF | YT_ANA_10BASET_FULL | YT_ANA_10BASET_HALF); ngbe_write_phy_reg_mdi(hw, YT_ANA, 0, value); /*disable 1000base-T Self-negotiation ability*/ ngbe_read_phy_reg_mdi(hw, YT_MS_CTRL, 0, &value); value &= ~(YT_MS_1000BASET_FULL | YT_MS_1000BASET_HALF); ngbe_write_phy_reg_mdi(hw, YT_MS_CTRL, 0, value); if (speed & NGBE_LINK_SPEED_1GB_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; value_r9 |= YT_MS_1000BASET_FULL; } if (speed & NGBE_LINK_SPEED_100M_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL; value_r4 |= YT_ANA_100BASET_FULL; } if (speed & NGBE_LINK_SPEED_10M_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL; value_r4 |= YT_ANA_10BASET_FULL; } /* enable 1000base-T Self-negotiation ability */ ngbe_read_phy_reg_mdi(hw, YT_MS_CTRL, 0, &value); value |= value_r9; ngbe_write_phy_reg_mdi(hw, YT_MS_CTRL, 0, value); /* enable 100/10base-T Self-negotiation ability */ ngbe_read_phy_reg_mdi(hw, YT_ANA, 0, &value); value |= value_r4; ngbe_write_phy_reg_mdi(hw, YT_ANA, 0, value); /* software reset to make the above configuration take effect*/ ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &value); value |= YT_BCR_RESET | YT_BCR_ANE | YT_BCR_RESTART_AN; ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, value); rte_spinlock_unlock(&hw->phy_lock); skip_an: hw->phy.set_phy_power(hw, true); } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(1)) { /* fiber to rgmii */ hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; /* RGMII_Config1 : Config rx and tx training delay */ value = YT_RGMII_CONF1_RXDELAY | YT_RGMII_CONF1_TXDELAY_FE | YT_RGMII_CONF1_TXDELAY; rte_spinlock_lock(&hw->phy_lock); ngbe_write_phy_reg_ext_yt(hw, YT_RGMII_CONF1, 0, value); value = YT_CHIP_MODE_SEL(1) | YT_CHIP_SW_LDO_EN | YT_CHIP_SW_RST; ngbe_write_phy_reg_ext_yt(hw, YT_CHIP, 0, value); ngbe_read_phy_reg_sds_ext_yt(hw, YT_AUTO, 0, &value); value &= ~YT_AUTO_SENSING; ngbe_write_phy_reg_sds_ext_yt(hw, YT_AUTO, 0, value); ngbe_read_phy_reg_ext_yt(hw, YT_MISC, 0, &value); value |= YT_MISC_RESV; ngbe_write_phy_reg_ext_yt(hw, YT_MISC, 0, value); ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &value); value &= ~YT_CHIP_SW_RST; ngbe_write_phy_reg_ext_yt(hw, YT_CHIP, 0, value); /* software reset */ if (hw->mac.autoneg) value = YT_BCR_RESET | YT_BCR_ANE | YT_BCR_RESTART_AN | YT_BCR_DUPLEX | YT_BCR_SPEED_SELECT1; else value = YT_BCR_RESET | YT_BCR_DUPLEX | YT_BCR_SPEED_SELECT1; hw->phy.write_reg(hw, YT_BCR, 0, value); rte_spinlock_unlock(&hw->phy_lock); hw->phy.set_phy_power(hw, true); } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(2)) { hw->phy.set_phy_power(hw, true); rte_spinlock_lock(&hw->phy_lock); hw->phy.read_reg(hw, YT_SPST, 0, &value); rte_spinlock_unlock(&hw->phy_lock); if (value & YT_SPST_LINK) { /* fiber up */ hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; } else { /* utp up */ rte_spinlock_lock(&hw->phy_lock); /*disable 100/10base-T Self-negotiation ability*/ ngbe_read_phy_reg_mdi(hw, YT_ANA, 0, &value); value &= ~(YT_ANA_100BASET_FULL | YT_ANA_100BASET_HALF | YT_ANA_10BASET_FULL | YT_ANA_10BASET_HALF); ngbe_write_phy_reg_mdi(hw, YT_ANA, 0, value); /*disable 1000base-T Self-negotiation ability*/ ngbe_read_phy_reg_mdi(hw, YT_MS_CTRL, 0, &value); value &= ~(YT_MS_1000BASET_FULL | YT_MS_1000BASET_HALF); ngbe_write_phy_reg_mdi(hw, YT_MS_CTRL, 0, value); if (speed & NGBE_LINK_SPEED_1GB_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; value_r9 |= YT_MS_1000BASET_FULL; } if (speed & NGBE_LINK_SPEED_100M_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL; value_r4 |= YT_ANA_100BASET_FULL; } if (speed & NGBE_LINK_SPEED_10M_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL; value_r4 |= YT_ANA_10BASET_FULL; } /* enable 1000base-T Self-negotiation ability */ ngbe_read_phy_reg_mdi(hw, YT_MS_CTRL, 0, &value); value |= value_r9; ngbe_write_phy_reg_mdi(hw, YT_MS_CTRL, 0, value); /* enable 100/10base-T Self-negotiation ability */ ngbe_read_phy_reg_mdi(hw, YT_ANA, 0, &value); value |= value_r4; ngbe_write_phy_reg_mdi(hw, YT_ANA, 0, value); /* config for yt8531sh-ca */ ngbe_write_phy_reg_ext_yt(hw, YT_SPEC_CONF, 0, YT_SPEC_CONF_8531SH_CA); /* software reset to make the above configuration * take effect */ ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &value); value |= YT_BCR_RESET; ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, value); rte_spinlock_unlock(&hw->phy_lock); } } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(4)) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; rte_spinlock_lock(&hw->phy_lock); ngbe_read_phy_reg_ext_yt(hw, YT_RGMII_CONF1, 0, &value); value |= YT_RGMII_CONF1_MODE; ngbe_write_phy_reg_ext_yt(hw, YT_RGMII_CONF1, 0, value); ngbe_read_phy_reg_ext_yt(hw, YT_RGMII_CONF2, 0, &value); value &= ~(YT_RGMII_CONF2_SPEED_MASK | YT_RGMII_CONF2_DUPLEX | YT_RGMII_CONF2_LINKUP); value |= YT_RGMII_CONF2_SPEED(2) | YT_RGMII_CONF2_DUPLEX | YT_RGMII_CONF2_LINKUP; ngbe_write_phy_reg_ext_yt(hw, YT_RGMII_CONF2, 0, value); ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &value); value &= ~YT_SMI_PHY_SW_RST; ngbe_write_phy_reg_ext_yt(hw, YT_CHIP, 0, value); rte_spinlock_unlock(&hw->phy_lock); hw->phy.set_phy_power(hw, true); } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(5)) { /* sgmii_to_rgmii */ if (!hw->mac.autoneg) { switch (speed) { case NGBE_LINK_SPEED_1GB_FULL: value = YT_BCR_SPEED_SELECT1; break; case NGBE_LINK_SPEED_100M_FULL: value = YT_BCR_SPEED_SELECT0; break; case NGBE_LINK_SPEED_10M_FULL: value = 0; break; default: value = YT_BCR_SPEED_SELECT0 | YT_BCR_SPEED_SELECT1; DEBUGOUT("unknown speed = 0x%x", speed); break; } /* duplex full */ value |= YT_BCR_DUPLEX | YT_BCR_RESET; rte_spinlock_lock(&hw->phy_lock); hw->phy.write_reg(hw, YT_BCR, 0, value); rte_spinlock_unlock(&hw->phy_lock); goto skip_an_sr; } value = 0; if (speed & NGBE_LINK_SPEED_1GB_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL; value |= YT_BCR_SPEED_SELECT1; } if (speed & NGBE_LINK_SPEED_100M_FULL) { hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL; value |= YT_BCR_SPEED_SELECT0; } if (speed & NGBE_LINK_SPEED_10M_FULL) hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL; /* duplex full */ value |= YT_BCR_DUPLEX | YT_BCR_RESET; rte_spinlock_lock(&hw->phy_lock); hw->phy.write_reg(hw, YT_BCR, 0, value); /* software reset to make the above configuration take effect */ hw->phy.read_reg(hw, YT_BCR, 0, &value); value |= YT_BCR_RESET | YT_BCR_ANE | YT_BCR_RESTART_AN; hw->phy.write_reg(hw, 0x0, 0, value); rte_spinlock_unlock(&hw->phy_lock); skip_an_sr: hw->phy.set_phy_power(hw, true); } rte_spinlock_lock(&hw->phy_lock); ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, 0, 0); ngbe_read_phy_reg_mdi(hw, YT_INTR_STATUS, 0, &value); rte_spinlock_unlock(&hw->phy_lock); return 0; } s32 ngbe_reset_phy_yt(struct ngbe_hw *hw) { u32 i; u16 ctrl = 0; s32 status = 0; if (hw->phy.type != ngbe_phy_yt8521s && hw->phy.type != ngbe_phy_yt8521s_sfi) return NGBE_ERR_PHY_TYPE; rte_spinlock_lock(&hw->phy_lock); /* check chip_mode first */ ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &ctrl); if (ctrl & YT_CHIP_MODE_MASK) { /* fiber to rgmii */ status = hw->phy.read_reg(hw, YT_BCR, 0, &ctrl); /* sds software reset */ ctrl |= YT_BCR_RESET; status = hw->phy.write_reg(hw, YT_BCR, 0, ctrl); for (i = 0; i < YT_PHY_RST_WAIT_PERIOD; i++) { status = hw->phy.read_reg(hw, YT_BCR, 0, &ctrl); if (!(ctrl & YT_BCR_RESET)) break; msleep(1); } } else { /* UTP to rgmii */ status = ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &ctrl); /* sds software reset */ ctrl |= YT_BCR_RESET; status = ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, ctrl); for (i = 0; i < YT_PHY_RST_WAIT_PERIOD; i++) { status = ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &ctrl); if (!(ctrl & YT_BCR_RESET)) break; msleep(1); } } rte_spinlock_unlock(&hw->phy_lock); if (i == YT_PHY_RST_WAIT_PERIOD) { DEBUGOUT("PHY reset polling failed to complete."); return NGBE_ERR_RESET_FAILED; } return status; } s32 ngbe_get_phy_advertised_pause_yt(struct ngbe_hw *hw, u8 *pause_bit) { u16 value; s32 status = 0; rte_spinlock_lock(&hw->phy_lock); status = hw->phy.read_reg(hw, YT_ANA, 0, &value); rte_spinlock_unlock(&hw->phy_lock); value &= YT_FANA_PAUSE_MASK; *pause_bit = (u8)(value >> 7); return status; } s32 ngbe_get_phy_lp_advertised_pause_yt(struct ngbe_hw *hw, u8 *pause_bit) { u16 value; s32 status = 0; rte_spinlock_lock(&hw->phy_lock); status = hw->phy.read_reg(hw, YT_LPAR, 0, &value); rte_spinlock_unlock(&hw->phy_lock); value &= YT_FLPAR_PAUSE_MASK; *pause_bit = (u8)(value >> 7); return status; } s32 ngbe_set_phy_pause_adv_yt(struct ngbe_hw *hw, u16 pause_bit) { u16 value; s32 status = 0; rte_spinlock_lock(&hw->phy_lock); status = hw->phy.read_reg(hw, YT_ANA, 0, &value); value &= ~YT_FANA_PAUSE_MASK; value |= pause_bit; status = hw->phy.write_reg(hw, YT_ANA, 0, value); rte_spinlock_unlock(&hw->phy_lock); return status; } s32 ngbe_check_phy_link_yt(struct ngbe_hw *hw, u32 *speed, bool *link_up) { s32 status = 0; u16 phy_link = 0; u16 phy_speed = 0; u16 phy_data = 0; u16 insr = 0; /* Initialize speed and link to default case */ *link_up = false; *speed = NGBE_LINK_SPEED_UNKNOWN; rte_spinlock_lock(&hw->phy_lock); ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, 0, 0); ngbe_read_phy_reg_mdi(hw, YT_INTR_STATUS, 0, &insr); status = hw->phy.read_reg(hw, YT_SPST, 0, &phy_data); phy_link = phy_data & YT_SPST_LINK; phy_speed = phy_data & YT_SPST_SPEED_MASK; if (phy_link) { *link_up = true; } else { status = ngbe_read_phy_reg_mdi(hw, YT_SPST, 0, &phy_data); phy_link = phy_data & YT_SPST_LINK; phy_speed = phy_data & YT_SPST_SPEED_MASK; if (phy_link) *link_up = true; } rte_spinlock_unlock(&hw->phy_lock); if (*link_up) { if (phy_speed == YT_SPST_SPEED_1000M) *speed = NGBE_LINK_SPEED_1GB_FULL; else if (phy_speed == YT_SPST_SPEED_100M) *speed = NGBE_LINK_SPEED_100M_FULL; else if (phy_speed == YT_SPST_SPEED_10M) *speed = NGBE_LINK_SPEED_10M_FULL; } return status; } s32 ngbe_set_phy_power_yt(struct ngbe_hw *hw, bool on) { u16 value = 0; rte_spinlock_lock(&hw->phy_lock); /* power down/up in fiber mode */ hw->phy.read_reg(hw, YT_BCR, 0, &value); if (on) value &= ~YT_BCR_PWDN; else value |= YT_BCR_PWDN; hw->phy.write_reg(hw, YT_BCR, 0, value); value = 0; /* power down/up in UTP mode */ ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &value); if (on) value &= ~YT_BCR_PWDN; else value |= YT_BCR_PWDN; ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, value); rte_spinlock_unlock(&hw->phy_lock); return 0; }