Add support for 82576 fiber adapters based on Intel code in FreeBSD.
authorjsg <jsg@openbsd.org>
Tue, 3 Aug 2010 16:39:33 +0000 (16:39 +0000)
committerjsg <jsg@openbsd.org>
Tue, 3 Aug 2010 16:39:33 +0000 (16:39 +0000)
Thanks to Frédéric URBAN for setting up a test network to develop this on.
Tested by various people on copper adapters and fiber support
also tested by mickey.

ok deraadt@

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

index bc0287f..0c5d67d 100644 (file)
@@ -31,7 +31,7 @@
 
 *******************************************************************************/
 
-/* $OpenBSD: if_em_hw.c,v 1.55 2010/07/13 21:55:52 jsg Exp $ */
+/* $OpenBSD: if_em_hw.c,v 1.56 2010/08/03 16:39:33 jsg Exp $ */
 /*
  * if_em_hw.c Shared functions for accessing and configuring the MAC
  */
@@ -176,7 +176,9 @@ int32_t             em_access_phy_debug_regs_hv(struct em_hw *, uint32_t,
 int32_t                em_access_phy_reg_hv(struct em_hw *, uint32_t, uint16_t *,
                    boolean_t);
 int32_t                em_oem_bits_config_pchlan(struct em_hw *, boolean_t);
-
+void           em_power_up_serdes_link_82575(struct em_hw *);
+int32_t                em_get_pcs_speed_and_duplex_82575(struct em_hw *, uint16_t *,
+    uint16_t *);
 
 /* IGP cable length table */
 static const uint16_t 
@@ -590,13 +592,35 @@ em_set_mac_type(struct em_hw *hw)
 void
 em_set_media_type(struct em_hw *hw)
 {
-       uint32_t status;
+       uint32_t status, ctrl_ext;
        DEBUGFUNC("em_set_media_type");
 
        if (hw->mac_type != em_82543) {
                /* tbi_compatibility is only valid on 82543 */
                hw->tbi_compatibility_en = FALSE;
        }
+
+       if (hw->mac_type == em_82575) {
+               hw->media_type = em_media_type_copper;
+       
+               ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+               switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
+               case E1000_CTRL_EXT_LINK_MODE_SGMII:
+                       ctrl_ext |= E1000_CTRL_I2C_ENA;
+                       break;
+               case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+               case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
+                       hw->media_type = em_media_type_internal_serdes;
+                       ctrl_ext |= E1000_CTRL_I2C_ENA;
+                       break;
+               default:
+                       ctrl_ext &= ~E1000_CTRL_I2C_ENA;
+                       break;
+               }
+               E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+               return;
+       }
+
        switch (hw->device_id) {
        case E1000_DEV_ID_82545GM_SERDES:
        case E1000_DEV_ID_82546GB_SERDES:
@@ -1308,7 +1332,8 @@ em_adjust_serdes_amplitude(struct em_hw *hw)
        int32_t  ret_val;
        DEBUGFUNC("em_adjust_serdes_amplitude");
 
-       if (hw->media_type != em_media_type_internal_serdes)
+       if (hw->media_type != em_media_type_internal_serdes ||
+           hw->mac_type == em_82575)
                return E1000_SUCCESS;
 
        switch (hw->mac_type) {
@@ -1512,6 +1537,26 @@ em_setup_link(struct em_hw *hw)
        return ret_val;
 }
 
+void
+em_power_up_serdes_link_82575(struct em_hw *hw)
+{
+       uint32_t reg;
+
+       /* Enable PCS to turn on link */
+       reg = E1000_READ_REG(hw, PCS_CFG0);
+       reg |= E1000_PCS_CFG_PCS_EN;
+       E1000_WRITE_REG(hw, PCS_CFG0, reg);
+
+       /* Power up the laser */
+       reg = E1000_READ_REG(hw, CTRL_EXT);
+       reg &= ~E1000_CTRL_EXT_SDP3_DATA;
+       E1000_WRITE_REG(hw, CTRL_EXT, reg);
+
+       /* flush the write to verify completion */
+       E1000_WRITE_FLUSH(hw);
+       delay(5);
+}
+
 /******************************************************************************
  * Sets up link for a fiber based or serdes based adapter
  *
@@ -1524,7 +1569,7 @@ em_setup_link(struct em_hw *hw)
 static int32_t
 em_setup_fiber_serdes_link(struct em_hw *hw)
 {
-       uint32_t ctrl;
+       uint32_t ctrl, reg;
        uint32_t status;
        uint32_t txcw = 0;
        uint32_t i;
@@ -1538,8 +1583,13 @@ em_setup_fiber_serdes_link(struct em_hw *hw)
         * Therefore, we ensure loopback mode is disabled during
         * initialization.
         */
-       if (hw->mac_type == em_82571 || hw->mac_type == em_82572)
+       if (hw->mac_type == em_82571 || hw->mac_type == em_82572 ||
+           hw->mac_type == em_82575)
                E1000_WRITE_REG(hw, SCTL, E1000_DISABLE_SERDES_LOOPBACK);
+
+       if (hw->mac_type == em_82575)
+               em_power_up_serdes_link_82575(hw);
+               
        /*
         * On adapters with a MAC newer than 82544, SWDP 1 will be set when
         * the optics detect a signal. On older adapters, it will be cleared
@@ -1558,6 +1608,16 @@ em_setup_fiber_serdes_link(struct em_hw *hw)
        /* Take the link out of reset */
        ctrl &= ~(E1000_CTRL_LRST);
 
+       if (hw->mac_type == em_82575) {
+               /* set both sw defined pins on 82575/82576*/
+               ctrl |= E1000_CTRL_SWDPIN0 | E1000_CTRL_SWDPIN1;
+
+               /* Set switch control to serdes energy detect */
+               reg = E1000_READ_REG(hw, CONNSW);
+               reg |= E1000_CONNSW_ENRGSRC;
+               E1000_WRITE_REG(hw, CONNSW, reg);
+       }
+
        /* Adjust VCO speed to improve BER performance */
        ret_val = em_set_vco_speed(hw);
        if (ret_val)
@@ -3380,6 +3440,16 @@ em_check_for_link(struct em_hw *hw)
        int32_t  ret_val;
        uint16_t phy_data;
        DEBUGFUNC("em_check_for_link");
+       uint16_t speed, duplex;
+
+       if (hw->mac_type == em_82575 &&
+           hw->media_type != em_media_type_copper) {
+               ret_val = em_get_pcs_speed_and_duplex_82575(hw, &speed,
+                   &duplex);
+               hw->get_link_status = hw->serdes_link_down;
+
+               return (ret_val);
+       }
 
        ctrl = E1000_READ_REG(hw, CTRL);
        status = E1000_READ_REG(hw, STATUS);
@@ -3638,6 +3708,52 @@ em_check_for_link(struct em_hw *hw)
        return E1000_SUCCESS;
 }
 
+int32_t
+em_get_pcs_speed_and_duplex_82575(struct em_hw *hw, uint16_t *speed,
+    uint16_t *duplex)
+{
+       uint32_t pcs;
+
+       hw->serdes_link_down = TRUE;
+       *speed = 0;
+       *duplex = 0;
+
+       /*
+        * Read the PCS Status register for link state. For non-copper mode,
+        * the status register is not accurate. The PCS status register is
+        * used instead.
+        */
+       pcs = E1000_READ_REG(hw, PCS_LSTAT);
+
+       /*
+        * The link up bit determines when link is up on autoneg. The sync ok
+        * gets set once both sides sync up and agree upon link. Stable link
+        * can be determined by checking for both link up and link sync ok
+        */
+       if ((pcs & E1000_PCS_LSTS_LINK_OK) && (pcs & E1000_PCS_LSTS_SYNK_OK)) {
+               hw->serdes_link_down = FALSE;
+       
+               /* Detect and store PCS speed */
+               if (pcs & E1000_PCS_LSTS_SPEED_1000) {
+                       *speed = SPEED_1000;
+               } else if (pcs & E1000_PCS_LSTS_SPEED_100) {
+                       *speed = SPEED_100;
+               } else {
+                       *speed = SPEED_10;
+               }
+
+               /* Detect and store PCS duplex */
+               if (pcs & E1000_PCS_LSTS_DUPLEX_FULL) {
+                       *duplex = FULL_DUPLEX;
+               } else {
+                       *duplex = HALF_DUPLEX;
+               }
+       }
+
+       return (0);
+}
+
+
 /******************************************************************************
  * Detects the current speed and duplex settings of the hardware.
  *
@@ -3653,6 +3769,9 @@ em_get_speed_and_duplex(struct em_hw *hw, uint16_t *speed, uint16_t *duplex)
        uint16_t phy_data;
        DEBUGFUNC("em_get_speed_and_duplex");
 
+       if (hw->mac_type == em_82575 && hw->media_type != em_media_type_copper)
+               return em_get_pcs_speed_and_duplex_82575(hw, speed, duplex);
+
        if (hw->mac_type >= em_82543) {
                status = E1000_READ_REG(hw, STATUS);
                if (status & E1000_STATUS_SPEED_1000) {
@@ -4963,6 +5082,14 @@ em_detect_gig_phy(struct em_hw *hw)
                hw->phy_type = em_phy_undefined;
                return E1000_SUCCESS;
        }
+
+       if ((hw->media_type == em_media_type_internal_serdes ||
+           hw->media_type == em_media_type_fiber) &&
+           hw->mac_type == em_82575) {
+               hw->phy_type = em_phy_undefined;
+               return E1000_SUCCESS;
+       }
+
        /*
         * Up to 82543 (incl), we need reset the phy, or it might not get
         * detected
index f78313a..1eef8ed 100644 (file)
@@ -31,7 +31,7 @@
 
 *******************************************************************************/
 
-/* $OpenBSD: if_em_hw.h,v 1.44 2010/07/02 21:41:59 jsg Exp $ */
+/* $OpenBSD: if_em_hw.h,v 1.45 2010/08/03 16:39:34 jsg Exp $ */
 /* $FreeBSD: if_em_hw.h,v 1.15 2005/05/26 23:32:02 tackerman Exp $ */
 
 /* if_em_hw.h
@@ -941,6 +941,7 @@ struct em_ffvt_entry {
 #define E1000_FCAL     0x00028  /* Flow Control Address Low - RW */
 #define E1000_FCAH     0x0002C  /* Flow Control Address High -RW */
 #define E1000_FCT      0x00030  /* Flow Control Type - RW */
+#define E1000_CONNSW   0x00034  /* Copper/Fiber switch control - RW */
 #define E1000_VET      0x00038  /* VLAN Ether Type - RW */
 #define E1000_ICR      0x000C0  /* Interrupt Cause Read - R/clr */
 #define E1000_ITR      0x000C4  /* Interrupt Throttling Rate - RW */
@@ -1092,6 +1093,9 @@ struct em_ffvt_entry {
 #define E1000_ICTXQMTC 0x0411C  /* Interrupt Cause Tx Queue Minimum Threshold Count */
 #define E1000_ICRXDMTC 0x04120  /* Interrupt Cause Rx Descriptor Minimum Threshold Count */
 #define E1000_ICRXOC   0x04124  /* Interrupt Cause Receiver Overrun Count */
+#define E1000_PCS_CFG0 0x04200  /* PCS Configuration 0 - RW */
+#define E1000_PCS_LCTL 0x04208  /* PCS Link Control - RW */
+#define E1000_PCS_LSTAT 0x0420C /* PCS Link Status - RO */
 #define E1000_RXCSUM   0x05000  /* RX Checksum Control - RW */
 #define E1000_RFCTL    0x05008  /* Receive Filter Control*/
 #define E1000_MTA      0x05200  /* Multicast Table Array - RW Array */
@@ -1155,6 +1159,7 @@ struct em_ffvt_entry {
 #define E1000_82542_FCAL     E1000_FCAL
 #define E1000_82542_FCAH     E1000_FCAH
 #define E1000_82542_FCT      E1000_FCT
+#define E1000_82542_CONNSW   E1000_CONNSW
 #define E1000_82542_VET      E1000_VET
 #define E1000_82542_RA       0x00040
 #define E1000_82542_ICR      E1000_ICR
@@ -1347,6 +1352,9 @@ struct em_ffvt_entry {
 #define E1000_82542_ICRXDMTC    E1000_ICRXDMTC
 #define E1000_82542_ICRXOC      E1000_ICRXOC
 #define E1000_82542_HICR        E1000_HICR
+#define E1000_82542_PCS_CFG0   E1000_PCS_CFG0
+#define E1000_82542_PCS_LCTL   E1000_PCS_LCTL
+#define E1000_82542_PCS_LSTAT  E1000_PCS_LSTAT
 
 #define E1000_82542_CPUVEC      E1000_CPUVEC
 #define E1000_82542_MRQC        E1000_MRQC
@@ -1566,6 +1574,16 @@ struct em_hw {
 #define E1000_CTRL_VME      0x40000000  /* IEEE VLAN mode enable */
 #define E1000_CTRL_PHY_RST  0x80000000  /* PHY Reset */
 #define E1000_CTRL_SW2FW_INT 0x02000000  /* Initiate an interrupt to manageability engine */
+#define E1000_CTRL_I2C_ENA  0x02000000  /* I2C enable */
+
+#define E1000_CONNSW_ENRGSRC   0x4
+#define E1000_PCS_CFG_PCS_EN   8
+
+#define E1000_PCS_LSTS_LINK_OK         0x01
+#define E1000_PCS_LSTS_SPEED_100       0x02
+#define E1000_PCS_LSTS_SPEED_1000      0x04
+#define E1000_PCS_LSTS_DUPLEX_FULL     0x08
+#define E1000_PCS_LSTS_SYNK_OK         0x10
 
 /* Device Status */
 #define E1000_STATUS_FD         0x00000001      /* Full duplex.0=half,1=full */
@@ -1669,6 +1687,7 @@ struct em_hw {
 #define E1000_CTRL_EXT_PHY_INT   E1000_CTRL_EXT_SDP5_DATA
 #define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */
 #define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */
+#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Defineable Pin 3 */
 #define E1000_CTRL_EXT_SDP4_DIR  0x00000100 /* Direction of SDP4 0=in 1=out */
 #define E1000_CTRL_EXT_SDP5_DIR  0x00000200 /* Direction of SDP5 0=in 1=out */
 #define E1000_CTRL_EXT_SDP6_DIR  0x00000400 /* Direction of SDP6 0=in 1=out */
@@ -1682,7 +1701,8 @@ struct em_hw {
 #define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000
 #define E1000_CTRL_EXT_LINK_MODE_TBI  0x00C00000
 #define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000
-#define E1000_CTRL_EXT_LINK_MODE_SERDES  0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES  0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX  0x00400000
 #define E1000_CTRL_EXT_LINK_MODE_SGMII   0x00800000
 #define E1000_CTRL_EXT_WR_WMARK_MASK  0x03000000
 #define E1000_CTRL_EXT_WR_WMARK_256   0x00000000