#include "ixgbe_api.h"
#include "ixgbe_common.h"
#include "ixgbe_phy.h"
static void ixgbe_i2c_start(struct ixgbe_hw *hw);
static void ixgbe_i2c_stop(struct ixgbe_hw *hw);
static void ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data);
static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data);
static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw);
static void ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data);
static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data);
static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl);
static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl);
static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data);
static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl);
static s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 *sff8472_data);
static s32 ixgbe_out_i2c_byte_ack(struct ixgbe_hw *hw, u8 byte)
{
s32 status;
status = ixgbe_clock_out_i2c_byte(hw, byte);
if (status)
return status;
return ixgbe_get_i2c_ack(hw);
}
static s32 ixgbe_in_i2c_byte_ack(struct ixgbe_hw *hw, u8 *byte)
{
ixgbe_clock_in_i2c_byte(hw, byte);
return ixgbe_clock_out_i2c_bit(hw, false);
}
static u8 ixgbe_ones_comp_byte_add(u8 add1, u8 add2)
{
u16 sum = add1 + add2;
sum = (sum & 0xFF) + (sum >> 8);
return sum & 0xFF;
}
s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, u16 reg,
u16 *val, bool lock)
{
u32 swfw_mask = hw->phy.phy_semaphore_mask;
int max_retry = 3;
int retry = 0;
u8 csum_byte;
u8 high_bits;
u8 low_bits;
u8 reg_high;
u8 csum;
reg_high = ((reg >> 7) & 0xFE) | 1;
csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF);
csum = ~csum;
do {
if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
return IXGBE_ERR_SWFW_SYNC;
ixgbe_i2c_start(hw);
if (ixgbe_out_i2c_byte_ack(hw, addr))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, reg_high))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, reg & 0xFF))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, csum))
goto fail;
ixgbe_i2c_start(hw);
if (ixgbe_out_i2c_byte_ack(hw, addr | 1))
goto fail;
if (ixgbe_in_i2c_byte_ack(hw, &high_bits))
goto fail;
if (ixgbe_in_i2c_byte_ack(hw, &low_bits))
goto fail;
ixgbe_clock_in_i2c_byte(hw, &csum_byte);
if (ixgbe_clock_out_i2c_bit(hw, false))
goto fail;
ixgbe_i2c_stop(hw);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
*val = (high_bits << 8) | low_bits;
return 0;
fail:
ixgbe_i2c_bus_clear(hw);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
if (retry < max_retry)
DEBUGOUT("I2C byte read combined error - Retrying.\n");
else
DEBUGOUT("I2C byte read combined error.\n");
retry++;
} while (retry <= max_retry);
return IXGBE_ERR_I2C;
}
s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, u16 reg,
u16 val, bool lock)
{
u32 swfw_mask = hw->phy.phy_semaphore_mask;
int max_retry = 1;
int retry = 0;
u8 reg_high;
u8 csum;
reg_high = (reg >> 7) & 0xFE;
csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF);
csum = ixgbe_ones_comp_byte_add(csum, val >> 8);
csum = ixgbe_ones_comp_byte_add(csum, val & 0xFF);
csum = ~csum;
do {
if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
return IXGBE_ERR_SWFW_SYNC;
ixgbe_i2c_start(hw);
if (ixgbe_out_i2c_byte_ack(hw, addr))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, reg_high))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, reg & 0xFF))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, val >> 8))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, val & 0xFF))
goto fail;
if (ixgbe_out_i2c_byte_ack(hw, csum))
goto fail;
ixgbe_i2c_stop(hw);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
return 0;
fail:
ixgbe_i2c_bus_clear(hw);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
if (retry < max_retry)
DEBUGOUT("I2C byte write combined error - Retrying.\n");
else
DEBUGOUT("I2C byte write combined error.\n");
retry++;
} while (retry <= max_retry);
return IXGBE_ERR_I2C;
}
s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw)
{
struct ixgbe_phy_info *phy = &hw->phy;
DEBUGFUNC("ixgbe_init_phy_ops_generic");
phy->ops.identify = ixgbe_identify_phy_generic;
phy->ops.reset = ixgbe_reset_phy_generic;
phy->ops.read_reg = ixgbe_read_phy_reg_generic;
phy->ops.write_reg = ixgbe_write_phy_reg_generic;
phy->ops.read_reg_mdi = ixgbe_read_phy_reg_mdi;
phy->ops.write_reg_mdi = ixgbe_write_phy_reg_mdi;
phy->ops.setup_link = ixgbe_setup_phy_link_generic;
phy->ops.setup_link_speed = ixgbe_setup_phy_link_speed_generic;
phy->ops.check_link = NULL;
phy->ops.get_firmware_version = ixgbe_get_phy_firmware_version_generic;
phy->ops.read_i2c_byte = ixgbe_read_i2c_byte_generic;
phy->ops.write_i2c_byte = ixgbe_write_i2c_byte_generic;
phy->ops.read_i2c_sff8472 = ixgbe_read_i2c_sff8472_generic;
phy->ops.read_i2c_eeprom = ixgbe_read_i2c_eeprom_generic;
phy->ops.write_i2c_eeprom = ixgbe_write_i2c_eeprom_generic;
phy->ops.i2c_bus_clear = ixgbe_i2c_bus_clear;
phy->ops.identify_sfp = ixgbe_identify_module_generic;
phy->sfp_type = ixgbe_sfp_type_unknown;
phy->ops.read_i2c_byte_unlocked = ixgbe_read_i2c_byte_generic_unlocked;
phy->ops.write_i2c_byte_unlocked =
ixgbe_write_i2c_byte_generic_unlocked;
phy->ops.check_overtemp = ixgbe_tn_check_overtemp;
return IXGBE_SUCCESS;
}
static bool ixgbe_probe_phy(struct ixgbe_hw *hw, u16 phy_addr)
{
u16 ext_ability = 0;
if (!ixgbe_validate_phy_addr(hw, phy_addr)) {
DEBUGOUT1("Unable to validate PHY address 0x%04X\n",
phy_addr);
return false;
}
if (ixgbe_get_phy_id(hw))
return false;
hw->phy.type = ixgbe_get_phy_type_from_id(hw->phy.id);
if (hw->phy.type == ixgbe_phy_unknown) {
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_EXT_ABILITY,
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &ext_ability);
if (ext_ability &
(IXGBE_MDIO_PHY_10GBASET_ABILITY |
IXGBE_MDIO_PHY_1000BASET_ABILITY))
hw->phy.type = ixgbe_phy_cu_unknown;
else
hw->phy.type = ixgbe_phy_generic;
}
return true;
}
s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
{
s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
u16 phy_addr;
DEBUGFUNC("ixgbe_identify_phy_generic");
if (!hw->phy.phy_semaphore_mask) {
if (hw->bus.lan_id)
hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY1_SM;
else
hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM;
}
if (hw->phy.type != ixgbe_phy_unknown)
return IXGBE_SUCCESS;
if (hw->phy.nw_mng_if_sel) {
phy_addr = (hw->phy.nw_mng_if_sel &
IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD) >>
IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT;
if (ixgbe_probe_phy(hw, phy_addr))
return IXGBE_SUCCESS;
else
return IXGBE_ERR_PHY_ADDR_INVALID;
}
for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) {
if (ixgbe_probe_phy(hw, phy_addr)) {
status = IXGBE_SUCCESS;
break;
}
}
if (status != IXGBE_SUCCESS)
hw->phy.addr = 0;
return status;
}
s32 ixgbe_check_reset_blocked(struct ixgbe_hw *hw)
{
u32 mmngc;
DEBUGFUNC("ixgbe_check_reset_blocked");
if (hw->mac.type == ixgbe_mac_82598EB)
return false;
mmngc = IXGBE_READ_REG(hw, IXGBE_MMNGC);
if (mmngc & IXGBE_MMNGC_MNG_VETO) {
ERROR_REPORT1(IXGBE_ERROR_SOFTWARE,
"MNG_VETO bit detected.\n");
return true;
}
return false;
}
bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr)
{
u16 phy_id = 0;
bool valid = false;
DEBUGFUNC("ixgbe_validate_phy_addr");
hw->phy.addr = phy_addr;
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_HIGH,
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &phy_id);
if (phy_id != 0xFFFF && phy_id != 0x0)
valid = true;
DEBUGOUT1("PHY ID HIGH is 0x%04X\n", phy_id);
return valid;
}
s32 ixgbe_get_phy_id(struct ixgbe_hw *hw)
{
u32 status;
u16 phy_id_high = 0;
u16 phy_id_low = 0;
DEBUGFUNC("ixgbe_get_phy_id");
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_HIGH,
IXGBE_MDIO_PMA_PMD_DEV_TYPE,
&phy_id_high);
if (status == IXGBE_SUCCESS) {
hw->phy.id = (u32)(phy_id_high << 16);
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_ID_LOW,
IXGBE_MDIO_PMA_PMD_DEV_TYPE,
&phy_id_low);
hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK);
hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK);
}
DEBUGOUT2("PHY_ID_HIGH 0x%04X, PHY_ID_LOW 0x%04X\n",
phy_id_high, phy_id_low);
return status;
}
enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id)
{
enum ixgbe_phy_type phy_type;
DEBUGFUNC("ixgbe_get_phy_type_from_id");
switch (phy_id) {
case TN1010_PHY_ID:
phy_type = ixgbe_phy_tn;
break;
case X550_PHY_ID:
case X540_PHY_ID:
phy_type = ixgbe_phy_aq;
break;
case QT2022_PHY_ID:
phy_type = ixgbe_phy_qt;
break;
case ATH_PHY_ID:
phy_type = ixgbe_phy_nl;
break;
case X557_PHY_ID:
case X557_PHY_ID2:
phy_type = ixgbe_phy_x550em_ext_t;
break;
case IXGBE_M88E1500_E_PHY_ID:
case IXGBE_M88E1543_E_PHY_ID:
phy_type = ixgbe_phy_ext_1g_t;
break;
default:
phy_type = ixgbe_phy_unknown;
break;
}
return phy_type;
}
s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw)
{
u32 i;
u16 ctrl = 0;
s32 status = IXGBE_SUCCESS;
DEBUGFUNC("ixgbe_reset_phy_generic");
if (hw->phy.type == ixgbe_phy_unknown)
status = ixgbe_identify_phy_generic(hw);
if (status != IXGBE_SUCCESS || hw->phy.type == ixgbe_phy_none)
goto out;
if (!hw->phy.reset_if_overtemp &&
(IXGBE_ERR_OVERTEMP == hw->phy.ops.check_overtemp(hw)))
goto out;
if (ixgbe_check_reset_blocked(hw))
goto out;
hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
IXGBE_MDIO_PHY_XS_DEV_TYPE,
IXGBE_MDIO_PHY_XS_RESET);
for (i = 0; i < 30; i++) {
msec_delay(100);
if (hw->phy.type == ixgbe_phy_x550em_ext_t) {
status = hw->phy.ops.read_reg(hw,
IXGBE_MDIO_TX_VENDOR_ALARMS_3,
IXGBE_MDIO_PMA_PMD_DEV_TYPE,
&ctrl);
if (status != IXGBE_SUCCESS)
return status;
if (ctrl & IXGBE_MDIO_TX_VENDOR_ALARMS_3_RST_MASK) {
usec_delay(2);
break;
}
} else {
status = hw->phy.ops.read_reg(hw,
IXGBE_MDIO_PHY_XS_CONTROL,
IXGBE_MDIO_PHY_XS_DEV_TYPE,
&ctrl);
if (status != IXGBE_SUCCESS)
return status;
if (!(ctrl & IXGBE_MDIO_PHY_XS_RESET)) {
usec_delay(2);
break;
}
}
}
if (ctrl & IXGBE_MDIO_PHY_XS_RESET) {
status = IXGBE_ERR_RESET_FAILED;
ERROR_REPORT1(IXGBE_ERROR_POLLING,
"PHY reset polling failed to complete.\n");
}
out:
return status;
}
void ixgbe_restart_auto_neg(struct ixgbe_hw *hw)
{
u16 autoneg_reg;
if (ixgbe_check_reset_blocked(hw))
return;
hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg);
autoneg_reg |= IXGBE_MII_RESTART;
hw->phy.ops.write_reg(hw, IXGBE_MDIO_AUTO_NEG_CONTROL,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_reg);
}
s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
u16 *phy_data)
{
u32 i, data, command;
command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
(device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
(hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
(IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND));
IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
usec_delay(10);
command = IXGBE_READ_REG(hw, IXGBE_MSCA);
if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
break;
}
if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
ERROR_REPORT1(IXGBE_ERROR_POLLING, "PHY address command did not complete.\n");
DEBUGOUT("PHY address command did not complete, returning IXGBE_ERR_PHY\n");
return IXGBE_ERR_PHY;
}
command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
(device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
(hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
(IXGBE_MSCA_READ | IXGBE_MSCA_MDI_COMMAND));
IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
usec_delay(10);
command = IXGBE_READ_REG(hw, IXGBE_MSCA);
if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
break;
}
if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
ERROR_REPORT1(IXGBE_ERROR_POLLING, "PHY read command didn't complete\n");
DEBUGOUT("PHY read command didn't complete, returning IXGBE_ERR_PHY\n");
return IXGBE_ERR_PHY;
}
data = IXGBE_READ_REG(hw, IXGBE_MSRWD);
data >>= IXGBE_MSRWD_READ_DATA_SHIFT;
*phy_data = (u16)(data);
return IXGBE_SUCCESS;
}
s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
u32 device_type, u16 *phy_data)
{
s32 status;
u32 gssr = hw->phy.phy_semaphore_mask;
DEBUGFUNC("ixgbe_read_phy_reg_generic");
if (hw->mac.ops.acquire_swfw_sync(hw, gssr))
return IXGBE_ERR_SWFW_SYNC;
status = hw->phy.ops.read_reg_mdi(hw, reg_addr, device_type, phy_data);
hw->mac.ops.release_swfw_sync(hw, gssr);
return status;
}
s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
u32 device_type, u16 phy_data)
{
u32 i, command;
IXGBE_WRITE_REG(hw, IXGBE_MSRWD, (u32)phy_data);
command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
(device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
(hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
(IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND));
IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
usec_delay(10);
command = IXGBE_READ_REG(hw, IXGBE_MSCA);
if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
break;
}
if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
ERROR_REPORT1(IXGBE_ERROR_POLLING, "PHY address cmd didn't complete\n");
return IXGBE_ERR_PHY;
}
command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
(device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
(hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
(IXGBE_MSCA_WRITE | IXGBE_MSCA_MDI_COMMAND));
IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
for (i = 0; i < IXGBE_MDIO_COMMAND_TIMEOUT; i++) {
usec_delay(10);
command = IXGBE_READ_REG(hw, IXGBE_MSCA);
if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
break;
}
if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
ERROR_REPORT1(IXGBE_ERROR_POLLING, "PHY write cmd didn't complete\n");
return IXGBE_ERR_PHY;
}
return IXGBE_SUCCESS;
}
s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
u32 device_type, u16 phy_data)
{
s32 status;
u32 gssr = hw->phy.phy_semaphore_mask;
DEBUGFUNC("ixgbe_write_phy_reg_generic");
if (hw->mac.ops.acquire_swfw_sync(hw, gssr) == IXGBE_SUCCESS) {
status = hw->phy.ops.write_reg_mdi(hw, reg_addr, device_type,
phy_data);
hw->mac.ops.release_swfw_sync(hw, gssr);
} else {
status = IXGBE_ERR_SWFW_SYNC;
}
return status;
}
s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw)
{
s32 status = IXGBE_SUCCESS;
u16 autoneg_reg = IXGBE_MII_AUTONEG_REG;
bool autoneg = false;
ixgbe_link_speed speed;
DEBUGFUNC("ixgbe_setup_phy_link_generic");
ixgbe_get_copper_link_capabilities_generic(hw, &speed, &autoneg);
hw->phy.ops.read_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_reg);
autoneg_reg &= ~IXGBE_MII_10GBASE_T_ADVERTISE;
if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) &&
(speed & IXGBE_LINK_SPEED_10GB_FULL))
autoneg_reg |= IXGBE_MII_10GBASE_T_ADVERTISE;
hw->phy.ops.write_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
autoneg_reg);
hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_reg);
if (hw->mac.type == ixgbe_mac_X550) {
autoneg_reg &= ~IXGBE_MII_5GBASE_T_ADVERTISE;
if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_5GB_FULL) &&
(speed & IXGBE_LINK_SPEED_5GB_FULL))
autoneg_reg |= IXGBE_MII_5GBASE_T_ADVERTISE;
autoneg_reg &= ~IXGBE_MII_2_5GBASE_T_ADVERTISE;
if ((hw->phy.autoneg_advertised &
IXGBE_LINK_SPEED_2_5GB_FULL) &&
(speed & IXGBE_LINK_SPEED_2_5GB_FULL))
autoneg_reg |= IXGBE_MII_2_5GBASE_T_ADVERTISE;
}
autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE;
if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) &&
(speed & IXGBE_LINK_SPEED_1GB_FULL))
autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE;
hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_VENDOR_PROVISION_1_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
autoneg_reg);
hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_reg);
autoneg_reg &= ~(IXGBE_MII_100BASE_T_ADVERTISE |
IXGBE_MII_100BASE_T_ADVERTISE_HALF);
if ((hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) &&
(speed & IXGBE_LINK_SPEED_100_FULL))
autoneg_reg |= IXGBE_MII_100BASE_T_ADVERTISE;
hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
autoneg_reg);
ixgbe_restart_auto_neg(hw);
return status;
}
s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
ixgbe_link_speed speed,
bool autoneg_wait_to_complete)
{
UNREFERENCED_1PARAMETER(autoneg_wait_to_complete);
DEBUGFUNC("ixgbe_setup_phy_link_speed_generic");
hw->phy.autoneg_advertised = 0;
if (speed & IXGBE_LINK_SPEED_10GB_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
if (speed & IXGBE_LINK_SPEED_5GB_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_5GB_FULL;
if (speed & IXGBE_LINK_SPEED_2_5GB_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_2_5GB_FULL;
if (speed & IXGBE_LINK_SPEED_1GB_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
if (speed & IXGBE_LINK_SPEED_100_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL;
if (speed & IXGBE_LINK_SPEED_10_FULL)
hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10_FULL;
ixgbe_setup_phy_link(hw);
return IXGBE_SUCCESS;
}
static s32 ixgbe_get_copper_speeds_supported(struct ixgbe_hw *hw)
{
s32 status;
u16 speed_ability;
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_SPEED_ABILITY,
IXGBE_MDIO_PMA_PMD_DEV_TYPE,
&speed_ability);
if (status)
return status;
if (speed_ability & IXGBE_MDIO_PHY_SPEED_10G)
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_10GB_FULL;
if (speed_ability & IXGBE_MDIO_PHY_SPEED_1G)
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_1GB_FULL;
if (speed_ability & IXGBE_MDIO_PHY_SPEED_100M)
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_100_FULL;
switch (hw->mac.type) {
case ixgbe_mac_X550:
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_2_5GB_FULL;
hw->phy.speeds_supported |= IXGBE_LINK_SPEED_5GB_FULL;
break;
case ixgbe_mac_X550EM_x:
case ixgbe_mac_X550EM_a:
hw->phy.speeds_supported &= ~IXGBE_LINK_SPEED_100_FULL;
break;
default:
break;
}
return status;
}
s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
ixgbe_link_speed *speed,
bool *autoneg)
{
s32 status = IXGBE_SUCCESS;
DEBUGFUNC("ixgbe_get_copper_link_capabilities_generic");
*autoneg = true;
if (!hw->phy.speeds_supported)
status = ixgbe_get_copper_speeds_supported(hw);
*speed = hw->phy.speeds_supported;
return status;
}
s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
bool *link_up)
{
s32 status = IXGBE_SUCCESS;
u32 time_out;
u32 max_time_out = 10;
u16 phy_link = 0;
u16 phy_speed = 0;
u16 phy_data = 0;
DEBUGFUNC("ixgbe_check_phy_link_tnx");
*link_up = false;
*speed = IXGBE_LINK_SPEED_10GB_FULL;
for (time_out = 0; time_out < max_time_out; time_out++) {
usec_delay(10);
status = hw->phy.ops.read_reg(hw,
IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&phy_data);
phy_link = phy_data & IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS;
phy_speed = phy_data &
IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS;
if (phy_link == IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS) {
*link_up = true;
if (phy_speed ==
IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS)
*speed = IXGBE_LINK_SPEED_1GB_FULL;
break;
}
}
return status;
}
s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
{
s32 status = IXGBE_SUCCESS;
u16 autoneg_reg = IXGBE_MII_AUTONEG_REG;
bool autoneg = false;
ixgbe_link_speed speed;
DEBUGFUNC("ixgbe_setup_phy_link_tnx");
ixgbe_get_copper_link_capabilities_generic(hw, &speed, &autoneg);
if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
hw->phy.ops.read_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_reg);
autoneg_reg &= ~IXGBE_MII_10GBASE_T_ADVERTISE;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
autoneg_reg |= IXGBE_MII_10GBASE_T_ADVERTISE;
hw->phy.ops.write_reg(hw, IXGBE_MII_10GBASE_T_AUTONEG_CTRL_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
autoneg_reg);
}
if (speed & IXGBE_LINK_SPEED_1GB_FULL) {
hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_XNP_TX_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_reg);
autoneg_reg &= ~IXGBE_MII_1GBASE_T_ADVERTISE_XNP_TX;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL)
autoneg_reg |= IXGBE_MII_1GBASE_T_ADVERTISE_XNP_TX;
hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_XNP_TX_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
autoneg_reg);
}
if (speed & IXGBE_LINK_SPEED_100_FULL) {
hw->phy.ops.read_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
&autoneg_reg);
autoneg_reg &= ~IXGBE_MII_100BASE_T_ADVERTISE;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
autoneg_reg |= IXGBE_MII_100BASE_T_ADVERTISE;
hw->phy.ops.write_reg(hw, IXGBE_MII_AUTONEG_ADVERTISE_REG,
IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
autoneg_reg);
}
ixgbe_restart_auto_neg(hw);
return status;
}
s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
u16 *firmware_version)
{
s32 status;
DEBUGFUNC("ixgbe_get_phy_firmware_version_tnx");
status = hw->phy.ops.read_reg(hw, TNX_FW_REV,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
firmware_version);
return status;
}
s32 ixgbe_get_phy_firmware_version_generic(struct ixgbe_hw *hw,
u16 *firmware_version)
{
s32 status;
DEBUGFUNC("ixgbe_get_phy_firmware_version_generic");
status = hw->phy.ops.read_reg(hw, AQ_FW_REV,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
firmware_version);
return status;
}
s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
{
u16 phy_offset, control, eword, edata, block_crc;
bool end_data = false;
u16 list_offset, data_offset;
u16 phy_data = 0;
s32 ret_val = IXGBE_SUCCESS;
u32 i;
DEBUGFUNC("ixgbe_reset_phy_nl");
if (ixgbe_check_reset_blocked(hw))
goto out;
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data);
hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
IXGBE_MDIO_PHY_XS_DEV_TYPE,
(phy_data | IXGBE_MDIO_PHY_XS_RESET));
for (i = 0; i < 100; i++) {
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data);
if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) == 0)
break;
msec_delay(10);
}
if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) != 0) {
DEBUGOUT("PHY reset did not complete.\n");
ret_val = IXGBE_ERR_PHY;
goto out;
}
ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
&data_offset);
if (ret_val != IXGBE_SUCCESS)
goto out;
ret_val = hw->eeprom.ops.read(hw, data_offset, &block_crc);
data_offset++;
while (!end_data) {
ret_val = hw->eeprom.ops.read(hw, data_offset, &eword);
if (ret_val)
goto err_eeprom;
control = (eword & IXGBE_CONTROL_MASK_NL) >>
IXGBE_CONTROL_SHIFT_NL;
edata = eword & IXGBE_DATA_MASK_NL;
switch (control) {
case IXGBE_DELAY_NL:
data_offset++;
DEBUGOUT1("DELAY: %d MS\n", edata);
msec_delay(edata);
break;
case IXGBE_DATA_NL:
DEBUGOUT("DATA:\n");
data_offset++;
ret_val = hw->eeprom.ops.read(hw, data_offset,
&phy_offset);
if (ret_val)
goto err_eeprom;
data_offset++;
for (i = 0; i < edata; i++) {
ret_val = hw->eeprom.ops.read(hw, data_offset,
&eword);
if (ret_val)
goto err_eeprom;
hw->phy.ops.write_reg(hw, phy_offset,
IXGBE_TWINAX_DEV, eword);
DEBUGOUT2("Wrote %4.4x to %4.4x\n", eword,
phy_offset);
data_offset++;
phy_offset++;
}
break;
case IXGBE_CONTROL_NL:
data_offset++;
DEBUGOUT("CONTROL:\n");
if (edata == IXGBE_CONTROL_EOL_NL) {
DEBUGOUT("EOL\n");
end_data = true;
} else if (edata == IXGBE_CONTROL_SOL_NL) {
DEBUGOUT("SOL\n");
} else {
DEBUGOUT("Bad control value\n");
ret_val = IXGBE_ERR_PHY;
goto out;
}
break;
default:
DEBUGOUT("Bad control type\n");
ret_val = IXGBE_ERR_PHY;
goto out;
}
}
out:
return ret_val;
err_eeprom:
ERROR_REPORT2(IXGBE_ERROR_INVALID_STATE,
"eeprom read at offset %d failed", data_offset);
return IXGBE_ERR_PHY;
}
s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw)
{
s32 status = IXGBE_ERR_SFP_NOT_PRESENT;
DEBUGFUNC("ixgbe_identify_module_generic");
switch (hw->mac.ops.get_media_type(hw)) {
case ixgbe_media_type_fiber:
status = ixgbe_identify_sfp_module_generic(hw);
break;
case ixgbe_media_type_fiber_qsfp:
status = ixgbe_identify_qsfp_module_generic(hw);
break;
default:
hw->phy.sfp_type = ixgbe_sfp_type_not_present;
status = IXGBE_ERR_SFP_NOT_PRESENT;
break;
}
return status;
}
s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
{
s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
u32 vendor_oui = 0;
enum ixgbe_sfp_type stored_sfp_type = hw->phy.sfp_type;
u8 identifier = 0;
u8 comp_codes_1g = 0;
u8 comp_codes_10g = 0;
u8 oui_bytes[3] = {0, 0, 0};
u8 cable_tech = 0;
u8 cable_spec = 0;
u16 enforce_sfp = 0;
static bool warned_once = false;
DEBUGFUNC("ixgbe_identify_sfp_module_generic");
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_fiber) {
hw->phy.sfp_type = ixgbe_sfp_type_not_present;
status = IXGBE_ERR_SFP_NOT_PRESENT;
goto out;
}
hw->mac.ops.set_lan_id(hw);
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_IDENTIFIER,
&identifier);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
if (identifier != IXGBE_SFF_IDENTIFIER_SFP) {
hw->phy.type = ixgbe_phy_sfp_unsupported;
status = IXGBE_ERR_SFP_NOT_SUPPORTED;
} else {
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_1GBE_COMP_CODES,
&comp_codes_1g);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_10GBE_COMP_CODES,
&comp_codes_10g);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_CABLE_TECHNOLOGY,
&cable_tech);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
if (hw->mac.type == ixgbe_mac_82598EB) {
if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE)
hw->phy.sfp_type = ixgbe_sfp_type_da_cu;
else if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE)
hw->phy.sfp_type = ixgbe_sfp_type_sr;
else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE)
hw->phy.sfp_type = ixgbe_sfp_type_lr;
else
hw->phy.sfp_type = ixgbe_sfp_type_unknown;
} else {
if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_da_cu_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_da_cu_core1;
} else if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE) {
hw->phy.ops.read_i2c_eeprom(
hw, IXGBE_SFF_CABLE_SPEC_COMP,
&cable_spec);
if (cable_spec &
IXGBE_SFF_DA_SPEC_ACTIVE_LIMITING) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_da_act_lmt_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_da_act_lmt_core1;
} else {
hw->phy.sfp_type =
ixgbe_sfp_type_unknown;
}
} else if (comp_codes_10g &
(IXGBE_SFF_10GBASESR_CAPABLE |
IXGBE_SFF_10GBASELR_CAPABLE)) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_srlr_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_srlr_core1;
} else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_1g_cu_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_1g_cu_core1;
} else if (comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_1g_sx_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_1g_sx_core1;
} else if (comp_codes_1g & IXGBE_SFF_1GBASELX_CAPABLE) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_1g_lx_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_1g_lx_core1;
} else if (comp_codes_1g & IXGBE_SFF_BASEBX10_CAPABLE) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_1g_bx_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_1g_bx_core1;
} else {
hw->phy.sfp_type = ixgbe_sfp_type_unknown;
}
}
if (hw->phy.sfp_type != stored_sfp_type)
hw->phy.sfp_setup_needed = true;
hw->phy.multispeed_fiber = false;
if (((comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE) &&
(comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE)) ||
((comp_codes_1g & IXGBE_SFF_1GBASELX_CAPABLE) &&
(comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE)) ||
(cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) ||
(cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE))
hw->phy.multispeed_fiber = true;
if (hw->phy.type != ixgbe_phy_nl) {
hw->phy.id = identifier;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_VENDOR_OUI_BYTE0,
&oui_bytes[0]);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_VENDOR_OUI_BYTE1,
&oui_bytes[1]);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_VENDOR_OUI_BYTE2,
&oui_bytes[2]);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
vendor_oui =
((oui_bytes[0] << IXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) |
(oui_bytes[1] << IXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) |
(oui_bytes[2] << IXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT));
switch (vendor_oui) {
case IXGBE_SFF_VENDOR_OUI_TYCO:
if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE)
hw->phy.type =
ixgbe_phy_sfp_passive_tyco;
break;
case IXGBE_SFF_VENDOR_OUI_FTL:
if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE)
hw->phy.type = ixgbe_phy_sfp_ftl_active;
else
hw->phy.type = ixgbe_phy_sfp_ftl;
break;
case IXGBE_SFF_VENDOR_OUI_AVAGO:
hw->phy.type = ixgbe_phy_sfp_avago;
break;
case IXGBE_SFF_VENDOR_OUI_INTEL:
hw->phy.type = ixgbe_phy_sfp_intel;
break;
default:
if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE)
hw->phy.type = ixgbe_phy_sfp_passive_unknown;
else if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE)
hw->phy.type = ixgbe_phy_sfp_active_unknown;
else
hw->phy.type = ixgbe_phy_sfp_unknown;
break;
}
}
if (cable_tech & (IXGBE_SFF_DA_PASSIVE_CABLE |
IXGBE_SFF_DA_ACTIVE_CABLE)) {
status = IXGBE_SUCCESS;
goto out;
}
if (comp_codes_10g == 0 &&
!(hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_lx_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_lx_core1 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core1)) {
hw->phy.type = ixgbe_phy_sfp_unsupported;
status = IXGBE_ERR_SFP_NOT_SUPPORTED;
goto out;
}
if (hw->mac.type == ixgbe_mac_82598EB) {
status = IXGBE_SUCCESS;
goto out;
}
ixgbe_get_device_caps(hw, &enforce_sfp);
if (!(enforce_sfp & IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP) &&
!(hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_lx_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_lx_core1 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core0 ||
hw->phy.sfp_type == ixgbe_sfp_type_1g_bx_core1)) {
if (hw->phy.type == ixgbe_phy_sfp_intel) {
status = IXGBE_SUCCESS;
} else {
if (hw->allow_unsupported_sfp == true) {
if (!warned_once)
EWARN(hw,
"WARNING: Intel (R) Network Connections are quality tested using Intel (R) Ethernet Optics. "
"Using untested modules is not supported and may cause unstable operation or damage to the module or the adapter. "
"Intel Corporation is not responsible for any harm caused by using untested modules.\n");
warned_once = true;
status = IXGBE_SUCCESS;
} else {
DEBUGOUT
("SFP+ module not supported\n");
hw->phy.type =
ixgbe_phy_sfp_unsupported;
status = IXGBE_ERR_SFP_NOT_SUPPORTED;
}
}
} else {
status = IXGBE_SUCCESS;
}
}
out:
return status;
err_read_i2c_eeprom:
hw->phy.sfp_type = ixgbe_sfp_type_not_present;
if (hw->phy.type != ixgbe_phy_nl) {
hw->phy.id = 0;
hw->phy.type = ixgbe_phy_unknown;
}
return IXGBE_ERR_SFP_NOT_PRESENT;
}
u64 ixgbe_get_supported_phy_sfp_layer_generic(struct ixgbe_hw *hw)
{
u64 physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
u8 comp_codes_10g = 0;
u8 comp_codes_1g = 0;
DEBUGFUNC("ixgbe_get_supported_phy_sfp_layer_generic");
hw->phy.ops.identify_sfp(hw);
if (hw->phy.sfp_type == ixgbe_sfp_type_not_present)
return physical_layer;
switch (hw->phy.type) {
case ixgbe_phy_sfp_passive_tyco:
case ixgbe_phy_sfp_passive_unknown:
case ixgbe_phy_qsfp_passive_unknown:
physical_layer = IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU;
break;
case ixgbe_phy_sfp_ftl_active:
case ixgbe_phy_sfp_active_unknown:
case ixgbe_phy_qsfp_active_unknown:
physical_layer = IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA;
break;
case ixgbe_phy_sfp_avago:
case ixgbe_phy_sfp_ftl:
case ixgbe_phy_sfp_intel:
case ixgbe_phy_sfp_unknown:
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_1GBE_COMP_CODES, &comp_codes_1g);
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_10GBE_COMP_CODES, &comp_codes_10g);
if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_SR;
else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR;
else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_1000BASE_T;
else if (comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_1000BASE_SX;
else if (comp_codes_1g & IXGBE_SFF_BASEBX10_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_1000BASE_BX;
break;
case ixgbe_phy_qsfp_intel:
case ixgbe_phy_qsfp_unknown:
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_10GBE_COMP, &comp_codes_10g);
if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_SR;
else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE)
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR;
break;
default:
break;
}
return physical_layer;
}
s32 ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw)
{
s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
u32 vendor_oui = 0;
enum ixgbe_sfp_type stored_sfp_type = hw->phy.sfp_type;
u8 identifier = 0;
u8 comp_codes_1g = 0;
u8 comp_codes_10g = 0;
u8 oui_bytes[3] = {0, 0, 0};
u16 enforce_sfp = 0;
u8 connector = 0;
u8 cable_length = 0;
u8 device_tech = 0;
bool active_cable = false;
static bool warned_once = false;
DEBUGFUNC("ixgbe_identify_qsfp_module_generic");
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_fiber_qsfp) {
hw->phy.sfp_type = ixgbe_sfp_type_not_present;
status = IXGBE_ERR_SFP_NOT_PRESENT;
goto out;
}
hw->mac.ops.set_lan_id(hw);
status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER,
&identifier);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
if (identifier != IXGBE_SFF_IDENTIFIER_QSFP_PLUS) {
hw->phy.type = ixgbe_phy_sfp_unsupported;
status = IXGBE_ERR_SFP_NOT_SUPPORTED;
goto out;
}
hw->phy.id = identifier;
status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_QSFP_10GBE_COMP,
&comp_codes_10g);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_QSFP_1GBE_COMP,
&comp_codes_1g);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
if (comp_codes_10g & IXGBE_SFF_QSFP_DA_PASSIVE_CABLE) {
hw->phy.type = ixgbe_phy_qsfp_passive_unknown;
if (hw->bus.lan_id == 0)
hw->phy.sfp_type = ixgbe_sfp_type_da_cu_core0;
else
hw->phy.sfp_type = ixgbe_sfp_type_da_cu_core1;
} else if (comp_codes_10g & (IXGBE_SFF_10GBASESR_CAPABLE |
IXGBE_SFF_10GBASELR_CAPABLE)) {
if (hw->bus.lan_id == 0)
hw->phy.sfp_type = ixgbe_sfp_type_srlr_core0;
else
hw->phy.sfp_type = ixgbe_sfp_type_srlr_core1;
} else {
if (comp_codes_10g & IXGBE_SFF_QSFP_DA_ACTIVE_CABLE)
active_cable = true;
if (!active_cable) {
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_CONNECTOR,
&connector);
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_CABLE_LENGTH,
&cable_length);
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_DEVICE_TECH,
&device_tech);
if ((connector ==
IXGBE_SFF_QSFP_CONNECTOR_NOT_SEPARABLE) &&
(cable_length > 0) &&
((device_tech >> 4) ==
IXGBE_SFF_QSFP_TRANSMITER_850NM_VCSEL))
active_cable = true;
}
if (active_cable) {
hw->phy.type = ixgbe_phy_qsfp_active_unknown;
if (hw->bus.lan_id == 0)
hw->phy.sfp_type =
ixgbe_sfp_type_da_act_lmt_core0;
else
hw->phy.sfp_type =
ixgbe_sfp_type_da_act_lmt_core1;
} else {
hw->phy.type = ixgbe_phy_sfp_unsupported;
status = IXGBE_ERR_SFP_NOT_SUPPORTED;
goto out;
}
}
if (hw->phy.sfp_type != stored_sfp_type)
hw->phy.sfp_setup_needed = true;
hw->phy.multispeed_fiber = false;
if (((comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE) &&
(comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE)) ||
((comp_codes_1g & IXGBE_SFF_1GBASELX_CAPABLE) &&
(comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE)))
hw->phy.multispeed_fiber = true;
if (comp_codes_10g & (IXGBE_SFF_10GBASESR_CAPABLE |
IXGBE_SFF_10GBASELR_CAPABLE)) {
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_VENDOR_OUI_BYTE0,
&oui_bytes[0]);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_VENDOR_OUI_BYTE1,
&oui_bytes[1]);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
status = hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_QSFP_VENDOR_OUI_BYTE2,
&oui_bytes[2]);
if (status != IXGBE_SUCCESS)
goto err_read_i2c_eeprom;
vendor_oui =
((oui_bytes[0] << IXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) |
(oui_bytes[1] << IXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) |
(oui_bytes[2] << IXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT));
if (vendor_oui == IXGBE_SFF_VENDOR_OUI_INTEL)
hw->phy.type = ixgbe_phy_qsfp_intel;
else
hw->phy.type = ixgbe_phy_qsfp_unknown;
ixgbe_get_device_caps(hw, &enforce_sfp);
if (!(enforce_sfp & IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP)) {
if (hw->phy.type == ixgbe_phy_qsfp_intel) {
status = IXGBE_SUCCESS;
} else {
if (hw->allow_unsupported_sfp == true) {
if (!warned_once)
EWARN(hw,
"WARNING: Intel (R) Network Connections are quality tested using Intel (R) Ethernet Optics. "
"Using untested modules is not supported and may cause unstable operation or damage to the module or the adapter. "
"Intel Corporation is not responsible for any harm caused by using untested modules.\n");
warned_once = true;
status = IXGBE_SUCCESS;
} else {
DEBUGOUT("QSFP module not supported\n");
hw->phy.type =
ixgbe_phy_sfp_unsupported;
status = IXGBE_ERR_SFP_NOT_SUPPORTED;
}
}
} else {
status = IXGBE_SUCCESS;
}
}
out:
return status;
err_read_i2c_eeprom:
hw->phy.sfp_type = ixgbe_sfp_type_not_present;
hw->phy.id = 0;
hw->phy.type = ixgbe_phy_unknown;
return IXGBE_ERR_SFP_NOT_PRESENT;
}
s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
u16 *list_offset,
u16 *data_offset)
{
u16 sfp_id;
u16 sfp_type = hw->phy.sfp_type;
DEBUGFUNC("ixgbe_get_sfp_init_sequence_offsets");
if (hw->phy.sfp_type == ixgbe_sfp_type_unknown)
return IXGBE_ERR_SFP_NOT_SUPPORTED;
if (hw->phy.sfp_type == ixgbe_sfp_type_not_present)
return IXGBE_ERR_SFP_NOT_PRESENT;
if ((hw->device_id == IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM) &&
(hw->phy.sfp_type == ixgbe_sfp_type_da_cu))
return IXGBE_ERR_SFP_NOT_SUPPORTED;
if (sfp_type == ixgbe_sfp_type_da_act_lmt_core0 ||
sfp_type == ixgbe_sfp_type_1g_lx_core0 ||
sfp_type == ixgbe_sfp_type_1g_cu_core0 ||
sfp_type == ixgbe_sfp_type_1g_sx_core0 ||
sfp_type == ixgbe_sfp_type_1g_bx_core0)
sfp_type = ixgbe_sfp_type_srlr_core0;
else if (sfp_type == ixgbe_sfp_type_da_act_lmt_core1 ||
sfp_type == ixgbe_sfp_type_1g_lx_core1 ||
sfp_type == ixgbe_sfp_type_1g_cu_core1 ||
sfp_type == ixgbe_sfp_type_1g_sx_core1 ||
sfp_type == ixgbe_sfp_type_1g_bx_core1)
sfp_type = ixgbe_sfp_type_srlr_core1;
if (hw->eeprom.ops.read(hw, IXGBE_PHY_INIT_OFFSET_NL, list_offset)) {
ERROR_REPORT2(IXGBE_ERROR_INVALID_STATE,
"eeprom read at offset %d failed",
IXGBE_PHY_INIT_OFFSET_NL);
return IXGBE_ERR_SFP_NO_INIT_SEQ_PRESENT;
}
if ((!*list_offset) || (*list_offset == 0xFFFF))
return IXGBE_ERR_SFP_NO_INIT_SEQ_PRESENT;
(*list_offset)++;
if (hw->eeprom.ops.read(hw, *list_offset, &sfp_id))
goto err_phy;
while (sfp_id != IXGBE_PHY_INIT_END_NL) {
if (sfp_id == sfp_type) {
(*list_offset)++;
if (hw->eeprom.ops.read(hw, *list_offset, data_offset))
goto err_phy;
if ((!*data_offset) || (*data_offset == 0xFFFF)) {
DEBUGOUT("SFP+ module not supported\n");
return IXGBE_ERR_SFP_NOT_SUPPORTED;
} else {
break;
}
} else {
(*list_offset) += 2;
if (hw->eeprom.ops.read(hw, *list_offset, &sfp_id))
goto err_phy;
}
}
if (sfp_id == IXGBE_PHY_INIT_END_NL) {
DEBUGOUT("No matching SFP+ module found\n");
return IXGBE_ERR_SFP_NOT_SUPPORTED;
}
return IXGBE_SUCCESS;
err_phy:
ERROR_REPORT2(IXGBE_ERROR_INVALID_STATE,
"eeprom read at offset %d failed", *list_offset);
return IXGBE_ERR_PHY;
}
s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 *eeprom_data)
{
DEBUGFUNC("ixgbe_read_i2c_eeprom_generic");
return hw->phy.ops.read_i2c_byte(hw, byte_offset,
IXGBE_I2C_EEPROM_DEV_ADDR,
eeprom_data);
}
static s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 *sff8472_data)
{
return hw->phy.ops.read_i2c_byte(hw, byte_offset,
IXGBE_I2C_EEPROM_DEV_ADDR2,
sff8472_data);
}
s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 eeprom_data)
{
DEBUGFUNC("ixgbe_write_i2c_eeprom_generic");
return hw->phy.ops.write_i2c_byte(hw, byte_offset,
IXGBE_I2C_EEPROM_DEV_ADDR,
eeprom_data);
}
static bool ixgbe_is_sfp_probe(struct ixgbe_hw *hw, u8 offset, u8 addr)
{
if (addr == IXGBE_I2C_EEPROM_DEV_ADDR &&
offset == IXGBE_SFF_IDENTIFIER &&
hw->phy.sfp_type == ixgbe_sfp_type_not_present)
return true;
return false;
}
static s32 ixgbe_read_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 *data, bool lock)
{
s32 status;
u32 max_retry = 10;
u32 retry = 0;
u32 swfw_mask = hw->phy.phy_semaphore_mask;
bool nack = 1;
*data = 0;
DEBUGFUNC("ixgbe_read_i2c_byte_generic");
if (hw->mac.type >= ixgbe_mac_X550)
max_retry = 3;
if (ixgbe_is_sfp_probe(hw, byte_offset, dev_addr))
max_retry = IXGBE_SFP_DETECT_RETRIES;
do {
if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
return IXGBE_ERR_SWFW_SYNC;
ixgbe_i2c_start(hw);
status = ixgbe_clock_out_i2c_byte(hw, dev_addr);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_get_i2c_ack(hw);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_clock_out_i2c_byte(hw, byte_offset);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_get_i2c_ack(hw);
if (status != IXGBE_SUCCESS)
goto fail;
ixgbe_i2c_start(hw);
status = ixgbe_clock_out_i2c_byte(hw, (dev_addr | 0x1));
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_get_i2c_ack(hw);
if (status != IXGBE_SUCCESS)
goto fail;
ixgbe_clock_in_i2c_byte(hw, data);
status = ixgbe_clock_out_i2c_bit(hw, nack);
if (status != IXGBE_SUCCESS)
goto fail;
ixgbe_i2c_stop(hw);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
return IXGBE_SUCCESS;
fail:
ixgbe_i2c_bus_clear(hw);
if (lock) {
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
msec_delay(100);
}
if (retry < max_retry)
DEBUGOUT("I2C byte read error - Retrying.\n");
else
DEBUGOUT("I2C byte read error.\n");
retry++;
} while (retry <= max_retry);
return status;
}
s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 *data)
{
return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr,
data, true);
}
s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 *data)
{
return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr,
data, false);
}
static s32 ixgbe_write_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 data, bool lock)
{
s32 status;
u32 max_retry = 1;
u32 retry = 0;
u32 swfw_mask = hw->phy.phy_semaphore_mask;
DEBUGFUNC("ixgbe_write_i2c_byte_generic");
if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) !=
IXGBE_SUCCESS)
return IXGBE_ERR_SWFW_SYNC;
do {
ixgbe_i2c_start(hw);
status = ixgbe_clock_out_i2c_byte(hw, dev_addr);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_get_i2c_ack(hw);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_clock_out_i2c_byte(hw, byte_offset);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_get_i2c_ack(hw);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_clock_out_i2c_byte(hw, data);
if (status != IXGBE_SUCCESS)
goto fail;
status = ixgbe_get_i2c_ack(hw);
if (status != IXGBE_SUCCESS)
goto fail;
ixgbe_i2c_stop(hw);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
return IXGBE_SUCCESS;
fail:
ixgbe_i2c_bus_clear(hw);
if (retry < max_retry)
DEBUGOUT("I2C byte write error - Retrying.\n");
else
DEBUGOUT("I2C byte write error.\n");
retry++;
} while (retry <= max_retry);
if (lock)
hw->mac.ops.release_swfw_sync(hw, swfw_mask);
return status;
}
s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 data)
{
return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr,
data, true);
}
s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset,
u8 dev_addr, u8 data)
{
return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr,
data, false);
}
static void ixgbe_i2c_start(struct ixgbe_hw *hw)
{
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
DEBUGFUNC("ixgbe_i2c_start");
i2cctl |= IXGBE_I2C_BB_EN_BY_MAC(hw);
ixgbe_set_i2c_data(hw, &i2cctl, 1);
ixgbe_raise_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_SU_STA);
ixgbe_set_i2c_data(hw, &i2cctl, 0);
usec_delay(IXGBE_I2C_T_HD_STA);
ixgbe_lower_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_LOW);
}
static void ixgbe_i2c_stop(struct ixgbe_hw *hw)
{
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN_BY_MAC(hw);
u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN_BY_MAC(hw);
u32 bb_en_bit = IXGBE_I2C_BB_EN_BY_MAC(hw);
DEBUGFUNC("ixgbe_i2c_stop");
ixgbe_set_i2c_data(hw, &i2cctl, 0);
ixgbe_raise_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_SU_STO);
ixgbe_set_i2c_data(hw, &i2cctl, 1);
usec_delay(IXGBE_I2C_T_BUF);
if (bb_en_bit || data_oe_bit || clk_oe_bit) {
i2cctl &= ~bb_en_bit;
i2cctl |= data_oe_bit | clk_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), i2cctl);
IXGBE_WRITE_FLUSH(hw);
}
}
static void ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data)
{
s32 i;
bool bit = 0;
DEBUGFUNC("ixgbe_clock_in_i2c_byte");
*data = 0;
for (i = 7; i >= 0; i--) {
ixgbe_clock_in_i2c_bit(hw, &bit);
*data |= bit << i;
}
}
static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data)
{
s32 status = IXGBE_SUCCESS;
s32 i;
u32 i2cctl;
bool bit;
DEBUGFUNC("ixgbe_clock_out_i2c_byte");
for (i = 7; i >= 0; i--) {
bit = (data >> i) & 0x1;
status = ixgbe_clock_out_i2c_bit(hw, bit);
if (status != IXGBE_SUCCESS)
break;
}
i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
i2cctl |= IXGBE_I2C_DATA_OUT_BY_MAC(hw);
i2cctl |= IXGBE_I2C_DATA_OE_N_EN_BY_MAC(hw);
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), i2cctl);
IXGBE_WRITE_FLUSH(hw);
return status;
}
static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw)
{
u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN_BY_MAC(hw);
s32 status = IXGBE_SUCCESS;
u32 i = 0;
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
u32 timeout = 10;
bool ack = 1;
DEBUGFUNC("ixgbe_get_i2c_ack");
if (data_oe_bit) {
i2cctl |= IXGBE_I2C_DATA_OUT_BY_MAC(hw);
i2cctl |= data_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), i2cctl);
IXGBE_WRITE_FLUSH(hw);
}
ixgbe_raise_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_HIGH);
for (i = 0; i < timeout; i++) {
i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
ack = ixgbe_get_i2c_data(hw, &i2cctl);
usec_delay(1);
if (!ack)
break;
}
if (ack) {
DEBUGOUT("I2C ack was not received.\n");
status = IXGBE_ERR_I2C;
}
ixgbe_lower_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_LOW);
return status;
}
static void ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data)
{
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN_BY_MAC(hw);
DEBUGFUNC("ixgbe_clock_in_i2c_bit");
if (data_oe_bit) {
i2cctl |= IXGBE_I2C_DATA_OUT_BY_MAC(hw);
i2cctl |= data_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), i2cctl);
IXGBE_WRITE_FLUSH(hw);
}
ixgbe_raise_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_HIGH);
i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
*data = ixgbe_get_i2c_data(hw, &i2cctl);
ixgbe_lower_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_LOW);
}
static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data)
{
s32 status;
u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
DEBUGFUNC("ixgbe_clock_out_i2c_bit");
status = ixgbe_set_i2c_data(hw, &i2cctl, data);
if (status == IXGBE_SUCCESS) {
ixgbe_raise_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_HIGH);
ixgbe_lower_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_LOW);
} else {
status = IXGBE_ERR_I2C;
ERROR_REPORT2(IXGBE_ERROR_INVALID_STATE,
"I2C data was not set to %X\n", data);
}
return status;
}
static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
{
u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN_BY_MAC(hw);
u32 i = 0;
u32 timeout = IXGBE_I2C_CLOCK_STRETCHING_TIMEOUT;
u32 i2cctl_r = 0;
DEBUGFUNC("ixgbe_raise_i2c_clk");
if (clk_oe_bit) {
*i2cctl |= clk_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), *i2cctl);
}
for (i = 0; i < timeout; i++) {
*i2cctl |= IXGBE_I2C_CLK_OUT_BY_MAC(hw);
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), *i2cctl);
IXGBE_WRITE_FLUSH(hw);
usec_delay(IXGBE_I2C_T_RISE);
i2cctl_r = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
if (i2cctl_r & IXGBE_I2C_CLK_IN_BY_MAC(hw))
break;
}
}
static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
{
DEBUGFUNC("ixgbe_lower_i2c_clk");
*i2cctl &= ~(IXGBE_I2C_CLK_OUT_BY_MAC(hw));
*i2cctl &= ~IXGBE_I2C_CLK_OE_N_EN_BY_MAC(hw);
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), *i2cctl);
IXGBE_WRITE_FLUSH(hw);
usec_delay(IXGBE_I2C_T_FALL);
}
static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data)
{
u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN_BY_MAC(hw);
s32 status = IXGBE_SUCCESS;
DEBUGFUNC("ixgbe_set_i2c_data");
if (data)
*i2cctl |= IXGBE_I2C_DATA_OUT_BY_MAC(hw);
else
*i2cctl &= ~(IXGBE_I2C_DATA_OUT_BY_MAC(hw));
*i2cctl &= ~data_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), *i2cctl);
IXGBE_WRITE_FLUSH(hw);
usec_delay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA);
if (!data)
return IXGBE_SUCCESS;
if (data_oe_bit) {
*i2cctl |= data_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), *i2cctl);
IXGBE_WRITE_FLUSH(hw);
}
*i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
if (data != ixgbe_get_i2c_data(hw, i2cctl)) {
status = IXGBE_ERR_I2C;
ERROR_REPORT2(IXGBE_ERROR_INVALID_STATE,
"Error - I2C data was not set to %X.\n",
data);
}
return status;
}
static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl)
{
u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN_BY_MAC(hw);
bool data;
DEBUGFUNC("ixgbe_get_i2c_data");
if (data_oe_bit) {
*i2cctl |= data_oe_bit;
IXGBE_WRITE_REG(hw, IXGBE_I2CCTL_BY_MAC(hw), *i2cctl);
IXGBE_WRITE_FLUSH(hw);
usec_delay(IXGBE_I2C_T_FALL);
}
if (*i2cctl & IXGBE_I2C_DATA_IN_BY_MAC(hw))
data = 1;
else
data = 0;
return data;
}
void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw)
{
u32 i2cctl;
u32 i;
DEBUGFUNC("ixgbe_i2c_bus_clear");
ixgbe_i2c_start(hw);
i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL_BY_MAC(hw));
ixgbe_set_i2c_data(hw, &i2cctl, 1);
for (i = 0; i < 9; i++) {
ixgbe_raise_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_HIGH);
ixgbe_lower_i2c_clk(hw, &i2cctl);
usec_delay(IXGBE_I2C_T_LOW);
}
ixgbe_i2c_start(hw);
ixgbe_i2c_stop(hw);
}
s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw)
{
s32 status = IXGBE_SUCCESS;
u16 phy_data = 0;
DEBUGFUNC("ixgbe_tn_check_overtemp");
if (hw->device_id != IXGBE_DEV_ID_82599_T3_LOM)
goto out;
hw->phy.ops.read_reg(hw, IXGBE_TN_LASI_STATUS_REG,
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &phy_data);
if (!(phy_data & IXGBE_TN_LASI_STATUS_TEMP_ALARM))
goto out;
status = IXGBE_ERR_OVERTEMP;
ERROR_REPORT1(IXGBE_ERROR_CAUTION, "Device over temperature");
out:
return status;
}
s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on)
{
u32 status;
u16 reg;
if (!on && ixgbe_mng_present(hw))
return 0;
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
®);
if (status)
return status;
if (on) {
reg &= ~IXGBE_MDIO_PHY_SET_LOW_POWER_MODE;
} else {
if (ixgbe_check_reset_blocked(hw))
return 0;
reg |= IXGBE_MDIO_PHY_SET_LOW_POWER_MODE;
}
status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
reg);
return status;
}