f40e9f0e22
Support to get and set flow control. Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
309 lines
7.7 KiB
C
309 lines
7.7 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2018-2021 Beijing WangXun Technology Co., Ltd.
|
|
*/
|
|
|
|
#include "ngbe_phy_mvl.h"
|
|
|
|
#define MVL_PHY_RST_WAIT_PERIOD 5
|
|
|
|
s32 ngbe_read_phy_reg_mvl(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;
|
|
|
|
if (hw->phy.media_type == ngbe_media_type_fiber)
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 1);
|
|
else
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 0);
|
|
|
|
ngbe_mdi_map_register(®, ®22);
|
|
|
|
ngbe_read_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 ngbe_write_phy_reg_mvl(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;
|
|
|
|
if (hw->phy.media_type == ngbe_media_type_fiber)
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 1);
|
|
else
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 0);
|
|
|
|
ngbe_mdi_map_register(®, ®22);
|
|
|
|
ngbe_write_phy_reg_mdi(hw, reg22.addr, reg22.device_type, phy_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 ngbe_init_phy_mvl(struct ngbe_hw *hw)
|
|
{
|
|
s32 ret_val = 0;
|
|
u16 value = 0;
|
|
int i;
|
|
|
|
DEBUGFUNC("ngbe_init_phy_mvl");
|
|
|
|
/* enable interrupts, only link status change and an done is allowed */
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 2);
|
|
ngbe_read_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, &value);
|
|
value &= ~MVL_RGM_CTL2_TTC;
|
|
value |= MVL_RGM_CTL2_RTC;
|
|
ngbe_write_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, value);
|
|
|
|
hw->phy.write_reg(hw, MVL_CTRL, 0, MVL_CTRL_RESET);
|
|
for (i = 0; i < 15; i++) {
|
|
ngbe_read_phy_reg_mdi(hw, MVL_CTRL, 0, &value);
|
|
if (value & MVL_CTRL_RESET)
|
|
msleep(1);
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (i == 15) {
|
|
DEBUGOUT("phy reset exceeds maximum waiting period.\n");
|
|
return NGBE_ERR_TIMEOUT;
|
|
}
|
|
|
|
ret_val = hw->phy.reset_hw(hw);
|
|
if (ret_val)
|
|
return ret_val;
|
|
|
|
/* set LED2 to interrupt output and INTn active low */
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
|
|
ngbe_read_phy_reg_mdi(hw, MVL_LEDTCR, 0, &value);
|
|
value |= MVL_LEDTCR_INTR_EN;
|
|
value &= ~(MVL_LEDTCR_INTR_POL);
|
|
ngbe_write_phy_reg_mdi(hw, MVL_LEDTCR, 0, value);
|
|
|
|
if (hw->phy.type == ngbe_phy_mvl_sfi) {
|
|
hw->phy.read_reg(hw, MVL_CTRL1, 0, &value);
|
|
value &= ~MVL_CTRL1_INTR_POL;
|
|
ngbe_write_phy_reg_mdi(hw, MVL_CTRL1, 0, value);
|
|
}
|
|
|
|
/* enable link status change and AN complete interrupts */
|
|
value = MVL_INTR_EN_ANC | MVL_INTR_EN_LSC;
|
|
hw->phy.write_reg(hw, MVL_INTR_EN, 0, value);
|
|
|
|
/* LED control */
|
|
ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
|
|
ngbe_read_phy_reg_mdi(hw, MVL_LEDFCR, 0, &value);
|
|
value &= ~(MVL_LEDFCR_CTL0 | MVL_LEDFCR_CTL1);
|
|
value |= MVL_LEDFCR_CTL0_CONF | MVL_LEDFCR_CTL1_CONF;
|
|
ngbe_write_phy_reg_mdi(hw, MVL_LEDFCR, 0, value);
|
|
ngbe_read_phy_reg_mdi(hw, MVL_LEDPCR, 0, &value);
|
|
value &= ~(MVL_LEDPCR_CTL0 | MVL_LEDPCR_CTL1);
|
|
value |= MVL_LEDPCR_CTL0_CONF | MVL_LEDPCR_CTL1_CONF;
|
|
ngbe_write_phy_reg_mdi(hw, MVL_LEDPCR, 0, value);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
s32 ngbe_setup_phy_link_mvl(struct ngbe_hw *hw, u32 speed,
|
|
bool autoneg_wait_to_complete)
|
|
{
|
|
u16 value_r4 = 0;
|
|
u16 value_r9 = 0;
|
|
u16 value;
|
|
|
|
DEBUGFUNC("ngbe_setup_phy_link_mvl");
|
|
UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
|
|
|
|
hw->phy.autoneg_advertised = 0;
|
|
|
|
if (hw->phy.type == ngbe_phy_mvl) {
|
|
if (speed & NGBE_LINK_SPEED_1GB_FULL) {
|
|
value_r9 |= MVL_PHY_1000BASET_FULL;
|
|
hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
|
|
}
|
|
|
|
if (speed & NGBE_LINK_SPEED_100M_FULL) {
|
|
value_r4 |= MVL_PHY_100BASET_FULL;
|
|
hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL;
|
|
}
|
|
|
|
if (speed & NGBE_LINK_SPEED_10M_FULL) {
|
|
value_r4 |= MVL_PHY_10BASET_FULL;
|
|
hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL;
|
|
}
|
|
|
|
hw->phy.read_reg(hw, MVL_ANA, 0, &value);
|
|
value &= ~(MVL_PHY_100BASET_FULL |
|
|
MVL_PHY_100BASET_HALF |
|
|
MVL_PHY_10BASET_FULL |
|
|
MVL_PHY_10BASET_HALF);
|
|
value_r4 |= value;
|
|
hw->phy.write_reg(hw, MVL_ANA, 0, value_r4);
|
|
|
|
hw->phy.read_reg(hw, MVL_PHY_1000BASET, 0, &value);
|
|
value &= ~(MVL_PHY_1000BASET_FULL |
|
|
MVL_PHY_1000BASET_HALF);
|
|
value_r9 |= value;
|
|
hw->phy.write_reg(hw, MVL_PHY_1000BASET, 0, value_r9);
|
|
} else {
|
|
hw->phy.autoneg_advertised = 1;
|
|
|
|
hw->phy.read_reg(hw, MVL_ANA, 0, &value);
|
|
value &= ~(MVL_PHY_1000BASEX_HALF | MVL_PHY_1000BASEX_FULL);
|
|
value |= MVL_PHY_1000BASEX_FULL;
|
|
hw->phy.write_reg(hw, MVL_ANA, 0, value);
|
|
}
|
|
|
|
value = MVL_CTRL_RESTART_AN | MVL_CTRL_ANE;
|
|
ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
|
|
|
|
hw->phy.read_reg(hw, MVL_INTR, 0, &value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
s32 ngbe_reset_phy_mvl(struct ngbe_hw *hw)
|
|
{
|
|
u32 i;
|
|
u16 ctrl = 0;
|
|
s32 status = 0;
|
|
|
|
DEBUGFUNC("ngbe_reset_phy_mvl");
|
|
|
|
if (hw->phy.type != ngbe_phy_mvl && hw->phy.type != ngbe_phy_mvl_sfi)
|
|
return NGBE_ERR_PHY_TYPE;
|
|
|
|
/* select page 18 reg 20 */
|
|
status = ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 18);
|
|
|
|
/* mode select to RGMII-to-copper or RGMII-to-sfi*/
|
|
if (hw->phy.type == ngbe_phy_mvl)
|
|
ctrl = MVL_GEN_CTL_MODE_COPPER;
|
|
else
|
|
ctrl = MVL_GEN_CTL_MODE_FIBER;
|
|
status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl);
|
|
/* mode reset */
|
|
ctrl |= MVL_GEN_CTL_RESET;
|
|
status = ngbe_write_phy_reg_mdi(hw, MVL_GEN_CTL, 0, ctrl);
|
|
|
|
for (i = 0; i < MVL_PHY_RST_WAIT_PERIOD; i++) {
|
|
status = ngbe_read_phy_reg_mdi(hw, MVL_GEN_CTL, 0, &ctrl);
|
|
if (!(ctrl & MVL_GEN_CTL_RESET))
|
|
break;
|
|
msleep(1);
|
|
}
|
|
|
|
if (i == MVL_PHY_RST_WAIT_PERIOD) {
|
|
DEBUGOUT("PHY reset polling failed to complete.\n");
|
|
return NGBE_ERR_RESET_FAILED;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
s32 ngbe_get_phy_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
|
|
{
|
|
u16 value;
|
|
s32 status = 0;
|
|
|
|
if (hw->phy.type == ngbe_phy_mvl) {
|
|
status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
|
|
value &= MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE;
|
|
*pause_bit = (u8)(value >> 10);
|
|
} else {
|
|
status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
|
|
value &= MVL_FANA_PAUSE_MASK;
|
|
*pause_bit = (u8)(value >> 7);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
s32 ngbe_get_phy_lp_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
|
|
{
|
|
u16 value;
|
|
s32 status = 0;
|
|
|
|
if (hw->phy.type == ngbe_phy_mvl) {
|
|
status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
|
|
value &= MVL_CLPAR_ASM_PAUSE | MVL_CLPAR_PAUSE;
|
|
*pause_bit = (u8)(value >> 10);
|
|
} else {
|
|
status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
|
|
value &= MVL_FLPAR_PAUSE_MASK;
|
|
*pause_bit = (u8)(value >> 7);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
s32 ngbe_set_phy_pause_adv_mvl(struct ngbe_hw *hw, u16 pause_bit)
|
|
{
|
|
u16 value;
|
|
s32 status = 0;
|
|
|
|
DEBUGFUNC("ngbe_set_phy_pause_adv_mvl");
|
|
|
|
if (hw->phy.type == ngbe_phy_mvl) {
|
|
status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
|
|
value &= ~(MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE);
|
|
} else {
|
|
status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
|
|
value &= ~MVL_FANA_PAUSE_MASK;
|
|
}
|
|
|
|
value |= pause_bit;
|
|
status = hw->phy.write_reg(hw, MVL_ANA, 0, value);
|
|
|
|
return status;
|
|
}
|
|
|
|
s32 ngbe_check_phy_link_mvl(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;
|
|
|
|
DEBUGFUNC("ngbe_check_phy_link_mvl");
|
|
|
|
/* Initialize speed and link to default case */
|
|
*link_up = false;
|
|
*speed = NGBE_LINK_SPEED_UNKNOWN;
|
|
|
|
hw->phy.read_reg(hw, MVL_INTR, 0, &insr);
|
|
|
|
/*
|
|
* Check current speed and link status of the PHY register.
|
|
* This is a vendor specific register and may have to
|
|
* be changed for other copper PHYs.
|
|
*/
|
|
status = hw->phy.read_reg(hw, MVL_PHYSR, 0, &phy_data);
|
|
phy_link = phy_data & MVL_PHYSR_LINK;
|
|
phy_speed = phy_data & MVL_PHYSR_SPEED_MASK;
|
|
|
|
if (phy_link == MVL_PHYSR_LINK) {
|
|
*link_up = true;
|
|
|
|
if (phy_speed == MVL_PHYSR_SPEED_1000M)
|
|
*speed = NGBE_LINK_SPEED_1GB_FULL;
|
|
else if (phy_speed == MVL_PHYSR_SPEED_100M)
|
|
*speed = NGBE_LINK_SPEED_100M_FULL;
|
|
else if (phy_speed == MVL_PHYSR_SPEED_10M)
|
|
*speed = NGBE_LINK_SPEED_10M_FULL;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|