Implement support for selecting SGMII or SerDes mode depending on the
authorpatrick <patrick@openbsd.org>
Tue, 14 Dec 2021 10:48:10 +0000 (10:48 +0000)
committerpatrick <patrick@openbsd.org>
Tue, 14 Dec 2021 10:48:10 +0000 (10:48 +0000)
plugged-in SFP transceiver and for reading out transceiver information
via ifconfig(8).  To read from the SFP, we need to let the card issue
I2C transfers.  Additionally we need I2C to read/write to the PHY when
MDIO is not available.  Depending on the SFP's supported media types
we can decide which mode to use.

This fixes hardware-initialization and link-up problems with some em(4)
Fiber NIC and SFP combinations.

Tested by dlg@ and been in snaps for quite a while
ok dlg@ jmatthew@

sys/dev/pci/if_em.c
sys/dev/pci/if_em.h
sys/dev/pci/if_em_hw.c
sys/dev/pci/if_em_hw.h

index 759da7a..18050b9 100644 (file)
@@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
 
 ***************************************************************************/
 
-/* $OpenBSD: if_em.c,v 1.358 2021/01/24 10:21:43 jsg Exp $ */
+/* $OpenBSD: if_em.c,v 1.359 2021/12/14 10:48:10 patrick Exp $ */
 /* $FreeBSD: if_em.c,v 1.46 2004/09/29 18:28:28 mlaier Exp $ */
 
 #include <dev/pci/if_em.h>
@@ -296,6 +296,7 @@ u_int32_t em_fill_descriptors(u_int64_t address, u_int32_t length,
 void em_flush_tx_ring(struct em_queue *);
 void em_flush_rx_ring(struct em_queue *);
 void em_flush_desc_rings(struct em_softc *);
+int em_get_sffpage(struct em_softc *, struct if_sffpage *);
 
 #ifndef SMALL_KERNEL
 /* MSIX/Multiqueue functions */
@@ -409,6 +410,8 @@ em_attach(struct device *parent, struct device *self, void *aux)
        timeout_set(&sc->timer_handle, em_local_timer, sc);
        timeout_set(&sc->tx_fifo_timer_handle, em_82547_move_tail, sc);
 
+       rw_init(&sc->sfflock, "emsff");
+
        /* Determine hardware revision */
        em_identify_hardware(sc);
 
@@ -764,6 +767,15 @@ em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
                    NULL, EM_MCLBYTES, &sc->queues->rx.sc_rx_ring);
                break;
 
+       case SIOCGIFSFFPAGE:
+               error = rw_enter(&sc->sfflock, RW_WRITE|RW_INTR);
+               if (error != 0)
+                       break;
+
+               error = em_get_sffpage(sc, (struct if_sffpage *)data);
+               rw_exit(&sc->sfflock);
+               break;
+
        default:
                error = ether_ioctl(ifp, &sc->sc_ac, command, data);
        }
@@ -4028,3 +4040,31 @@ em_allocate_desc_rings(struct em_softc *sc)
 
        return (0);
 }
+
+int
+em_get_sffpage(struct em_softc *sc, struct if_sffpage *sff)
+{
+       struct em_hw *hw = &sc->hw;
+       size_t i;
+       int off;
+
+       if (hw->mac_type != em_82575 && hw->mac_type != em_82580 &&
+           hw->mac_type != em_82576 &&
+           hw->mac_type != em_i210 && hw->mac_type != em_i350)
+               return (ENODEV);
+
+       if (sff->sff_addr == IFSFF_ADDR_EEPROM)
+               off = E1000_I2CCMD_SFP_DATA_ADDR(0);
+       else if (sff->sff_addr == IFSFF_ADDR_DDM)
+               off = E1000_I2CCMD_SFP_DIAG_ADDR(0);
+       else
+               return (EIO);
+
+       for (i = 0; i < sizeof(sff->sff_data); i++) {
+               if (em_read_sfp_data_byte(hw, off + i,
+                   &sff->sff_data[i]) != E1000_SUCCESS)
+                       return (EIO);
+       }
+
+       return (0);
+}
index f9aa599..dc8a985 100644 (file)
@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
 ***************************************************************************/
 
 /* $FreeBSD: if_em.h,v 1.26 2004/09/01 23:22:41 pdeuskar Exp $ */
-/* $OpenBSD: if_em.h,v 1.78 2020/07/13 10:35:55 dlg Exp $ */
+/* $OpenBSD: if_em.h,v 1.79 2021/12/14 10:48:10 patrick Exp $ */
 
 #ifndef _EM_H_DEFINED_
 #define _EM_H_DEFINED_
@@ -405,6 +405,7 @@ struct em_softc {
        u_int32_t       tx_abs_int_delay;
        u_int32_t       rx_int_delay;
        u_int32_t       rx_abs_int_delay;
+       struct rwlock   sfflock;
 
        u_int                    sc_tx_slots;
        u_int                    sc_rx_slots;
index 34202c5..4ee9857 100644 (file)
@@ -31,7 +31,7 @@
 
 *******************************************************************************/
 
-/* $OpenBSD: if_em_hw.c,v 1.110 2021/01/24 10:21:43 jsg Exp $ */
+/* $OpenBSD: if_em_hw.c,v 1.111 2021/12/14 10:48:10 patrick Exp $ */
 /*
  * if_em_hw.c Shared functions for accessing and configuring the MAC
  */
@@ -724,6 +724,75 @@ em_set_mac_type(struct em_hw *hw)
        return E1000_SUCCESS;
 }
 
+/**
+ *  em_set_sfp_media_type_82575 - derives SFP module media type.
+ *  @hw: pointer to the HW structure
+ *
+ *  The media type is chosen based on SFP module.
+ *  compatibility flags retrieved from SFP ID EEPROM.
+ **/
+STATIC int32_t em_set_sfp_media_type_82575(struct em_hw *hw)
+{
+       struct sfp_e1000_flags eth_flags;
+       int32_t ret_val = E1000_ERR_CONFIG;
+       uint32_t ctrl_ext = 0;
+       uint8_t tranceiver_type = 0;
+       int32_t timeout = 3;
+
+       /* Turn I2C interface ON and power on sfp cage */
+       ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+       ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
+       E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA);
+
+       E1000_WRITE_FLUSH(hw);
+
+       /* Read SFP module data */
+       while (timeout) {
+               ret_val = em_read_sfp_data_byte(hw,
+                       E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET),
+                       &tranceiver_type);
+               if (ret_val == E1000_SUCCESS)
+                       break;
+               msec_delay(100);
+               timeout--;
+       }
+       if (ret_val != E1000_SUCCESS)
+               goto out;
+
+       ret_val = em_read_sfp_data_byte(hw,
+                       E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET),
+                       (uint8_t *)&eth_flags);
+       if (ret_val != E1000_SUCCESS)
+               goto out;
+
+       /* Check if there is some SFP module plugged and powered */
+       if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) ||
+           (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) {
+               if (eth_flags.e1000_base_lx || eth_flags.e1000_base_sx) {
+                       hw->media_type = em_media_type_internal_serdes;
+               } else if (eth_flags.e100_base_fx || eth_flags.e100_base_lx) {
+                       hw->media_type = em_media_type_internal_serdes;
+                       hw->sgmii_active = TRUE;
+               } else if (eth_flags.e1000_base_t) {
+                       hw->media_type = em_media_type_copper;
+                       hw->sgmii_active = TRUE;
+               } else {
+                       DEBUGOUT("PHY module has not been recognized\n");
+                       ret_val = E1000_ERR_CONFIG;
+                       goto out;
+               }
+       } else {
+               ret_val = E1000_ERR_CONFIG;
+               goto out;
+       }
+       ret_val = E1000_SUCCESS;
+out:
+       /* Restore I2C interface setting */
+       E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+       return ret_val;
+}
+
+
 /*****************************************************************************
  * Set media type and TBI compatibility.
  *
@@ -732,7 +801,7 @@ em_set_mac_type(struct em_hw *hw)
 void
 em_set_media_type(struct em_hw *hw)
 {
-       uint32_t status, ctrl_ext;
+       uint32_t status, ctrl_ext, mdic;
        DEBUGFUNC("em_set_media_type");
 
        if (hw->mac_type != em_82543) {
@@ -744,16 +813,39 @@ em_set_media_type(struct em_hw *hw)
            hw->mac_type == em_82576 ||
            hw->mac_type == em_i210 || hw->mac_type == em_i350) {
                hw->media_type = em_media_type_copper;
-       
+               hw->sgmii_active = FALSE;
+
                ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
                switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
-               case E1000_CTRL_EXT_LINK_MODE_SGMII:
+               case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+                       hw->media_type = em_media_type_internal_serdes;
                        ctrl_ext |= E1000_CTRL_I2C_ENA;
                        break;
-               case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+               case E1000_CTRL_EXT_LINK_MODE_SGMII:
+                       mdic = EM_READ_REG(hw, E1000_MDICNFG);
+                       ctrl_ext |= E1000_CTRL_I2C_ENA;
+                       if (mdic & E1000_MDICNFG_EXT_MDIO) {
+                               hw->media_type = em_media_type_copper;
+                               hw->sgmii_active = TRUE;
+                               break;
+                       }
+                       /* FALLTHROUGH */
                case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
-                       hw->media_type = em_media_type_internal_serdes;
                        ctrl_ext |= E1000_CTRL_I2C_ENA;
+                       if (em_set_sfp_media_type_82575(hw) != 0) {
+                               hw->media_type = em_media_type_internal_serdes;
+                               if ((ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) ==
+                                   E1000_CTRL_EXT_LINK_MODE_SGMII) {
+                                       hw->media_type = em_media_type_copper;
+                                       hw->sgmii_active = TRUE;
+                               }
+                       }
+
+                       ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK;
+                       if (hw->sgmii_active)
+                               ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII;
+                       else
+                               ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES;
                        break;
                default:
                        ctrl_ext &= ~E1000_CTRL_I2C_ENA;
@@ -1976,6 +2068,10 @@ em_power_up_serdes_link_82575(struct em_hw *hw)
 {
        uint32_t reg;
 
+       if (hw->media_type != em_media_type_internal_serdes &&
+           hw->sgmii_active == FALSE)
+               return;
+
        /* Enable PCS to turn on link */
        reg = E1000_READ_REG(hw, PCS_CFG0);
        reg |= E1000_PCS_CFG_PCS_EN;
@@ -2010,6 +2106,11 @@ em_setup_fiber_serdes_link(struct em_hw *hw)
        uint32_t signal = 0;
        int32_t  ret_val;
        DEBUGFUNC("em_setup_fiber_serdes_link");
+
+       if (hw->media_type != em_media_type_internal_serdes &&
+           hw->sgmii_active == FALSE)
+               return -E1000_ERR_CONFIG;
+
        /*
         * On 82571 and 82572 Fiber connections, SerDes loopback mode
         * persists until explicitly turned off or a power cycle is
@@ -5066,6 +5167,14 @@ em_read_phy_reg_ex(struct em_hw *hw, uint32_t reg_addr, uint16_t *phy_data)
        uint32_t mdic = 0;
        DEBUGFUNC("em_read_phy_reg_ex");
 
+       /* SGMII active is only set on some specific chips */
+       if (hw->sgmii_active && !em_sgmii_uses_mdio_82575(hw)) {
+               if (reg_addr > E1000_MAX_SGMII_PHY_REG_ADDR) {
+                       DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
+                       return -E1000_ERR_PARAM;
+               }
+               return em_read_phy_reg_i2c(hw, reg_addr, phy_data);
+       }
        if (reg_addr > MAX_PHY_REG_ADDRESS) {
                DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
                return -E1000_ERR_PARAM;
@@ -5227,6 +5336,14 @@ em_write_phy_reg_ex(struct em_hw *hw, uint32_t reg_addr, uint16_t phy_data)
        uint32_t mdic = 0;
        DEBUGFUNC("em_write_phy_reg_ex");
 
+       /* SGMII active is only set on some specific chips */
+       if (hw->sgmii_active && !em_sgmii_uses_mdio_82575(hw)) {
+               if (reg_addr > E1000_MAX_SGMII_PHY_REG_ADDR) {
+                       DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
+                       return -E1000_ERR_PARAM;
+               }
+               return em_write_phy_reg_i2c(hw, reg_addr, phy_data);
+       }
        if (reg_addr > MAX_PHY_REG_ADDRESS) {
                DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
                return -E1000_ERR_PARAM;
@@ -5337,6 +5454,195 @@ em_write_kmrn_reg(struct em_hw *hw, uint32_t reg_addr, uint16_t data)
        return E1000_SUCCESS;
 }
 
+/**
+ *  em_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO
+ *  @hw: pointer to the HW structure
+ *
+ *  Called to determine if the I2C pins are being used for I2C or as an
+ *  external MDIO interface since the two options are mutually exclusive.
+ **/
+int em_sgmii_uses_mdio_82575(struct em_hw *hw)
+{
+       uint32_t reg = 0;
+       int ext_mdio = 0;
+
+       DEBUGFUNC("em_sgmii_uses_mdio_82575");
+
+       switch (hw->mac_type) {
+       case em_82575:
+       case em_82576:
+               reg = E1000_READ_REG(hw, MDIC);
+               ext_mdio = !!(reg & E1000_MDIC_DEST);
+               break;
+       case em_82580:
+       case em_i350:
+       case em_i210:
+               reg = E1000_READ_REG(hw, MDICNFG);
+               ext_mdio = !!(reg & E1000_MDICNFG_EXT_MDIO);
+               break;
+       default:
+               break;
+       }
+       return ext_mdio;
+}
+
+/**
+ *  em_read_phy_reg_i2c - Read PHY register using i2c
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to be read
+ *  @data: pointer to the read data
+ *
+ *  Reads the PHY register at offset using the i2c interface and stores the
+ *  retrieved information in data.
+ **/
+int32_t em_read_phy_reg_i2c(struct em_hw *hw, uint32_t offset, uint16_t *data)
+{
+       uint32_t i, i2ccmd = 0;
+
+       DEBUGFUNC("em_read_phy_reg_i2c");
+
+       /* Set up Op-code, Phy Address, and register address in the I2CCMD
+        * register.  The MAC will take care of interfacing with the
+        * PHY to retrieve the desired data.
+        */
+       i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+                 (hw->phy_addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+                 (E1000_I2CCMD_OPCODE_READ));
+
+       E1000_WRITE_REG(hw, I2CCMD, i2ccmd);
+
+       /* Poll the ready bit to see if the I2C read completed */
+       for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+               usec_delay(50);
+               i2ccmd = E1000_READ_REG(hw, I2CCMD);
+               if (i2ccmd & E1000_I2CCMD_READY)
+                       break;
+       }
+       if (!(i2ccmd & E1000_I2CCMD_READY)) {
+               DEBUGOUT("I2CCMD Read did not complete\n");
+               return -E1000_ERR_PHY;
+       }
+       if (i2ccmd & E1000_I2CCMD_ERROR) {
+               DEBUGOUT("I2CCMD Error bit set\n");
+               return -E1000_ERR_PHY;
+       }
+
+       /* Need to byte-swap the 16-bit value. */
+       *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00);
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  em_write_phy_reg_i2c - Write PHY register using i2c
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to write to
+ *  @data: data to write at register offset
+ *
+ *  Writes the data to PHY register at the offset using the i2c interface.
+ **/
+int32_t em_write_phy_reg_i2c(struct em_hw *hw, uint32_t offset, uint16_t data)
+{
+       uint32_t i, i2ccmd = 0;
+       uint16_t phy_data_swapped;
+
+       DEBUGFUNC("em_write_phy_reg_i2c");
+
+       /* Prevent overwritting SFP I2C EEPROM which is at A0 address.*/
+       if ((hw->phy_addr == 0) || (hw->phy_addr > 7)) {
+               DEBUGOUT1("PHY I2C Address %d is out of range.\n",
+                         hw->phy_addr);
+               return -E1000_ERR_CONFIG;
+       }
+
+       /* Swap the data bytes for the I2C interface */
+       phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
+
+       /* Set up Op-code, Phy Address, and register address in the I2CCMD
+        * register.  The MAC will take care of interfacing with the
+        * PHY to retrieve the desired data.
+        */
+       i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+                 (hw->phy_addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+                 E1000_I2CCMD_OPCODE_WRITE |
+                 phy_data_swapped);
+
+       E1000_WRITE_REG(hw, I2CCMD, i2ccmd);
+
+       /* Poll the ready bit to see if the I2C read completed */
+       for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+               usec_delay(50);
+               i2ccmd = E1000_READ_REG(hw, I2CCMD);
+               if (i2ccmd & E1000_I2CCMD_READY)
+                       break;
+       }
+       if (!(i2ccmd & E1000_I2CCMD_READY)) {
+               DEBUGOUT("I2CCMD Write did not complete\n");
+               return -E1000_ERR_PHY;
+       }
+       if (i2ccmd & E1000_I2CCMD_ERROR) {
+               DEBUGOUT("I2CCMD Error bit set\n");
+               return -E1000_ERR_PHY;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  em_read_sfp_data_byte - Reads SFP module data.
+ *  @hw: pointer to the HW structure
+ *  @offset: byte location offset to be read
+ *  @data: read data buffer pointer
+ *
+ *  Reads one byte from SFP module data stored
+ *  in SFP resided EEPROM memory or SFP diagnostic area.
+ *  Function should be called with
+ *  E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
+ *  E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
+ *  access
+ **/
+int32_t em_read_sfp_data_byte(struct em_hw *hw, uint16_t offset, uint8_t *data)
+{
+       uint32_t i = 0;
+       uint32_t i2ccmd = 0;
+       uint32_t data_local = 0;
+
+       DEBUGFUNC("em_read_sfp_data_byte");
+
+       if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
+               DEBUGOUT("I2CCMD command address exceeds upper limit\n");
+               return -E1000_ERR_PHY;
+       }
+
+       /* Set up Op-code, EEPROM Address,in the I2CCMD
+        * register. The MAC will take care of interfacing with the
+        * EEPROM to retrieve the desired data.
+        */
+       i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+                 E1000_I2CCMD_OPCODE_READ);
+
+       E1000_WRITE_REG(hw, I2CCMD, i2ccmd);
+
+       /* Poll the ready bit to see if the I2C read completed */
+       for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+               usec_delay(50);
+               data_local = E1000_READ_REG(hw, I2CCMD);
+               if (data_local & E1000_I2CCMD_READY)
+                       break;
+       }
+       if (!(data_local & E1000_I2CCMD_READY)) {
+               DEBUGOUT("I2CCMD Read did not complete\n");
+               return -E1000_ERR_PHY;
+       }
+       if (data_local & E1000_I2CCMD_ERROR) {
+               DEBUGOUT("I2CCMD Error bit set\n");
+               return -E1000_ERR_PHY;
+       }
+       *data = (uint8_t) data_local & 0xFF;
+
+       return E1000_SUCCESS;
+}
+
 /******************************************************************************
  * Returns the PHY to the power-on reset state
  *
@@ -5713,16 +6019,19 @@ em_match_gig_phy(struct em_hw *hw)
                    hw->phy_id == I210_I_PHY_ID ||
                    hw->phy_id == I347AT4_E_PHY_ID ||
                    hw->phy_id == I350_I_PHY_ID ||
+                   hw->phy_id == M88E1111_I_PHY_ID ||
                    hw->phy_id == M88E1112_E_PHY_ID ||
                    hw->phy_id == M88E1543_E_PHY_ID ||
                    hw->phy_id == M88E1512_E_PHY_ID) {
                        uint32_t mdic;
 
                        mdic = EM_READ_REG(hw, E1000_MDICNFG);
-                       mdic &= E1000_MDICNFG_PHY_MASK;
-                       hw->phy_addr = mdic >> E1000_MDICNFG_PHY_SHIFT;
-                       DEBUGOUT1("MDICNFG PHY ADDR %d",
-                           mdic >> E1000_MDICNFG_PHY_SHIFT);
+                       if (mdic & E1000_MDICNFG_EXT_MDIO) {
+                               mdic &= E1000_MDICNFG_PHY_MASK;
+                               hw->phy_addr = mdic >> E1000_MDICNFG_PHY_SHIFT;
+                               DEBUGOUT1("MDICNFG PHY ADDR %d",
+                                   mdic >> E1000_MDICNFG_PHY_SHIFT);
+                       }
                        match = TRUE;
                }
                break;
@@ -5858,11 +6167,12 @@ em_detect_gig_phy(struct em_hw *hw)
                uint32_t ctrl_ext = EM_READ_REG(hw, E1000_CTRL_EXT);
                EM_WRITE_REG(hw, E1000_CTRL_EXT,
                    ctrl_ext & ~E1000_CTRL_EXT_SDP3_DATA);
-               delay(300);
+               E1000_WRITE_FLUSH(hw);
+               msec_delay(300);
        }
 
        /* Read the PHY ID Registers to identify which PHY is onboard. */
-       for (i = 1; i < 4; i++) {
+       for (i = 1; i < 8; i++) {
                /*
                 * hw->phy_addr may be modified down in the call stack,
                 * we can't use it as loop variable.
index 443889f..af176c4 100644 (file)
@@ -31,7 +31,7 @@
 
 *******************************************************************************/
 
-/* $OpenBSD: if_em_hw.h,v 1.84 2021/01/24 10:21:43 jsg Exp $ */
+/* $OpenBSD: if_em_hw.h,v 1.85 2021/12/14 10:48:10 patrick Exp $ */
 /* $FreeBSD: if_em_hw.h,v 1.15 2005/05/26 23:32:02 tackerman Exp $ */
 
 /* if_em_hw.h
@@ -361,6 +361,10 @@ int32_t em_phy_reset(struct em_hw *hw);
 int32_t em_phy_get_info(struct em_hw *hw, struct em_phy_info *phy_info);
 int32_t em_validate_mdi_setting(struct em_hw *hw);
 void em_phy_powerdown_workaround(struct em_hw *hw);
+int em_sgmii_uses_mdio_82575(struct em_hw *);
+int32_t em_read_phy_reg_i2c(struct em_hw *, uint32_t, uint16_t *);
+int32_t em_write_phy_reg_i2c(struct em_hw *, uint32_t, uint16_t);
+int32_t em_read_sfp_data_byte(struct em_hw *, uint16_t, uint8_t *);
 
 /* EEPROM Functions */
 int32_t em_init_eeprom_params(struct em_hw *hw);
@@ -1095,6 +1099,7 @@ struct em_ffvt_entry {
 #define E1000_FLSWDATA 0x01034  /* FLASH data register */
 #define E1000_FLSWCNT  0x01038  /* FLASH Access Counter */
 #define E1000_FLOP     0x0103C  /* FLASH Opcode Register */
+#define E1000_I2CCMD   0x01028  /* SFPI2C Command Register - RW */
 #define E1000_ERT      0x02008  /* Early Rx Threshold - RW */
 #define E1000_FCRTL    0x02160  /* Flow Control Receive Threshold Low - RW */
 #define E1000_FCRTH    0x02168  /* Flow Control Receive Threshold High - RW */
@@ -1492,6 +1497,7 @@ struct em_hw {
     uint16_t swfw;
     boolean_t eee_enable;
     int sw_flag;
+    boolean_t sgmii_active;
 };
 
 #define E1000_EEPROM_SWDPIN0   0x0001   /* SWDPIN 0 EEPROM Value */
@@ -1528,6 +1534,16 @@ struct em_hw {
 #define E1000_CTRL_LANPHYPC_VALUE    0x00020000 /* SW value of LANPHYPC */
 #define E1000_CTRL_EXT_FORCE_SMBUS   0x00000800 /* Force SMBus mode */
 #define E1000_CTRL_EXT_PHYPDEN       0x00100000
+#define E1000_I2CCMD_REG_ADDR_SHIFT    16
+#define E1000_I2CCMD_PHY_ADDR_SHIFT    24
+#define E1000_I2CCMD_OPCODE_READ       0x08000000
+#define E1000_I2CCMD_OPCODE_WRITE      0x00000000
+#define E1000_I2CCMD_READY             0x20000000
+#define E1000_I2CCMD_ERROR             0x80000000
+#define E1000_I2CCMD_SFP_DATA_ADDR(a)  (0x0000 + (a))
+#define E1000_I2CCMD_SFP_DIAG_ADDR(a)  (0x0100 + (a))
+#define E1000_MAX_SGMII_PHY_REG_ADDR   255
+#define E1000_I2CCMD_PHY_TIMEOUT       200
 
 #define E1000_CTRL_SWDPIN0  0x00040000  /* SWDPIN 0 value */
 #define E1000_CTRL_SWDPIN1  0x00080000  /* SWDPIN 1 value */
@@ -3790,4 +3806,28 @@ union ich8_hws_flash_regacc {
          (((offset) >> (PHY_UPPER_SHIFT - PHY_PAGE_SHIFT)) &\
                 ~MAX_PHY_REG_ADDRESS)))
 
+/* SFP modules ID memory locations */
+#define E1000_SFF_IDENTIFIER_OFFSET     0x00
+#define E1000_SFF_IDENTIFIER_SFF        0x02
+#define E1000_SFF_IDENTIFIER_SFP        0x03
+
+#define E1000_SFF_ETH_FLAGS_OFFSET      0x06
+/* Flags for SFP modules compatible with ETH up to 1Gb */
+struct sfp_e1000_flags {
+        uint8_t e1000_base_sx:1;
+        uint8_t e1000_base_lx:1;
+        uint8_t e1000_base_cx:1;
+        uint8_t e1000_base_t:1;
+        uint8_t e100_base_lx:1;
+        uint8_t e100_base_fx:1;
+        uint8_t e10_base_bx10:1;
+        uint8_t e10_base_px:1;
+};
+
+/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */
+#define E1000_SFF_VENDOR_OUI_TYCO       0x00407600
+#define E1000_SFF_VENDOR_OUI_FTL        0x00906500
+#define E1000_SFF_VENDOR_OUI_AVAGO      0x00176A00
+#define E1000_SFF_VENDOR_OUI_INTEL      0x001B2100
+
 #endif /* _EM_HW_H_ */