summaryrefslogtreecommitdiffstats
path: root/sys/dev/ixgbe/ixgbe_phy.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ixgbe/ixgbe_phy.c')
-rw-r--r--sys/dev/ixgbe/ixgbe_phy.c448
1 files changed, 352 insertions, 96 deletions
diff --git a/sys/dev/ixgbe/ixgbe_phy.c b/sys/dev/ixgbe/ixgbe_phy.c
index e19d303..134e310 100644
--- a/sys/dev/ixgbe/ixgbe_phy.c
+++ b/sys/dev/ixgbe/ixgbe_phy.c
@@ -1,6 +1,6 @@
-/*******************************************************************************
+/******************************************************************************
- Copyright (c) 2001-2007, Intel Corporation
+ Copyright (c) 2001-2008, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -29,43 +29,32 @@
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-*******************************************************************************/
-/* $FreeBSD$ */
-
+******************************************************************************/
+/*$FreeBSD$*/
#include "ixgbe_api.h"
#include "ixgbe_common.h"
#include "ixgbe_phy.h"
/**
- * ixgbe_init_shared_code_phy - Initialize PHY shared code
- * @hw: pointer to hardware structure
- **/
-s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw)
-{
- /* Assign function pointers */
- ixgbe_assign_func_pointers_phy(hw);
-
- return IXGBE_SUCCESS;
-}
-
-/**
- * ixgbe_assign_func_pointers_phy - Assigns PHY-specific function pointers
- * @hw: pointer to hardware structure
+ * ixgbe_init_phy_ops_generic - Inits PHY function ptrs
+ * @hw: pointer to the hardware structure
*
- * Note, generic function pointers have already been assigned, so the
- * function pointers set here are only for PHY-specific functions.
+ * Initialize the function pointers.
**/
-s32 ixgbe_assign_func_pointers_phy(struct ixgbe_hw *hw)
+s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw)
{
- hw->func.ixgbe_func_reset_phy =
- &ixgbe_reset_phy_generic;
- hw->func.ixgbe_func_read_phy_reg =
- &ixgbe_read_phy_reg_generic;
- hw->func.ixgbe_func_write_phy_reg =
- &ixgbe_write_phy_reg_generic;
- hw->func.ixgbe_func_identify_phy =
- &ixgbe_identify_phy_generic;
+ struct ixgbe_phy_info *phy = &hw->phy;
+
+ /* PHY */
+ 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.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 = NULL;
return IXGBE_SUCCESS;
}
@@ -81,15 +70,21 @@ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
u32 phy_addr;
- for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) {
- if (ixgbe_validate_phy_addr(hw, phy_addr)) {
- hw->phy.addr = phy_addr;
- ixgbe_get_phy_id(hw);
- hw->phy.type = ixgbe_get_phy_type_from_id(hw->phy.id);
- status = IXGBE_SUCCESS;
- break;
+ if (hw->phy.type == ixgbe_phy_unknown) {
+ for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) {
+ if (ixgbe_validate_phy_addr(hw, phy_addr)) {
+ hw->phy.addr = phy_addr;
+ ixgbe_get_phy_id(hw);
+ hw->phy.type =
+ ixgbe_get_phy_type_from_id(hw->phy.id);
+ status = IXGBE_SUCCESS;
+ break;
+ }
}
+ } else {
+ status = IXGBE_SUCCESS;
}
+
return status;
}
@@ -104,10 +99,8 @@ bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr)
bool valid = FALSE;
hw->phy.addr = phy_addr;
- ixgbe_read_phy_reg_generic(hw,
- IXGBE_MDIO_PHY_ID_HIGH,
- IXGBE_MDIO_PMA_PMD_DEV_TYPE,
- &phy_id);
+ 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;
@@ -126,17 +119,15 @@ s32 ixgbe_get_phy_id(struct ixgbe_hw *hw)
u16 phy_id_high = 0;
u16 phy_id_low = 0;
- status = ixgbe_read_phy_reg_generic(hw,
- IXGBE_MDIO_PHY_ID_HIGH,
- IXGBE_MDIO_PMA_PMD_DEV_TYPE,
- &phy_id_high);
+ 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 = ixgbe_read_phy_reg_generic(hw,
- IXGBE_MDIO_PHY_ID_LOW,
- IXGBE_MDIO_PMA_PMD_DEV_TYPE,
- &phy_id_low);
+ 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);
}
@@ -154,14 +145,21 @@ enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id)
enum ixgbe_phy_type phy_type;
switch (phy_id) {
+ case TN1010_PHY_ID:
+ phy_type = ixgbe_phy_tn;
+ break;
case QT2022_PHY_ID:
phy_type = ixgbe_phy_qt;
break;
+ case ATH_PHY_ID:
+ phy_type = ixgbe_phy_nl;
+ break;
default:
phy_type = ixgbe_phy_unknown;
break;
}
+ DEBUGOUT1("phy type found is %d\n", phy_type);
return phy_type;
}
@@ -175,9 +173,9 @@ s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw)
* Perform soft PHY reset to the PHY_XS.
* This will cause a soft reset to the PHY
*/
- return ixgbe_write_phy_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
- IXGBE_MDIO_PHY_XS_DEV_TYPE,
- IXGBE_MDIO_PHY_XS_RESET);
+ return hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+ IXGBE_MDIO_PHY_XS_DEV_TYPE,
+ IXGBE_MDIO_PHY_XS_RESET);
}
/**
@@ -187,7 +185,7 @@ s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw)
* @phy_data: Pointer to read data from PHY register
**/
s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
- u32 device_type, u16 *phy_data)
+ u32 device_type, u16 *phy_data)
{
u32 command;
u32 i;
@@ -206,9 +204,9 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
if (status == IXGBE_SUCCESS) {
/* Setup and write the address cycle 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));
+ (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);
@@ -238,9 +236,9 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
* 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_READ | IXGBE_MSCA_MDI_COMMAND));
+ (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);
@@ -274,6 +272,7 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
ixgbe_release_swfw_sync(hw, gssr);
}
+
return status;
}
@@ -285,7 +284,7 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
* @phy_data: Data to write to the PHY register
**/
s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
- u32 device_type, u16 phy_data)
+ u32 device_type, u16 phy_data)
{
u32 command;
u32 i;
@@ -306,9 +305,9 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
/* Setup and write the address cycle 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));
+ (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);
@@ -322,14 +321,14 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
command = IXGBE_READ_REG(hw, IXGBE_MSCA);
- if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
- DEBUGFUNC("PHY address cmd didn't complete\n");
+ if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
break;
- }
}
- if ((command & IXGBE_MSCA_MDI_COMMAND) != 0)
+ if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
+ DEBUGOUT("PHY address cmd didn't complete\n");
status = IXGBE_ERR_PHY;
+ }
if (status == IXGBE_SUCCESS) {
/*
@@ -337,9 +336,9 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
* 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_WRITE | IXGBE_MSCA_MDI_COMMAND));
+ (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);
@@ -353,15 +352,14 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
command = IXGBE_READ_REG(hw, IXGBE_MSCA);
- if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
- DEBUGFUNC("PHY write command did not "
- "complete.\n");
+ if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
break;
- }
}
- if ((command & IXGBE_MSCA_MDI_COMMAND) != 0)
+ if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
+ DEBUGOUT("PHY address cmd didn't complete\n");
status = IXGBE_ERR_PHY;
+ }
}
ixgbe_release_swfw_sync(hw, gssr);
@@ -371,45 +369,303 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
}
/**
- * ixgbe_setup_phy_link - Restart PHY autoneg
+ * ixgbe_setup_phy_link_generic - Set and restart autoneg
* @hw: pointer to hardware structure
*
* Restart autonegotiation and PHY and waits for completion.
**/
-s32 ixgbe_setup_phy_link(struct ixgbe_hw *hw)
+s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_NOT_IMPLEMENTED;
+ u32 time_out;
+ u32 max_time_out = 10;
+ u16 autoneg_reg = IXGBE_MII_AUTONEG_REG;
+
+ /*
+ * Set advertisement settings in PHY based on autoneg_advertised
+ * settings. If autoneg_advertised = 0, then advertise default values
+ * tnx devices cannot be "forced" to a autoneg 10G and fail. But can
+ * for a 1G.
+ */
+ hw->phy.ops.read_reg(hw, IXGBE_MII_SPEED_SELECTION_REG,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE, &autoneg_reg);
+
+ if (hw->phy.autoneg_advertised == IXGBE_LINK_SPEED_1GB_FULL)
+ autoneg_reg &= 0xEFFF; /* 0 in bit 12 is 1G operation */
+ else
+ autoneg_reg |= 0x1000; /* 1 in bit 12 is 10G/1G operation */
+
+ hw->phy.ops.write_reg(hw, IXGBE_MII_SPEED_SELECTION_REG,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE, autoneg_reg);
+
+ /* Restart PHY autonegotiation and wait for completion */
+ 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);
+
+ /* Wait for autonegotiation to finish */
+ for (time_out = 0; time_out < max_time_out; time_out++) {
+ usec_delay(10);
+ /* Restart PHY autonegotiation and wait for completion */
+ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_AUTO_NEG_STATUS,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+ &autoneg_reg);
+
+ autoneg_reg &= IXGBE_MII_AUTONEG_COMPLETE;
+ if (autoneg_reg == IXGBE_MII_AUTONEG_COMPLETE) {
+ status = IXGBE_SUCCESS;
+ break;
+ }
+ }
+
+ if (time_out == max_time_out)
+ status = IXGBE_ERR_LINK_SETUP;
+
+ return status;
+}
+
+/**
+ * ixgbe_setup_phy_link_speed_generic - Sets the auto advertised capabilities
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: TRUE if autonegotiation enabled
+ **/
+s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
+ ixgbe_link_speed speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete)
{
- return ixgbe_call_func(hw, ixgbe_func_setup_phy_link, (hw),
- IXGBE_NOT_IMPLEMENTED);
+ UNREFERENCED_PARAMETER(autoneg);
+ UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
+
+ /*
+ * Clear autoneg_advertised and set new values based on input link
+ * speed.
+ */
+ 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_1GB_FULL) {
+ hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+ }
+
+ /* Setup link based on the new speed settings */
+ hw->phy.ops.setup_link(hw);
+
+ return IXGBE_SUCCESS;
}
/**
- * ixgbe_check_phy_link - Determine link and speed status
+ * ixgbe_check_phy_link_tnx - Determine link and speed status
* @hw: pointer to hardware structure
*
- * Reads a PHY register to determine if link is up and the current speed for
+ * Reads the VS1 register to determine if link is up and the current speed for
* the PHY.
**/
-s32 ixgbe_check_phy_link(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
- bool *link_up)
+s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
+ bool *link_up)
{
- return ixgbe_call_func(hw, ixgbe_func_check_phy_link, (hw, speed,
- link_up), IXGBE_NOT_IMPLEMENTED);
+ s32 status = IXGBE_SUCCESS;
+ u32 time_out;
+ u32 max_time_out = 10;
+ u16 phy_link = 0;
+ u16 phy_speed = 0;
+ u16 phy_data = 0;
+
+ /* Initialize speed and link to default case */
+ *link_up = FALSE;
+ *speed = IXGBE_LINK_SPEED_10GB_FULL;
+
+ /*
+ * 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.
+ */
+ 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;
}
/**
- * ixgbe_setup_phy_link_speed - Set auto advertise
+ * ixgbe_get_phy_firmware_version_tnx - Gets the PHY Firmware Version
* @hw: pointer to hardware structure
- * @speed: new link speed
- * @autoneg: TRUE if autonegotiation enabled
- *
- * Sets the auto advertised capabilities
+ * @firmware_version: pointer to the PHY Firmware Version
**/
-s32 ixgbe_setup_phy_link_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed,
- bool autoneg,
- bool autoneg_wait_to_complete)
+s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
+ u16 *firmware_version)
{
- return ixgbe_call_func(hw, ixgbe_func_setup_phy_link_speed, (hw, speed,
- autoneg, autoneg_wait_to_complete),
- IXGBE_NOT_IMPLEMENTED);
+ s32 status = IXGBE_SUCCESS;
+
+ status = hw->phy.ops.read_reg(hw, TNX_FW_REV,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+ firmware_version);
+
+ return status;
}
+/**
+ * ixgbe_reset_phy_nl - Performs a PHY reset
+ * @hw: pointer to hardware structure
+ **/
+s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
+{
+ u16 phy_offset, control, eword, edata, list_crc, block_crc, id, sfp_id;
+ bool end_data = FALSE;
+ u16 list_offset, data_offset;
+ u16 phy_data = 0;
+ s32 ret_val = IXGBE_SUCCESS;
+ u32 i;
+
+ hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+ IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data);
+
+ /* reset the PHY and poll for completion */
+ 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;
+ }
+
+ /* read offset to PHY init contents */
+ hw->eeprom.ops.read(hw, IXGBE_PHY_INIT_OFFSET_NL, &list_offset);
+
+ if ((!list_offset) || (list_offset == 0xFFFF)) {
+ ret_val = IXGBE_ERR_PHY;
+ goto out;
+ }
+
+ /* Acquire the CRC */
+ hw->eeprom.ops.read(hw, list_offset, &list_crc);
+
+ /* Shift offset to first ID word */
+ list_offset++;
+
+ /* determine the sfp sequence based on device ID */
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_82598_DA_DUAL_PORT:
+ sfp_id = 0;
+ break;
+ case IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM:
+ sfp_id = 1;
+ break;
+ default:
+ ret_val = IXGBE_ERR_PHY;
+ goto out;
+ }
+
+ /*
+ * Find the matching sfp ID in the EEPROM
+ * and program the init sequence
+ */
+ hw->eeprom.ops.read(hw, list_offset, &id);
+
+ while (!((id == IXGBE_CONTROL_EOL_NL) || (end_data == TRUE))) {
+ if (id == sfp_id) {
+ list_offset++;
+ hw->eeprom.ops.read(hw, list_offset, &data_offset);
+ if ((!data_offset) || (data_offset == 0xFFFF))
+ break;
+ ret_val = hw->eeprom.ops.read(hw, data_offset,
+ &block_crc);
+ data_offset++;
+ while (!end_data) {
+ /*
+ * Read control word from PHY init contents
+ * offset
+ */
+ ret_val = hw->eeprom.ops.read(hw, data_offset,
+ &eword);
+ 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++;
+ hw->eeprom.ops.read(hw, data_offset++,
+ &phy_offset);
+ for (i = 0; i < edata; i++) {
+ hw->eeprom.ops.read(hw,
+ data_offset,
+ &eword);
+ 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;
+ }
+ }
+ } else {
+ list_offset += 2;
+ ret_val = hw->eeprom.ops.read(hw, list_offset, &id);
+ if (ret_val)
+ goto out;
+ }
+ }
+
+out:
+ return ret_val;
+}
OpenPOWER on IntegriCloud