mirror of https://github.com/F-Stack/f-stack.git
440 lines
10 KiB
C
440 lines
10 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2018-2021 Beijing WangXun Technology Co., Ltd.
|
|
* Copyright(c) 2010-2017 Intel Corporation
|
|
*/
|
|
|
|
#include "ngbe_hw.h"
|
|
#include "ngbe_phy.h"
|
|
|
|
s32 ngbe_mdi_map_register(mdi_reg_t *reg, mdi_reg_22_t *reg22)
|
|
{
|
|
bool match = 1;
|
|
switch (reg->device_type) {
|
|
case NGBE_MD_DEV_PMA_PMD:
|
|
switch (reg->addr) {
|
|
case NGBE_MD_PHY_ID_HIGH:
|
|
case NGBE_MD_PHY_ID_LOW:
|
|
reg22->page = 0;
|
|
reg22->addr = reg->addr;
|
|
reg22->device_type = 0;
|
|
break;
|
|
default:
|
|
match = 0;
|
|
}
|
|
break;
|
|
default:
|
|
match = 0;
|
|
break;
|
|
}
|
|
|
|
if (!match) {
|
|
reg22->page = reg->device_type;
|
|
reg22->device_type = reg->device_type;
|
|
reg22->addr = reg->addr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ngbe_probe_phy - Identify a single address for a PHY
|
|
* @hw: pointer to hardware structure
|
|
* @phy_addr: PHY address to probe
|
|
*
|
|
* Returns true if PHY found
|
|
*/
|
|
static bool ngbe_probe_phy(struct ngbe_hw *hw, u16 phy_addr)
|
|
{
|
|
if (!ngbe_validate_phy_addr(hw, phy_addr)) {
|
|
DEBUGOUT("Unable to validate PHY address 0x%04X",
|
|
phy_addr);
|
|
return false;
|
|
}
|
|
|
|
if (ngbe_get_phy_id(hw))
|
|
return false;
|
|
|
|
hw->phy.type = ngbe_get_phy_type_from_id(hw);
|
|
if (hw->phy.type == ngbe_phy_unknown)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* ngbe_identify_phy - Get physical layer module
|
|
* @hw: pointer to hardware structure
|
|
*
|
|
* Determines the physical layer module found on the current adapter.
|
|
**/
|
|
s32 ngbe_identify_phy(struct ngbe_hw *hw)
|
|
{
|
|
s32 err = NGBE_ERR_PHY_ADDR_INVALID;
|
|
u16 phy_addr;
|
|
|
|
if (hw->phy.type != ngbe_phy_unknown)
|
|
return 0;
|
|
|
|
/* select clause22 */
|
|
wr32(hw, NGBE_MDIOMODE, NGBE_MDIOMODE_MASK);
|
|
|
|
for (phy_addr = 0; phy_addr < NGBE_MAX_PHY_ADDR; phy_addr++) {
|
|
if (ngbe_probe_phy(hw, phy_addr)) {
|
|
err = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ngbe_check_reset_blocked - check status of MNG FW veto bit
|
|
* @hw: pointer to the hardware structure
|
|
*
|
|
* This function checks the STAT.MNGVETO bit to see if there are
|
|
* any constraints on link from manageability. For MAC's that don't
|
|
* have this bit just return faluse since the link can not be blocked
|
|
* via this method.
|
|
**/
|
|
s32 ngbe_check_reset_blocked(struct ngbe_hw *hw)
|
|
{
|
|
u32 mmngc;
|
|
|
|
mmngc = rd32(hw, NGBE_STAT);
|
|
if (mmngc & NGBE_STAT_MNGVETO) {
|
|
DEBUGOUT("MNG_VETO bit detected.");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* ngbe_validate_phy_addr - Determines phy address is valid
|
|
* @hw: pointer to hardware structure
|
|
* @phy_addr: PHY address
|
|
*
|
|
**/
|
|
bool ngbe_validate_phy_addr(struct ngbe_hw *hw, u32 phy_addr)
|
|
{
|
|
u16 phy_id = 0;
|
|
bool valid = false;
|
|
|
|
hw->phy.addr = phy_addr;
|
|
hw->phy.read_reg(hw, NGBE_MD_PHY_ID_LOW,
|
|
NGBE_MD_DEV_PMA_PMD, &phy_id);
|
|
|
|
if (phy_id != 0xFFFF && phy_id != 0x0)
|
|
valid = true;
|
|
|
|
DEBUGOUT("PHY ID LOW is 0x%04X", phy_id);
|
|
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* ngbe_get_phy_id - Get the phy ID
|
|
* @hw: pointer to hardware structure
|
|
*
|
|
**/
|
|
s32 ngbe_get_phy_id(struct ngbe_hw *hw)
|
|
{
|
|
u32 err;
|
|
u16 phy_id_high = 0;
|
|
u16 phy_id_low = 0;
|
|
|
|
err = hw->phy.read_reg(hw, NGBE_MD_PHY_ID_HIGH,
|
|
NGBE_MD_DEV_PMA_PMD,
|
|
&phy_id_high);
|
|
hw->phy.id = (u32)(phy_id_high << 16);
|
|
|
|
err = hw->phy.read_reg(hw, NGBE_MD_PHY_ID_LOW,
|
|
NGBE_MD_DEV_PMA_PMD,
|
|
&phy_id_low);
|
|
hw->phy.id |= (u32)(phy_id_low & NGBE_PHY_REVISION_MASK);
|
|
hw->phy.revision = (u32)(phy_id_low & ~NGBE_PHY_REVISION_MASK);
|
|
|
|
DEBUGOUT("PHY_ID_HIGH 0x%04X, PHY_ID_LOW 0x%04X",
|
|
phy_id_high, phy_id_low);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ngbe_get_phy_type_from_id - Get the phy type
|
|
* @phy_id: PHY ID information
|
|
*
|
|
**/
|
|
enum ngbe_phy_type ngbe_get_phy_type_from_id(struct ngbe_hw *hw)
|
|
{
|
|
enum ngbe_phy_type phy_type;
|
|
|
|
switch (hw->phy.id) {
|
|
case NGBE_PHYID_RTL:
|
|
phy_type = ngbe_phy_rtl;
|
|
break;
|
|
case NGBE_PHYID_MVL:
|
|
if (hw->phy.media_type == ngbe_media_type_fiber)
|
|
phy_type = ngbe_phy_mvl_sfi;
|
|
else
|
|
phy_type = ngbe_phy_mvl;
|
|
break;
|
|
case NGBE_PHYID_YT:
|
|
if (hw->phy.media_type == ngbe_media_type_fiber)
|
|
phy_type = ngbe_phy_yt8521s_sfi;
|
|
else
|
|
phy_type = ngbe_phy_yt8521s;
|
|
break;
|
|
default:
|
|
phy_type = ngbe_phy_unknown;
|
|
break;
|
|
}
|
|
|
|
return phy_type;
|
|
}
|
|
|
|
/**
|
|
* ngbe_reset_phy - Performs a PHY reset
|
|
* @hw: pointer to hardware structure
|
|
**/
|
|
s32 ngbe_reset_phy(struct ngbe_hw *hw)
|
|
{
|
|
s32 err = 0;
|
|
|
|
if (hw->phy.type == ngbe_phy_unknown)
|
|
err = ngbe_identify_phy(hw);
|
|
|
|
if (err != 0 || hw->phy.type == ngbe_phy_none)
|
|
return err;
|
|
|
|
/* Don't reset PHY if it's shut down due to overtemp. */
|
|
if (hw->mac.check_overtemp(hw) == NGBE_ERR_OVERTEMP)
|
|
return err;
|
|
|
|
/* Blocked by MNG FW so bail */
|
|
if (ngbe_check_reset_blocked(hw))
|
|
return err;
|
|
|
|
switch (hw->phy.type) {
|
|
case ngbe_phy_rtl:
|
|
err = ngbe_reset_phy_rtl(hw);
|
|
break;
|
|
case ngbe_phy_mvl:
|
|
case ngbe_phy_mvl_sfi:
|
|
err = ngbe_reset_phy_mvl(hw);
|
|
break;
|
|
case ngbe_phy_yt8521s:
|
|
case ngbe_phy_yt8521s_sfi:
|
|
err = ngbe_reset_phy_yt(hw);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ngbe_read_phy_mdi - Reads a value from a specified PHY register without
|
|
* the SWFW lock
|
|
* @hw: pointer to hardware structure
|
|
* @reg_addr: 32 bit address of PHY register to read
|
|
* @device_type: 5 bit device type
|
|
* @phy_data: Pointer to read data from PHY register
|
|
**/
|
|
s32 ngbe_read_phy_reg_mdi(struct ngbe_hw *hw, u32 reg_addr, u32 device_type,
|
|
u16 *phy_data)
|
|
{
|
|
u32 command, data;
|
|
|
|
/* Setup and write the address cycle command */
|
|
command = NGBE_MDIOSCA_REG(reg_addr) |
|
|
NGBE_MDIOSCA_DEV(device_type) |
|
|
NGBE_MDIOSCA_PORT(hw->phy.addr);
|
|
wr32(hw, NGBE_MDIOSCA, command);
|
|
|
|
command = NGBE_MDIOSCD_CMD_READ |
|
|
NGBE_MDIOSCD_BUSY |
|
|
NGBE_MDIOSCD_CLOCK(6);
|
|
wr32(hw, NGBE_MDIOSCD, command);
|
|
|
|
/*
|
|
* Check every 10 usec to see if the address cycle completed.
|
|
* The MDI Command bit will clear when the operation is
|
|
* complete
|
|
*/
|
|
if (!po32m(hw, NGBE_MDIOSCD, NGBE_MDIOSCD_BUSY,
|
|
0, NULL, 100, 100)) {
|
|
DEBUGOUT("PHY address command did not complete");
|
|
return NGBE_ERR_PHY;
|
|
}
|
|
|
|
data = rd32(hw, NGBE_MDIOSCD);
|
|
*phy_data = (u16)NGBE_MDIOSCD_DAT_R(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ngbe_read_phy_reg - Reads a value from a specified PHY register
|
|
* using the SWFW lock - this function is needed in most cases
|
|
* @hw: pointer to hardware structure
|
|
* @reg_addr: 32 bit address of PHY register to read
|
|
* @device_type: 5 bit device type
|
|
* @phy_data: Pointer to read data from PHY register
|
|
**/
|
|
s32 ngbe_read_phy_reg(struct ngbe_hw *hw, u32 reg_addr,
|
|
u32 device_type, u16 *phy_data)
|
|
{
|
|
s32 err;
|
|
u32 gssr = hw->phy.phy_semaphore_mask;
|
|
|
|
if (hw->mac.acquire_swfw_sync(hw, gssr))
|
|
return NGBE_ERR_SWFW_SYNC;
|
|
|
|
err = hw->phy.read_reg_unlocked(hw, reg_addr, device_type,
|
|
phy_data);
|
|
|
|
hw->mac.release_swfw_sync(hw, gssr);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ngbe_write_phy_reg_mdi - Writes a value to specified PHY register
|
|
* without SWFW lock
|
|
* @hw: pointer to hardware structure
|
|
* @reg_addr: 32 bit PHY register to write
|
|
* @device_type: 5 bit device type
|
|
* @phy_data: Data to write to the PHY register
|
|
**/
|
|
s32 ngbe_write_phy_reg_mdi(struct ngbe_hw *hw, u32 reg_addr,
|
|
u32 device_type, u16 phy_data)
|
|
{
|
|
u32 command;
|
|
|
|
/* write command */
|
|
command = NGBE_MDIOSCA_REG(reg_addr) |
|
|
NGBE_MDIOSCA_DEV(device_type) |
|
|
NGBE_MDIOSCA_PORT(hw->phy.addr);
|
|
wr32(hw, NGBE_MDIOSCA, command);
|
|
|
|
command = NGBE_MDIOSCD_CMD_WRITE |
|
|
NGBE_MDIOSCD_DAT(phy_data) |
|
|
NGBE_MDIOSCD_BUSY |
|
|
NGBE_MDIOSCD_CLOCK(6);
|
|
wr32(hw, NGBE_MDIOSCD, command);
|
|
|
|
/* wait for completion */
|
|
if (!po32m(hw, NGBE_MDIOSCD, NGBE_MDIOSCD_BUSY,
|
|
0, NULL, 100, 100)) {
|
|
DEBUGOUT("PHY write cmd didn't complete");
|
|
return NGBE_ERR_PHY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* ngbe_write_phy_reg - Writes a value to specified PHY register
|
|
* using SWFW lock- this function is needed in most cases
|
|
* @hw: pointer to hardware structure
|
|
* @reg_addr: 32 bit PHY register to write
|
|
* @device_type: 5 bit device type
|
|
* @phy_data: Data to write to the PHY register
|
|
**/
|
|
s32 ngbe_write_phy_reg(struct ngbe_hw *hw, u32 reg_addr,
|
|
u32 device_type, u16 phy_data)
|
|
{
|
|
s32 err;
|
|
u32 gssr = hw->phy.phy_semaphore_mask;
|
|
|
|
if (hw->mac.acquire_swfw_sync(hw, gssr))
|
|
err = NGBE_ERR_SWFW_SYNC;
|
|
|
|
err = hw->phy.write_reg_unlocked(hw, reg_addr, device_type,
|
|
phy_data);
|
|
|
|
hw->mac.release_swfw_sync(hw, gssr);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* ngbe_init_phy - PHY specific init
|
|
* @hw: pointer to hardware structure
|
|
*
|
|
* Initialize any function pointers that were not able to be
|
|
* set during init_shared_code because the PHY type was
|
|
* not known.
|
|
*
|
|
**/
|
|
s32 ngbe_init_phy(struct ngbe_hw *hw)
|
|
{
|
|
struct ngbe_phy_info *phy = &hw->phy;
|
|
s32 err = 0;
|
|
|
|
hw->phy.addr = 0;
|
|
|
|
switch (hw->sub_device_id) {
|
|
case NGBE_SUB_DEV_ID_EM_RTL_SGMII:
|
|
hw->phy.read_reg_unlocked = ngbe_read_phy_reg_rtl;
|
|
hw->phy.write_reg_unlocked = ngbe_write_phy_reg_rtl;
|
|
break;
|
|
case NGBE_SUB_DEV_ID_EM_MVL_RGMII:
|
|
case NGBE_SUB_DEV_ID_EM_MVL_SFP:
|
|
hw->phy.read_reg_unlocked = ngbe_read_phy_reg_mvl;
|
|
hw->phy.write_reg_unlocked = ngbe_write_phy_reg_mvl;
|
|
break;
|
|
case NGBE_SUB_DEV_ID_EM_YT8521S_SFP:
|
|
hw->phy.read_reg_unlocked = ngbe_read_phy_reg_yt;
|
|
hw->phy.write_reg_unlocked = ngbe_write_phy_reg_yt;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
hw->phy.phy_semaphore_mask = NGBE_MNGSEM_SWPHY;
|
|
|
|
/* Identify the PHY */
|
|
err = phy->identify(hw);
|
|
if (err == NGBE_ERR_PHY_ADDR_INVALID)
|
|
goto init_phy_ops_out;
|
|
|
|
/* Set necessary function pointers based on PHY type */
|
|
switch (hw->phy.type) {
|
|
case ngbe_phy_rtl:
|
|
hw->phy.init_hw = ngbe_init_phy_rtl;
|
|
hw->phy.check_link = ngbe_check_phy_link_rtl;
|
|
hw->phy.setup_link = ngbe_setup_phy_link_rtl;
|
|
hw->phy.get_adv_pause = ngbe_get_phy_advertised_pause_rtl;
|
|
hw->phy.get_lp_adv_pause = ngbe_get_phy_lp_advertised_pause_rtl;
|
|
hw->phy.set_pause_adv = ngbe_set_phy_pause_adv_rtl;
|
|
break;
|
|
case ngbe_phy_mvl:
|
|
case ngbe_phy_mvl_sfi:
|
|
hw->phy.init_hw = ngbe_init_phy_mvl;
|
|
hw->phy.check_link = ngbe_check_phy_link_mvl;
|
|
hw->phy.setup_link = ngbe_setup_phy_link_mvl;
|
|
hw->phy.get_adv_pause = ngbe_get_phy_advertised_pause_mvl;
|
|
hw->phy.get_lp_adv_pause = ngbe_get_phy_lp_advertised_pause_mvl;
|
|
hw->phy.set_pause_adv = ngbe_set_phy_pause_adv_mvl;
|
|
break;
|
|
case ngbe_phy_yt8521s:
|
|
case ngbe_phy_yt8521s_sfi:
|
|
hw->phy.init_hw = ngbe_init_phy_yt;
|
|
hw->phy.check_link = ngbe_check_phy_link_yt;
|
|
hw->phy.setup_link = ngbe_setup_phy_link_yt;
|
|
hw->phy.get_adv_pause = ngbe_get_phy_advertised_pause_yt;
|
|
hw->phy.get_lp_adv_pause = ngbe_get_phy_lp_advertised_pause_yt;
|
|
hw->phy.set_pause_adv = ngbe_set_phy_pause_adv_yt;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
init_phy_ops_out:
|
|
return err;
|
|
}
|
|
|