Commit c4900be0 authored by Donald Skidmore's avatar Donald Skidmore Committed by David S. Miller
Browse files

ixgbe: add SFP+ driver support



This patch adds support for SFP+ PHY in the following device ID's (10DB,
10F1, 10E1).  These SFP+ PHY's are accessed via an I2C interface so the
patch also includes functions to support this.

Another feature of note is that the PHY is pluggable and some
rearchitecting was needed to support this.
Signed-off-by: default avatarDonald Skidmore <donald.c.skidmore@intel.com>
Signed-off-by: default avatarPeter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 859ee3c4
......@@ -311,12 +311,15 @@ struct ixgbe_adapter {
unsigned long link_check_timeout;
struct work_struct watchdog_task;
struct work_struct sfp_task;
struct timer_list sfp_timer;
};
enum ixbge_state_t {
__IXGBE_TESTING,
__IXGBE_RESETTING,
__IXGBE_DOWN
__IXGBE_DOWN,
__IXGBE_SFP_MODULE_NOT_FOUND
};
enum ixgbe_boards {
......
......@@ -46,6 +46,8 @@ static s32 ixgbe_setup_copper_link_speed_82598(struct ixgbe_hw *hw,
ixgbe_link_speed speed,
bool autoneg,
bool autoneg_wait_to_complete);
static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
u8 *eeprom_data);
/**
*/
......@@ -53,6 +55,8 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
{
struct ixgbe_mac_info *mac = &hw->mac;
struct ixgbe_phy_info *phy = &hw->phy;
s32 ret_val = 0;
u16 list_offset, data_offset;
/* Call PHY identify routine to get the phy type */
ixgbe_identify_phy_generic(hw);
......@@ -64,6 +68,27 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
phy->ops.get_firmware_version =
&ixgbe_get_phy_firmware_version_tnx;
break;
case ixgbe_phy_nl:
phy->ops.reset = &ixgbe_reset_phy_nl;
/* Call SFP+ identify routine to get the SFP+ module type */
ret_val = phy->ops.identify_sfp(hw);
if (ret_val != 0)
goto out;
else if (hw->phy.sfp_type == ixgbe_sfp_type_unknown) {
ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED;
goto out;
}
/* Check to see if SFP+ module is supported */
ret_val = ixgbe_get_sfp_init_sequence_offsets(hw,
&list_offset,
&data_offset);
if (ret_val != 0) {
ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED;
goto out;
}
break;
default:
break;
}
......@@ -82,7 +107,8 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
mac->max_rx_queues = IXGBE_82598_MAX_RX_QUEUES;
mac->max_tx_queues = IXGBE_82598_MAX_TX_QUEUES;
return 0;
out:
return ret_val;
}
/**
......@@ -191,7 +217,10 @@ static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
case IXGBE_DEV_ID_82598EB_CX4:
case IXGBE_DEV_ID_82598_CX4_DUAL_PORT:
case IXGBE_DEV_ID_82598_DA_DUAL_PORT:
case IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM:
case IXGBE_DEV_ID_82598EB_XF_LR:
case IXGBE_DEV_ID_82598EB_SFP_LOM:
media_type = ixgbe_media_type_fiber;
break;
case IXGBE_DEV_ID_82598AT:
......@@ -399,6 +428,46 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
{
u32 links_reg;
u32 i;
u16 link_reg, adapt_comp_reg;
/*
* SERDES PHY requires us to read link status from register 0xC79F.
* Bit 0 set indicates link is up/ready; clear indicates link down.
* 0xC00C is read to check that the XAUI lanes are active. Bit 0
* clear indicates active; set indicates inactive.
*/
if (hw->phy.type == ixgbe_phy_nl) {
hw->phy.ops.read_reg(hw, 0xC79F, IXGBE_TWINAX_DEV, &link_reg);
hw->phy.ops.read_reg(hw, 0xC79F, IXGBE_TWINAX_DEV, &link_reg);
hw->phy.ops.read_reg(hw, 0xC00C, IXGBE_TWINAX_DEV,
&adapt_comp_reg);
if (link_up_wait_to_complete) {
for (i = 0; i < IXGBE_LINK_UP_TIME; i++) {
if ((link_reg & 1) &&
((adapt_comp_reg & 1) == 0)) {
*link_up = true;
break;
} else {
*link_up = false;
}
msleep(100);
hw->phy.ops.read_reg(hw, 0xC79F,
IXGBE_TWINAX_DEV,
&link_reg);
hw->phy.ops.read_reg(hw, 0xC00C,
IXGBE_TWINAX_DEV,
&adapt_comp_reg);
}
} else {
if ((link_reg & 1) && ((adapt_comp_reg & 1) == 0))
*link_up = true;
else
*link_up = false;
}
if (*link_up == false)
goto out;
}
links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
if (link_up_wait_to_complete) {
......@@ -424,6 +493,7 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
else
*speed = IXGBE_LINK_SPEED_1GB_FULL;
out:
return 0;
}
......@@ -858,6 +928,69 @@ s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val)
return 0;
}
/**
* ixgbe_read_i2c_eeprom_82598 - Read 8 bit EEPROM word of an SFP+ module
* over I2C interface through an intermediate phy.
* @hw: pointer to hardware structure
* @byte_offset: EEPROM byte offset to read
* @eeprom_data: value read
*
* Performs byte read operation to SFP module's EEPROM over I2C interface.
**/
s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
u8 *eeprom_data)
{
s32 status = 0;
u16 sfp_addr = 0;
u16 sfp_data = 0;
u16 sfp_stat = 0;
u32 i;
if (hw->phy.type == ixgbe_phy_nl) {
/*
* phy SDA/SCL registers are at addresses 0xC30A to
* 0xC30D. These registers are used to talk to the SFP+
* module's EEPROM through the SDA/SCL (I2C) interface.
*/
sfp_addr = (IXGBE_I2C_EEPROM_DEV_ADDR << 8) + byte_offset;
sfp_addr = (sfp_addr | IXGBE_I2C_EEPROM_READ_MASK);
hw->phy.ops.write_reg(hw,
IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR,
IXGBE_MDIO_PMA_PMD_DEV_TYPE,
sfp_addr);
/* Poll status */
for (i = 0; i < 100; i++) {
hw->phy.ops.read_reg(hw,
IXGBE_MDIO_PMA_PMD_SDA_SCL_STAT,
IXGBE_MDIO_PMA_PMD_DEV_TYPE,
&sfp_stat);
sfp_stat = sfp_stat & IXGBE_I2C_EEPROM_STATUS_MASK;
if (sfp_stat != IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS)
break;
msleep(10);
}
if (sfp_stat != IXGBE_I2C_EEPROM_STATUS_PASS) {
hw_dbg(hw, "EEPROM read did not pass.\n");
status = IXGBE_ERR_SFP_NOT_PRESENT;
goto out;
}
/* Read data */
hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA,
IXGBE_MDIO_PMA_PMD_DEV_TYPE, &sfp_data);
*eeprom_data = (u8)(sfp_data >> 8);
} else {
status = IXGBE_ERR_PHY;
goto out;
}
out:
return status;
}
/**
* ixgbe_get_supported_physical_layer_82598 - Returns physical layer type
* @hw: pointer to hardware structure
......@@ -873,8 +1006,12 @@ s32 ixgbe_get_supported_physical_layer_82598(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_82598_CX4_DUAL_PORT:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_CX4;
break;
case IXGBE_DEV_ID_82598_DA_DUAL_PORT:
physical_layer = IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU;
break;
case IXGBE_DEV_ID_82598AF_DUAL_PORT:
case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
case IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_SR;
break;
case IXGBE_DEV_ID_82598EB_XF_LR:
......@@ -884,6 +1021,24 @@ s32 ixgbe_get_supported_physical_layer_82598(struct ixgbe_hw *hw)
physical_layer = (IXGBE_PHYSICAL_LAYER_10GBASE_T |
IXGBE_PHYSICAL_LAYER_1000BASE_T);
break;
case IXGBE_DEV_ID_82598EB_SFP_LOM:
hw->phy.ops.identify_sfp(hw);
switch (hw->phy.sfp_type) {
case ixgbe_sfp_type_da_cu:
physical_layer = IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU;
break;
case ixgbe_sfp_type_sr:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_SR;
break;
case ixgbe_sfp_type_lr:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR;
break;
default:
physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
break;
}
break;
default:
physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
......@@ -935,12 +1090,13 @@ static struct ixgbe_eeprom_operations eeprom_ops_82598 = {
static struct ixgbe_phy_operations phy_ops_82598 = {
.identify = &ixgbe_identify_phy_generic,
/* .identify_sfp = &ixgbe_identify_sfp_module_generic, */
.identify_sfp = &ixgbe_identify_sfp_module_generic,
.reset = &ixgbe_reset_phy_generic,
.read_reg = &ixgbe_read_phy_reg_generic,
.write_reg = &ixgbe_write_phy_reg_generic,
.setup_link = &ixgbe_setup_phy_link_generic,
.setup_link_speed = &ixgbe_setup_phy_link_speed_generic,
.read_i2c_eeprom = &ixgbe_read_i2c_eeprom_82598,
};
struct ixgbe_info ixgbe_82598_info = {
......
......@@ -74,8 +74,14 @@ static struct pci_device_id ixgbe_pci_tbl[] = {
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT),
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT),
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM),
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR),
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM),
board_82598 },
/* required last entry */
{0, }
......@@ -2679,6 +2685,57 @@ err_alloc_queues:
return err;
}
/**
* ixgbe_sfp_timer - worker thread to find a missing module
* @data: pointer to our adapter struct
**/
static void ixgbe_sfp_timer(unsigned long data)
{
struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
/* Do the sfp_timer outside of interrupt context due to the
* delays that sfp+ detection requires
*/
schedule_work(&adapter->sfp_task);
}
/**
* ixgbe_sfp_task - worker thread to find a missing module
* @work: pointer to work_struct containing our data
**/
static void ixgbe_sfp_task(struct work_struct *work)
{
struct ixgbe_adapter *adapter = container_of(work,
struct ixgbe_adapter,
sfp_task);
struct ixgbe_hw *hw = &adapter->hw;
if ((hw->phy.type == ixgbe_phy_nl) &&
(hw->phy.sfp_type == ixgbe_sfp_type_not_present)) {
s32 ret = hw->phy.ops.identify_sfp(hw);
if (ret)
goto reschedule;
ret = hw->phy.ops.reset(hw);
if (ret == IXGBE_ERR_SFP_NOT_SUPPORTED) {
DPRINTK(PROBE, ERR, "failed to initialize because an "
"unsupported SFP+ module type was detected.\n"
"Reload the driver after installing a "
"supported module.\n");
unregister_netdev(adapter->netdev);
} else {
DPRINTK(PROBE, INFO, "detected SFP+: %d\n",
hw->phy.sfp_type);
}
/* don't need this routine any more */
clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
}
return;
reschedule:
if (test_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state))
mod_timer(&adapter->sfp_timer,
round_jiffies(jiffies + (2 * HZ)));
}
/**
* ixgbe_sw_init - Initialize general software structures (struct ixgbe_adapter)
* @adapter: board private structure to initialize
......@@ -4006,11 +4063,31 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
/* PHY */
memcpy(&hw->phy.ops, ii->phy_ops, sizeof(hw->phy.ops));
/* phy->sfp_type = ixgbe_sfp_type_unknown; */
hw->phy.sfp_type = ixgbe_sfp_type_unknown;
/* set up this timer and work struct before calling get_invariants
* which might start the timer
*/
init_timer(&adapter->sfp_timer);
adapter->sfp_timer.function = &ixgbe_sfp_timer;
adapter->sfp_timer.data = (unsigned long) adapter;
INIT_WORK(&adapter->sfp_task, ixgbe_sfp_task);
err = ii->get_invariants(hw);
if (err)
if (err == IXGBE_ERR_SFP_NOT_PRESENT) {
/* start a kernel thread to watch for a module to arrive */
set_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
mod_timer(&adapter->sfp_timer,
round_jiffies(jiffies + (2 * HZ)));
err = 0;
} else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) {
DPRINTK(PROBE, ERR, "failed to load because an "
"unsupported SFP+ module type was detected.\n");
goto err_hw_init;
} else if (err) {
goto err_hw_init;
}
/* setup the private structure */
err = ixgbe_sw_init(adapter);
......@@ -4144,6 +4221,9 @@ err_hw_init:
err_sw_init:
ixgbe_reset_interrupt_capability(adapter);
err_eeprom:
clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
del_timer_sync(&adapter->sfp_timer);
cancel_work_sync(&adapter->sfp_task);
iounmap(hw->hw_addr);
err_ioremap:
free_netdev(netdev);
......@@ -4170,8 +4250,15 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
struct ixgbe_adapter *adapter = netdev_priv(netdev);
set_bit(__IXGBE_DOWN, &adapter->state);
/* clear the module not found bit to make sure the worker won't
* reschedule
*/
clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
del_timer_sync(&adapter->watchdog_timer);
del_timer_sync(&adapter->sfp_timer);
cancel_work_sync(&adapter->watchdog_task);
cancel_work_sync(&adapter->sfp_task);
flush_scheduled_work();
#ifdef CONFIG_IXGBE_DCA
......@@ -4182,7 +4269,8 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
}
#endif
unregister_netdev(netdev);
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
ixgbe_reset_interrupt_capability(adapter);
......
......@@ -127,6 +127,9 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id)
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;
......@@ -429,6 +432,261 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
return 0;
}
/**
* 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, block_crc;
bool end_data = false;
u16 list_offset, data_offset;
u16 phy_data = 0;
s32 ret_val = 0;
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;
msleep(10);
}
if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) != 0) {
hw_dbg(hw, "PHY reset did not complete.\n");
ret_val = IXGBE_ERR_PHY;
goto out;
}
/* Get init offsets */
ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
&data_offset);
if (ret_val != 0)
goto out;
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++;
hw_dbg(hw, "DELAY: %d MS\n", edata);
msleep(edata);
break;
case IXGBE_DATA_NL:
hw_dbg(hw, "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);
hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword,
phy_offset);
data_offset++;
phy_offset++;
}
break;
case IXGBE_CONTROL_NL:
data_offset++;
hw_dbg(hw, "CONTROL: \n");
if (edata == IXGBE_CONTROL_EOL_NL) {
hw_dbg(hw, "EOL\n");
end_data = true;
} else if (edata == IXGBE_CONTROL_SOL_NL) {
hw_dbg(hw, "SOL\n");
} else {
hw_dbg(hw, "Bad control value\n");
ret_val = IXGBE_ERR_PHY;
goto out;
}
break;
default:
hw_dbg(hw, "Bad control type\n");
ret_val = IXGBE_ERR_PHY;
goto out;
}
}
out:
return ret_val;
}
/**
* ixgbe_identify_sfp_module_generic - Identifies SFP module and assigns
* the PHY type.
* @hw: pointer to hardware structure
*
* Searches for and indentifies the SFP module. Assings appropriate PHY type.
**/
s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
{
s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
u32 vendor_oui = 0;
u8 identifier = 0;
u8 comp_codes_1g = 0;
u8 comp_codes_10g = 0;
u8 oui_bytes[4] = {0, 0, 0, 0};
u8 transmission_media = 0;
status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER,
&identifier);
if (status == IXGBE_ERR_SFP_NOT_PRESENT) {
hw->phy.sfp_type = ixgbe_sfp_type_not_present;
goto out;
}
if (identifier == IXGBE_SFF_IDENTIFIER_SFP) {
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);
hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_TRANSMISSION_MEDIA,
&transmission_media);
/* ID Module
* =========
* 0 SFP_DA_CU
* 1 SFP_SR
* 2 SFP_LR
*/
if (transmission_media & IXGBE_SFF_TWIN_AX_CAPABLE)
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;
/* Determine PHY vendor */
if (hw->phy.type == ixgbe_phy_unknown) {
hw->phy.id = identifier;
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_VENDOR_OUI_BYTE0,
&oui_bytes[0]);
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_VENDOR_OUI_BYTE1,
&oui_bytes[1]);
hw->phy.ops.read_i2c_eeprom(hw,
IXGBE_SFF_VENDOR_OUI_BYTE2,
&oui_bytes[2]);
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 (transmission_media &
IXGBE_SFF_TWIN_AX_CAPABLE)
hw->phy.type = ixgbe_phy_tw_tyco;
break;
case IXGBE_SFF_VENDOR_OUI_FTL:
hw->phy.type = ixgbe_phy_sfp_ftl;
break;
case IXGBE_SFF_VENDOR_OUI_AVAGO:
hw->phy.type = ixgbe_phy_sfp_avago;
break;
default:
if (transmission_media &
IXGBE_SFF_TWIN_AX_CAPABLE)
hw->phy.type = ixgbe_phy_tw_unknown;
else
hw->phy.type = ixgbe_phy_sfp_unknown;
break;
}
}
status = 0;
}
out:
return status;
}
/**
* ixgbe_get_sfp_init_sequence_offsets - Checks the MAC's EEPROM to see
* if it supports a given SFP+ module type, if so it returns the offsets to the
* phy init sequence block.
* @hw: pointer to hardware structure
* @list_offset: offset to the SFP ID list
* @data_offset: offset to the SFP data block
**/
s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
u16 *list_offset,
u16 *data_offset)
{
u16 sfp_id;
if (hw->phy.sfp_type == ixgbe_sfp_type_unknown)
return IXGBE_ERR_SFP_NOT_SUPPORTED;