--- /dev/null
+From: John Ronciak <john.ronciak@intel.com>
+Subject: ixgbe driver update to add Longcove (SFP+) NIC support for FCoE needs
+Acked-by: Karsten Keil <kkeil@novell.com>
+Reference: bnc#442411
+
+
+The patch that will be attached is to update the ixgbe driver to add SFP+ NIC
+support to SLES11 beta4 version of the ixgbe driver. NIC have been sent to
+Nurnberg for testing purposes. We have already been testing the patch with
+both our test lab and in our FCoE testing.
+
+
+---
+ drivers/net/ixgbe/ixgbe.h | 5
+ drivers/net/ixgbe/ixgbe_82598.c | 161 ++++++++++++++++++++++++
+ drivers/net/ixgbe/ixgbe_main.c | 92 +++++++++++++-
+ drivers/net/ixgbe/ixgbe_phy.c | 258 ++++++++++++++++++++++++++++++++++++++++
+ drivers/net/ixgbe/ixgbe_phy.h | 18 ++
+ drivers/net/ixgbe/ixgbe_type.h | 19 ++
+ 6 files changed, 546 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ixgbe/ixgbe_82598.c
++++ b/drivers/net/ixgbe/ixgbe_82598.c
+@@ -46,6 +46,8 @@ static s32 ixgbe_setup_copper_link_speed
+ 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,12 +55,35 @@ static s32 ixgbe_get_invariants_82598(st
+ {
+ 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);
+
+ /* PHY Init */
+ switch (phy->type) {
++ 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;
+ case ixgbe_phy_tn:
+ phy->ops.check_link = &ixgbe_check_phy_link_tnx;
+ phy->ops.get_firmware_version =
+@@ -82,7 +107,8 @@ static s32 ixgbe_get_invariants_82598(st
+ 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_m
+ 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,47 @@ static s32 ixgbe_check_mac_link_82598(st
+ {
+ 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.
++ * OxC00C 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 +494,7 @@ static s32 ixgbe_check_mac_link_82598(st
+ else
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+
++out:
+ return 0;
+ }
+
+@@ -859,6 +930,69 @@ s32 ixgbe_write_analog_reg8_82598(struct
+ }
+
+ /**
++ * ixgbe_read_i2c_eeprom_82598 - Reads 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,13 +1007,35 @@ s32 ixgbe_get_supported_physical_layer_8
+ 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:
+ physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR;
+ 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;
+ case IXGBE_DEV_ID_82598AT:
+ physical_layer = (IXGBE_PHYSICAL_LAYER_10GBASE_T |
+ IXGBE_PHYSICAL_LAYER_1000BASE_T);
+@@ -935,12 +1091,13 @@ static struct ixgbe_eeprom_operations ee
+
+ 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 = {
+--- a/drivers/net/ixgbe/ixgbe.h
++++ b/drivers/net/ixgbe/ixgbe.h
+@@ -318,12 +318,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 {
+--- a/drivers/net/ixgbe/ixgbe_main.c
++++ b/drivers/net/ixgbe/ixgbe_main.c
+@@ -74,8 +74,14 @@ static struct pci_device_id ixgbe_pci_tb
+ 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, }
+@@ -2678,6 +2684,56 @@ err_alloc_queues:
+ }
+
+ /**
++ * 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
+ *
+@@ -4002,11 +4058,30 @@ static int __devinit ixgbe_probe(struct
+
+ /* 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 +4219,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 +4248,13 @@ static void __devexit ixgbe_remove(struc
+ 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();
+
+ #if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
+@@ -4182,7 +4265,8 @@ static void __devexit ixgbe_remove(struc
+ }
+
+ #endif
+- unregister_netdev(netdev);
++ if (netdev->reg_state == NETREG_REGISTERED)
++ unregister_netdev(netdev);
+
+ ixgbe_reset_interrupt_capability(adapter);
+
+--- a/drivers/net/ixgbe/ixgbe_phy.c
++++ b/drivers/net/ixgbe/ixgbe_phy.c
+@@ -127,6 +127,9 @@ static enum ixgbe_phy_type ixgbe_get_phy
+ 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;
+@@ -430,6 +433,261 @@ s32 ixgbe_setup_phy_link_speed_generic(s
+ }
+
+ /**
++ * 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 identifies the SFP module. Assigns 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;
++
++ 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;
++
++ /* Read offset to PHY init contents */
++ hw->eeprom.ops.read(hw, IXGBE_PHY_INIT_OFFSET_NL, list_offset);
++
++ if ((!*list_offset) || (*list_offset == 0xFFFF))
++ return IXGBE_ERR_PHY;
++
++ /* Shift offset to first ID word */
++ (*list_offset)++;
++
++ /*
++ * Find the matching SFP ID in the EEPROM
++ * and program the init sequence
++ */
++ hw->eeprom.ops.read(hw, *list_offset, &sfp_id);
++
++ while (sfp_id != IXGBE_PHY_INIT_END_NL) {
++ if (sfp_id == hw->phy.sfp_type) {
++ (*list_offset)++;
++ hw->eeprom.ops.read(hw, *list_offset, data_offset);
++ if ((!*data_offset) || (*data_offset == 0xFFFF)) {
++ hw_dbg(hw, "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))
++ return IXGBE_ERR_PHY;
++ }
++ }
++
++ if (sfp_id == IXGBE_PHY_INIT_END_NL) {
++ hw_dbg(hw, "No matching SFP+ module found\n");
++ return IXGBE_ERR_SFP_NOT_SUPPORTED;
++ }
++
++ return 0;
++}
++
++/**
+ * ixgbe_check_phy_link_tnx - Determine link and speed status
+ * @hw: pointer to hardware structure
+ *
+--- a/drivers/net/ixgbe/ixgbe_phy.h
++++ b/drivers/net/ixgbe/ixgbe_phy.h
+@@ -63,6 +63,18 @@
+ #define IXGBE_SFF_VENDOR_OUI_FTL 0x00906500
+ #define IXGBE_SFF_VENDOR_OUI_AVAGO 0x00176A00
+
++/* I2C SDA and SCL timing parameters for standard mode */
++#define IXGBE_I2C_T_HD_STA 4
++#define IXGBE_I2C_T_LOW 5
++#define IXGBE_I2C_T_HIGH 4
++#define IXGBE_I2C_T_SU_STA 5
++#define IXGBE_I2C_T_HD_DATA 5
++#define IXGBE_I2C_T_SU_DATA 1
++#define IXGBE_I2C_T_RISE 1
++#define IXGBE_I2C_T_FALL 1
++#define IXGBE_I2C_T_SU_STO 4
++#define IXGBE_I2C_T_BUF 5
++
+
+ s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw);
+ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
+@@ -84,4 +96,10 @@ s32 ixgbe_check_phy_link_tnx(struct ixgb
+ s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
+ u16 *firmware_version);
+
++/* PHY specific */
++s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw);
++s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw);
++s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
++ u16 *list_offset,
++ u16 *data_offset);
+ #endif /* _IXGBE_PHY_H_ */
+--- a/drivers/net/ixgbe/ixgbe_type.h
++++ b/drivers/net/ixgbe/ixgbe_type.h
+@@ -36,9 +36,12 @@
+ /* Device IDs */
+ #define IXGBE_DEV_ID_82598AF_DUAL_PORT 0x10C6
+ #define IXGBE_DEV_ID_82598AF_SINGLE_PORT 0x10C7
++#define IXGBE_DEV_ID_82598EB_SFP_LOM 0x10DB
+ #define IXGBE_DEV_ID_82598AT 0x10C8
+ #define IXGBE_DEV_ID_82598EB_CX4 0x10DD
+ #define IXGBE_DEV_ID_82598_CX4_DUAL_PORT 0x10EC
++#define IXGBE_DEV_ID_82598_DA_DUAL_PORT 0x10F1
++#define IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM 0x10E1
+ #define IXGBE_DEV_ID_82598EB_XF_LR 0x10F4
+
+ /* General Registers */
+@@ -453,6 +456,7 @@
+ #define IXGBE_MDIO_PHY_XS_DEV_TYPE 0x4
+ #define IXGBE_MDIO_AUTO_NEG_DEV_TYPE 0x7
+ #define IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE 0x1E /* Device 30 */
++#define IXGBE_TWINAX_DEV 1
+
+ #define IXGBE_MDIO_COMMAND_TIMEOUT 100 /* PHY Timeout for 1 GB mode */
+
+@@ -492,9 +496,21 @@
+ #define TN1010_PHY_ID 0x00A19410
+ #define TNX_FW_REV 0xB
+ #define QT2022_PHY_ID 0x0043A400
++#define ATH_PHY_ID 0x03429050
+
+ /* PHY Types */
+ #define IXGBE_M88E1145_E_PHY_ID 0x01410CD0
++/* Special PHY Init Routine */
++#define IXGBE_PHY_INIT_OFFSET_NL 0x002B
++#define IXGBE_PHY_INIT_END_NL 0xFFFF
++#define IXGBE_CONTROL_MASK_NL 0xF000
++#define IXGBE_DATA_MASK_NL 0x0FFF
++#define IXGBE_CONTROL_SHIFT_NL 12
++#define IXGBE_DELAY_NL 0
++#define IXGBE_DATA_NL 1
++#define IXGBE_CONTROL_NL 0x000F
++#define IXGBE_CONTROL_EOL_NL 0x0FFF
++#define IXGBE_CONTROL_SOL_NL 0x0000
+
+ /* General purpose Interrupt Enable */
+ #define IXGBE_SDP0_GPIEN 0x00000001 /* SDP0 */
+@@ -1208,6 +1224,7 @@ enum ixgbe_phy_type {
+ ixgbe_phy_tn,
+ ixgbe_phy_qt,
+ ixgbe_phy_xaui,
++ ixgbe_phy_nl,
+ ixgbe_phy_tw_tyco,
+ ixgbe_phy_tw_unknown,
+ ixgbe_phy_sfp_avago,
+@@ -1229,6 +1246,7 @@ enum ixgbe_sfp_type {
+ ixgbe_sfp_type_da_cu = 0,
+ ixgbe_sfp_type_sr = 1,
+ ixgbe_sfp_type_lr = 2,
++ ixgbe_sfp_type_not_present = 0xFFFE,
+ ixgbe_sfp_type_unknown = 0xFFFF
+ };
+
+@@ -1492,6 +1510,7 @@ struct ixgbe_info {
+ #define IXGBE_ERR_PHY_ADDR_INVALID -17
+ #define IXGBE_ERR_I2C -18
+ #define IXGBE_ERR_SFP_NOT_SUPPORTED -19
++#define IXGBE_ERR_SFP_NOT_PRESENT -20
+ #define IXGBE_NOT_IMPLEMENTED 0x7FFFFFFF
+
+ #endif /* _IXGBE_TYPE_H_ */