igc: Add setup link functionality
authorSasha Neftin <sasha.neftin@intel.com>
Thu, 11 Oct 2018 07:17:34 +0000 (10:17 +0300)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Wed, 17 Oct 2018 20:56:55 +0000 (13:56 -0700)
Add link establishment methods
Add auto negotiation methods
Add read MAC address method

Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_base.c
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_mac.c
drivers/net/ethernet/intel/igc/igc_mac.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/igc/igc_phy.c
drivers/net/ethernet/intel/igc/igc_phy.h

index 7cfbd83d25e4f68668b4dc5ab7b17640998529f3..86fa889b4ab66a1ac32c75b72882e6a94d4cba80 100644 (file)
@@ -314,6 +314,7 @@ struct igc_adapter {
        struct work_struct reset_task;
        struct work_struct watchdog_task;
        struct work_struct dma_err_task;
+       bool fc_autoneg;
 
        u8 tx_timeout_factor;
 
index 55faef987479ff96b36fecdcbb9599e93d673050..832da609d9a7d463462ae97d3143fe17153c6192 100644 (file)
@@ -177,6 +177,29 @@ static s32 igc_init_nvm_params_base(struct igc_hw *hw)
        return 0;
 }
 
+/**
+ * igc_setup_copper_link_base - Configure copper link settings
+ * @hw: pointer to the HW structure
+ *
+ * Configures the link for auto-neg or forced speed and duplex.  Then we check
+ * for link, once link is established calls to configure collision distance
+ * and flow control are called.
+ */
+static s32 igc_setup_copper_link_base(struct igc_hw *hw)
+{
+       s32  ret_val = 0;
+       u32 ctrl;
+
+       ctrl = rd32(IGC_CTRL);
+       ctrl |= IGC_CTRL_SLU;
+       ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
+       wr32(IGC_CTRL, ctrl);
+
+       ret_val = igc_setup_copper_link(hw);
+
+       return ret_val;
+}
+
 /**
  * igc_init_mac_params_base - Init MAC func ptrs.
  * @hw: pointer to the HW structure
@@ -200,6 +223,9 @@ static s32 igc_init_mac_params_base(struct igc_hw *hw)
        if (mac->type == igc_i225)
                dev_spec->clear_semaphore_once = true;
 
+       /* physical interface link setup */
+       mac->ops.setup_physical_interface = igc_setup_copper_link_base;
+
        return 0;
 }
 
@@ -242,6 +268,8 @@ static s32 igc_init_phy_params_base(struct igc_hw *hw)
        if (ret_val)
                return ret_val;
 
+       igc_check_for_link_base(hw);
+
        /* Verify phy id and set remaining function pointers */
        switch (phy->id) {
        case I225_I_PHY_ID:
@@ -258,10 +286,22 @@ out:
 
 static s32 igc_get_invariants_base(struct igc_hw *hw)
 {
+       struct igc_mac_info *mac = &hw->mac;
        u32 link_mode = 0;
        u32 ctrl_ext = 0;
        s32 ret_val = 0;
 
+       switch (hw->device_id) {
+       case IGC_DEV_ID_I225_LM:
+       case IGC_DEV_ID_I225_V:
+               mac->type = igc_i225;
+               break;
+       default:
+               return -IGC_ERR_MAC_INIT;
+       }
+
+       hw->phy.media_type = igc_media_type_copper;
+
        ctrl_ext = rd32(IGC_CTRL_EXT);
        link_mode = ctrl_ext & IGC_CTRL_EXT_LINK_MODE_MASK;
 
index d271671e6825589cbd1777ff4f402acf07559012..70275a0e85d7af342e3e1a805faf42dece8c979a 100644 (file)
 /* Physical Func Reset Done Indication */
 #define IGC_CTRL_EXT_LINK_MODE_MASK    0x00C00000
 
+/* Loop limit on how long we wait for auto-negotiation to complete */
+#define COPPER_LINK_UP_LIMIT           10
+#define PHY_AUTO_NEG_LIMIT             45
+#define PHY_FORCE_LIMIT                        20
+
 /* Number of 100 microseconds we wait for PCI Express master disable */
 #define MASTER_DISABLE_TIMEOUT         800
 /*Blocks new Master requests */
 #define IGC_CTRL_RST           0x04000000  /* Global reset */
 
 #define IGC_CTRL_PHY_RST       0x80000000  /* PHY Reset */
+#define IGC_CTRL_SLU           0x00000040  /* Set link up (Force Link) */
+#define IGC_CTRL_FRCSPD                0x00000800  /* Force Speed */
+#define IGC_CTRL_FRCDPX                0x00001000  /* Force Duplex */
+
+#define IGC_CTRL_RFCE          0x08000000  /* Receive Flow Control enable */
+#define IGC_CTRL_TFCE          0x10000000  /* Transmit flow control enable */
 
 /* PBA constants */
 #define IGC_PBA_34K            0x0022
 #define IGC_SWFW_EEP_SM                0x1
 #define IGC_SWFW_PHY0_SM       0x2
 
+/* Autoneg Advertisement Register */
+#define NWAY_AR_10T_HD_CAPS    0x0020   /* 10T   Half Duplex Capable */
+#define NWAY_AR_10T_FD_CAPS    0x0040   /* 10T   Full Duplex Capable */
+#define NWAY_AR_100TX_HD_CAPS  0x0080   /* 100TX Half Duplex Capable */
+#define NWAY_AR_100TX_FD_CAPS  0x0100   /* 100TX Full Duplex Capable */
+#define NWAY_AR_PAUSE          0x0400   /* Pause operation desired */
+#define NWAY_AR_ASM_DIR                0x0800   /* Asymmetric Pause Direction bit */
+
+/* Link Partner Ability Register (Base Page) */
+#define NWAY_LPAR_PAUSE                0x0400 /* LP Pause operation desired */
+#define NWAY_LPAR_ASM_DIR      0x0800 /* LP Asymmetric Pause Direction bit */
+
+/* 1000BASE-T Control Register */
+#define CR_1000T_ASYM_PAUSE    0x0080 /* Advertise asymmetric pause bit */
+#define CR_1000T_HD_CAPS       0x0100 /* Advertise 1000T HD capability */
+#define CR_1000T_FD_CAPS       0x0200 /* Advertise 1000T FD capability  */
+
+/* PHY GPY 211 registers */
+#define STANDARD_AN_REG_MASK   0x0007 /* MMD */
+#define ANEG_MULTIGBT_AN_CTRL  0x0020 /* MULTI GBT AN Control Register */
+#define MMD_DEVADDR_SHIFT      16     /* Shift MMD to higher bits */
+#define CR_2500T_FD_CAPS       0x0080 /* Advertise 2500T FD capability */
+
 /* NVM Control */
 /* Number of milliseconds for NVM auto read done after MAC reset. */
 #define AUTO_READ_DONE_TIMEOUT         10
 #define PHY_STATUS             0x01 /* Status Register */
 #define PHY_ID1                        0x02 /* Phy Id Reg (word 1) */
 #define PHY_ID2                        0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV                0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY         0x05 /* Link Partner Ability (Base Page) */
+#define PHY_1000T_CTRL         0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS       0x0A /* 1000Base-T Status Reg */
 
 /* Bit definitions for valid PHY IDs. I = Integrated E = External */
 #define I225_I_PHY_ID          0x67C9DC00
index fce7f7f5aa4610c2911917f781e1863b30d94425..f7683d3ae47c1074bbe5d68f1fc6234750b84fb9 100644 (file)
@@ -92,6 +92,8 @@ s32 igc_setup_link(struct igc_hw *hw)
        /* In the case of the phy reset being blocked, we already have a link.
         * We do not need to set it up again.
         */
+       if (igc_check_reset_block(hw))
+               goto out;
 
        /* If requested flow control is set to default, set flow control
         * based on the EEPROM flow control settings.
@@ -142,9 +144,73 @@ out:
  */
 static s32 igc_set_default_fc(struct igc_hw *hw)
 {
+       hw->fc.requested_mode = igc_fc_full;
        return 0;
 }
 
+/**
+ * igc_force_mac_fc - Force the MAC's flow control settings
+ * @hw: pointer to the HW structure
+ *
+ * Force the MAC's flow control settings.  Sets the TFCE and RFCE bits in the
+ * device control register to reflect the adapter settings.  TFCE and RFCE
+ * need to be explicitly set by software when a copper PHY is used because
+ * autonegotiation is managed by the PHY rather than the MAC.  Software must
+ * also configure these bits when link is forced on a fiber connection.
+ */
+s32 igc_force_mac_fc(struct igc_hw *hw)
+{
+       s32 ret_val = 0;
+       u32 ctrl;
+
+       ctrl = rd32(IGC_CTRL);
+
+       /* Because we didn't get link via the internal auto-negotiation
+        * mechanism (we either forced link or we got link via PHY
+        * auto-neg), we have to manually enable/disable transmit an
+        * receive flow control.
+        *
+        * The "Case" statement below enables/disable flow control
+        * according to the "hw->fc.current_mode" parameter.
+        *
+        * The possible values of the "fc" parameter are:
+        *      0:  Flow control is completely disabled
+        *      1:  Rx flow control is enabled (we can receive pause
+        *          frames but not send pause frames).
+        *      2:  Tx flow control is enabled (we can send pause frames
+        *          frames but we do not receive pause frames).
+        *      3:  Both Rx and TX flow control (symmetric) is enabled.
+        *  other:  No other values should be possible at this point.
+        */
+       hw_dbg("hw->fc.current_mode = %u\n", hw->fc.current_mode);
+
+       switch (hw->fc.current_mode) {
+       case igc_fc_none:
+               ctrl &= (~(IGC_CTRL_TFCE | IGC_CTRL_RFCE));
+               break;
+       case igc_fc_rx_pause:
+               ctrl &= (~IGC_CTRL_TFCE);
+               ctrl |= IGC_CTRL_RFCE;
+               break;
+       case igc_fc_tx_pause:
+               ctrl &= (~IGC_CTRL_RFCE);
+               ctrl |= IGC_CTRL_TFCE;
+               break;
+       case igc_fc_full:
+               ctrl |= (IGC_CTRL_TFCE | IGC_CTRL_RFCE);
+               break;
+       default:
+               hw_dbg("Flow control param set incorrectly\n");
+               ret_val = -IGC_ERR_CONFIG;
+               goto out;
+       }
+
+       wr32(IGC_CTRL, ctrl);
+
+out:
+       return ret_val;
+}
+
 /**
  * igc_set_fc_watermarks - Set flow control high/low watermarks
  * @hw: pointer to the HW structure
@@ -371,6 +437,7 @@ s32 igc_check_for_copper_link(struct igc_hw *hw)
         * settings because we may have had to re-autoneg with a
         * different link partner.
         */
+       ret_val = igc_config_fc_after_link_up(hw);
        if (ret_val)
                hw_dbg("Error configuring flow control\n");
 
@@ -399,6 +466,210 @@ void igc_config_collision_dist(struct igc_hw *hw)
        wrfl();
 }
 
+/**
+ * igc_config_fc_after_link_up - Configures flow control after link
+ * @hw: pointer to the HW structure
+ *
+ * Checks the status of auto-negotiation after link up to ensure that the
+ * speed and duplex were not forced.  If the link needed to be forced, then
+ * flow control needs to be forced also.  If auto-negotiation is enabled
+ * and did not fail, then we configure flow control based on our link
+ * partner.
+ */
+s32 igc_config_fc_after_link_up(struct igc_hw *hw)
+{
+       u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
+       struct igc_mac_info *mac = &hw->mac;
+       u16 speed, duplex;
+       s32 ret_val = 0;
+
+       /* Check for the case where we have fiber media and auto-neg failed
+        * so we had to force link.  In this case, we need to force the
+        * configuration of the MAC to match the "fc" parameter.
+        */
+       if (mac->autoneg_failed) {
+               if (hw->phy.media_type == igc_media_type_copper)
+                       ret_val = igc_force_mac_fc(hw);
+       }
+
+       if (ret_val) {
+               hw_dbg("Error forcing flow control settings\n");
+               goto out;
+       }
+
+       /* Check for the case where we have copper media and auto-neg is
+        * enabled.  In this case, we need to check and see if Auto-Neg
+        * has completed, and if so, how the PHY and link partner has
+        * flow control configured.
+        */
+       if (hw->phy.media_type == igc_media_type_copper && mac->autoneg) {
+               /* Read the MII Status Register and check to see if AutoNeg
+                * has completed.  We read this twice because this reg has
+                * some "sticky" (latched) bits.
+                */
+               ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
+                                              &mii_status_reg);
+               if (ret_val)
+                       goto out;
+               ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
+                                              &mii_status_reg);
+               if (ret_val)
+                       goto out;
+
+               if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
+                       hw_dbg("Copper PHY and Auto Neg has not completed.\n");
+                       goto out;
+               }
+
+               /* The AutoNeg process has completed, so we now need to
+                * read both the Auto Negotiation Advertisement
+                * Register (Address 4) and the Auto_Negotiation Base
+                * Page Ability Register (Address 5) to determine how
+                * flow control was negotiated.
+                */
+               ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
+                                              &mii_nway_adv_reg);
+               if (ret_val)
+                       goto out;
+               ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
+                                              &mii_nway_lp_ability_reg);
+               if (ret_val)
+                       goto out;
+               /* Two bits in the Auto Negotiation Advertisement Register
+                * (Address 4) and two bits in the Auto Negotiation Base
+                * Page Ability Register (Address 5) determine flow control
+                * for both the PHY and the link partner.  The following
+                * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,
+                * 1999, describes these PAUSE resolution bits and how flow
+                * control is determined based upon these settings.
+                * NOTE:  DC = Don't Care
+                *
+                *   LOCAL DEVICE  |   LINK PARTNER
+                * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
+                *-------|---------|-------|---------|--------------------
+                *   0   |    0    |  DC   |   DC    | igc_fc_none
+                *   0   |    1    |   0   |   DC    | igc_fc_none
+                *   0   |    1    |   1   |    0    | igc_fc_none
+                *   0   |    1    |   1   |    1    | igc_fc_tx_pause
+                *   1   |    0    |   0   |   DC    | igc_fc_none
+                *   1   |   DC    |   1   |   DC    | igc_fc_full
+                *   1   |    1    |   0   |    0    | igc_fc_none
+                *   1   |    1    |   0   |    1    | igc_fc_rx_pause
+                *
+                * Are both PAUSE bits set to 1?  If so, this implies
+                * Symmetric Flow Control is enabled at both ends.  The
+                * ASM_DIR bits are irrelevant per the spec.
+                *
+                * For Symmetric Flow Control:
+                *
+                *   LOCAL DEVICE  |   LINK PARTNER
+                * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+                *-------|---------|-------|---------|--------------------
+                *   1   |   DC    |   1   |   DC    | IGC_fc_full
+                *
+                */
+               if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+                   (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {
+                       /* Now we need to check if the user selected RX ONLY
+                        * of pause frames.  In this case, we had to advertise
+                        * FULL flow control because we could not advertise RX
+                        * ONLY. Hence, we must now check to see if we need to
+                        * turn OFF  the TRANSMISSION of PAUSE frames.
+                        */
+                       if (hw->fc.requested_mode == igc_fc_full) {
+                               hw->fc.current_mode = igc_fc_full;
+                               hw_dbg("Flow Control = FULL.\n");
+                       } else {
+                               hw->fc.current_mode = igc_fc_rx_pause;
+                               hw_dbg("Flow Control = RX PAUSE frames only.\n");
+                       }
+               }
+
+               /* For receiving PAUSE frames ONLY.
+                *
+                *   LOCAL DEVICE  |   LINK PARTNER
+                * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+                *-------|---------|-------|---------|--------------------
+                *   0   |    1    |   1   |    1    | igc_fc_tx_pause
+                */
+               else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+                        (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+                        (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+                        (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+                       hw->fc.current_mode = igc_fc_tx_pause;
+                       hw_dbg("Flow Control = TX PAUSE frames only.\n");
+               }
+               /* For transmitting PAUSE frames ONLY.
+                *
+                *   LOCAL DEVICE  |   LINK PARTNER
+                * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+                *-------|---------|-------|---------|--------------------
+                *   1   |    1    |   0   |    1    | igc_fc_rx_pause
+                */
+               else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+                        (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+                        !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+                        (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+                       hw->fc.current_mode = igc_fc_rx_pause;
+                       hw_dbg("Flow Control = RX PAUSE frames only.\n");
+               }
+               /* Per the IEEE spec, at this point flow control should be
+                * disabled.  However, we want to consider that we could
+                * be connected to a legacy switch that doesn't advertise
+                * desired flow control, but can be forced on the link
+                * partner.  So if we advertised no flow control, that is
+                * what we will resolve to.  If we advertised some kind of
+                * receive capability (Rx Pause Only or Full Flow Control)
+                * and the link partner advertised none, we will configure
+                * ourselves to enable Rx Flow Control only.  We can do
+                * this safely for two reasons:  If the link partner really
+                * didn't want flow control enabled, and we enable Rx, no
+                * harm done since we won't be receiving any PAUSE frames
+                * anyway.  If the intent on the link partner was to have
+                * flow control enabled, then by us enabling RX only, we
+                * can at least receive pause frames and process them.
+                * This is a good idea because in most cases, since we are
+                * predominantly a server NIC, more times than not we will
+                * be asked to delay transmission of packets than asking
+                * our link partner to pause transmission of frames.
+                */
+               else if ((hw->fc.requested_mode == igc_fc_none) ||
+                        (hw->fc.requested_mode == igc_fc_tx_pause) ||
+                        (hw->fc.strict_ieee)) {
+                       hw->fc.current_mode = igc_fc_none;
+                       hw_dbg("Flow Control = NONE.\n");
+               } else {
+                       hw->fc.current_mode = igc_fc_rx_pause;
+                       hw_dbg("Flow Control = RX PAUSE frames only.\n");
+               }
+
+               /* Now we need to do one last check...  If we auto-
+                * negotiated to HALF DUPLEX, flow control should not be
+                * enabled per IEEE 802.3 spec.
+                */
+               ret_val = hw->mac.ops.get_speed_and_duplex(hw, &speed, &duplex);
+               if (ret_val) {
+                       hw_dbg("Error getting link speed and duplex\n");
+                       goto out;
+               }
+
+               if (duplex == HALF_DUPLEX)
+                       hw->fc.current_mode = igc_fc_none;
+
+               /* Now we call a subroutine to actually force the MAC
+                * controller to use the correct flow control settings.
+                */
+               ret_val = igc_force_mac_fc(hw);
+               if (ret_val) {
+                       hw_dbg("Error forcing flow control settings\n");
+                       goto out;
+               }
+       }
+
+out:
+       return 0;
+}
+
 /**
  * igc_get_auto_rd_done - Check for auto read completion
  * @hw: pointer to the HW structure
index c842cc561123c7f1e848438f17dfcde2caab75cf..782bc995badc8e8dd77c1a6839a53ff6e632c98e 100644 (file)
@@ -15,6 +15,8 @@
 /* forward declaration */
 s32 igc_disable_pcie_master(struct igc_hw *hw);
 s32 igc_check_for_copper_link(struct igc_hw *hw);
+s32 igc_config_fc_after_link_up(struct igc_hw *hw);
+s32 igc_force_mac_fc(struct igc_hw *hw);
 void igc_init_rx_addrs(struct igc_hw *hw, u16 rar_count);
 s32 igc_setup_link(struct igc_hw *hw);
 void igc_clear_hw_cntrs_base(struct igc_hw *hw);
index 14f324826604d73e609499812eb12e813cf44c72..e1a078e084f0e804c8b5ca1d75ad4a6f6ba8633f 100644 (file)
@@ -3403,6 +3403,25 @@ static int igc_probe(struct pci_dev *pdev,
        netdev->min_mtu = ETH_MIN_MTU;
        netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE;
 
+       /* before reading the NVM, reset the controller to put the device in a
+        * known good starting state
+        */
+       hw->mac.ops.reset_hw(hw);
+
+       if (eth_platform_get_mac_address(&pdev->dev, hw->mac.addr)) {
+               /* copy the MAC address out of the NVM */
+               if (hw->mac.ops.read_mac_addr(hw))
+                       dev_err(&pdev->dev, "NVM Read Error\n");
+       }
+
+       memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len);
+
+       if (!is_valid_ether_addr(netdev->dev_addr)) {
+               dev_err(&pdev->dev, "Invalid MAC Address\n");
+               err = -EIO;
+               goto err_eeprom;
+       }
+
        /* configure RXPBSIZE and TXPBSIZE */
        wr32(IGC_RXPBS, I225_RXPBSIZE_DEFAULT);
        wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
@@ -3411,6 +3430,14 @@ static int igc_probe(struct pci_dev *pdev,
 
        INIT_WORK(&adapter->reset_task, igc_reset_task);
 
+       /* Initialize link properties that are user-changeable */
+       adapter->fc_autoneg = true;
+       hw->mac.autoneg = true;
+       hw->phy.autoneg_advertised = 0xaf;
+
+       hw->fc.requested_mode = igc_fc_default;
+       hw->fc.current_mode = igc_fc_default;
+
        /* reset the hardware with the new settings */
        igc_reset(adapter);
 
@@ -3438,6 +3465,9 @@ static int igc_probe(struct pci_dev *pdev,
 
 err_register:
        igc_release_hw_control(adapter);
+err_eeprom:
+       if (!igc_check_reset_block(hw))
+               igc_reset_phy(hw);
 err_sw_init:
        igc_clear_interrupt_scheme(adapter);
        iounmap(adapter->io_addr);
index 88583c1d49708b958ebc2eeff6fd74a2c54dfe87..38e43e6fc1c7c55f3770fd6e7e4809c580095d96 100644 (file)
@@ -3,6 +3,10 @@
 
 #include "igc_phy.h"
 
+/* forward declaration */
+static s32 igc_phy_setup_autoneg(struct igc_hw *hw);
+static s32 igc_wait_autoneg(struct igc_hw *hw);
+
 /**
  * igc_check_reset_block - Check if PHY reset is blocked
  * @hw: pointer to the HW structure
@@ -211,6 +215,336 @@ out:
        return ret_val;
 }
 
+/**
+ * igc_copper_link_autoneg - Setup/Enable autoneg for copper link
+ * @hw: pointer to the HW structure
+ *
+ * Performs initial bounds checking on autoneg advertisement parameter, then
+ * configure to advertise the full capability.  Setup the PHY to autoneg
+ * and restart the negotiation process between the link partner.  If
+ * autoneg_wait_to_complete, then wait for autoneg to complete before exiting.
+ */
+static s32 igc_copper_link_autoneg(struct igc_hw *hw)
+{
+       struct igc_phy_info *phy = &hw->phy;
+       u16 phy_ctrl;
+       s32 ret_val;
+
+       /* Perform some bounds checking on the autoneg advertisement
+        * parameter.
+        */
+       phy->autoneg_advertised &= phy->autoneg_mask;
+
+       /* If autoneg_advertised is zero, we assume it was not defaulted
+        * by the calling code so we set to advertise full capability.
+        */
+       if (phy->autoneg_advertised == 0)
+               phy->autoneg_advertised = phy->autoneg_mask;
+
+       hw_dbg("Reconfiguring auto-neg advertisement params\n");
+       ret_val = igc_phy_setup_autoneg(hw);
+       if (ret_val) {
+               hw_dbg("Error Setting up Auto-Negotiation\n");
+               goto out;
+       }
+       hw_dbg("Restarting Auto-Neg\n");
+
+       /* Restart auto-negotiation by setting the Auto Neg Enable bit and
+        * the Auto Neg Restart bit in the PHY control register.
+        */
+       ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
+       if (ret_val)
+               goto out;
+
+       phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+       ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
+       if (ret_val)
+               goto out;
+
+       /* Does the user want to wait for Auto-Neg to complete here, or
+        * check at a later time (for example, callback routine).
+        */
+       if (phy->autoneg_wait_to_complete) {
+               ret_val = igc_wait_autoneg(hw);
+               if (ret_val) {
+                       hw_dbg("Error while waiting for autoneg to complete\n");
+                       goto out;
+               }
+       }
+
+       hw->mac.get_link_status = true;
+
+out:
+       return ret_val;
+}
+
+/**
+ * igc_wait_autoneg - Wait for auto-neg completion
+ * @hw: pointer to the HW structure
+ *
+ * Waits for auto-negotiation to complete or for the auto-negotiation time
+ * limit to expire, which ever happens first.
+ */
+static s32 igc_wait_autoneg(struct igc_hw *hw)
+{
+       u16 i, phy_status;
+       s32 ret_val = 0;
+
+       /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */
+       for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) {
+               ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+               if (ret_val)
+                       break;
+               ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+               if (ret_val)
+                       break;
+               if (phy_status & MII_SR_AUTONEG_COMPLETE)
+                       break;
+               msleep(100);
+       }
+
+       /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation
+        * has completed.
+        */
+       return ret_val;
+}
+
+/**
+ * igc_phy_setup_autoneg - Configure PHY for auto-negotiation
+ * @hw: pointer to the HW structure
+ *
+ * Reads the MII auto-neg advertisement register and/or the 1000T control
+ * register and if the PHY is already setup for auto-negotiation, then
+ * return successful.  Otherwise, setup advertisement and flow control to
+ * the appropriate values for the wanted auto-negotiation.
+ */
+static s32 igc_phy_setup_autoneg(struct igc_hw *hw)
+{
+       struct igc_phy_info *phy = &hw->phy;
+       u16 aneg_multigbt_an_ctrl = 0;
+       u16 mii_1000t_ctrl_reg = 0;
+       u16 mii_autoneg_adv_reg;
+       s32 ret_val;
+
+       phy->autoneg_advertised &= phy->autoneg_mask;
+
+       /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+       ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg);
+       if (ret_val)
+               return ret_val;
+
+       if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
+               /* Read the MII 1000Base-T Control Register (Address 9). */
+               ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL,
+                                           &mii_1000t_ctrl_reg);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
+           hw->phy.id == I225_I_PHY_ID) {
+               /* Read the MULTI GBT AN Control Register - reg 7.32 */
+               ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK <<
+                                           MMD_DEVADDR_SHIFT) |
+                                           ANEG_MULTIGBT_AN_CTRL,
+                                           &aneg_multigbt_an_ctrl);
+
+               if (ret_val)
+                       return ret_val;
+       }
+
+       /* Need to parse both autoneg_advertised and fc and set up
+        * the appropriate PHY registers.  First we will parse for
+        * autoneg_advertised software override.  Since we can advertise
+        * a plethora of combinations, we need to check each bit
+        * individually.
+        */
+
+       /* First we clear all the 10/100 mb speed bits in the Auto-Neg
+        * Advertisement Register (Address 4) and the 1000 mb speed bits in
+        * the  1000Base-T Control Register (Address 9).
+        */
+       mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS |
+                                NWAY_AR_100TX_HD_CAPS |
+                                NWAY_AR_10T_FD_CAPS   |
+                                NWAY_AR_10T_HD_CAPS);
+       mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS);
+
+       hw_dbg("autoneg_advertised %x\n", phy->autoneg_advertised);
+
+       /* Do we want to advertise 10 Mb Half Duplex? */
+       if (phy->autoneg_advertised & ADVERTISE_10_HALF) {
+               hw_dbg("Advertise 10mb Half duplex\n");
+               mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
+       }
+
+       /* Do we want to advertise 10 Mb Full Duplex? */
+       if (phy->autoneg_advertised & ADVERTISE_10_FULL) {
+               hw_dbg("Advertise 10mb Full duplex\n");
+               mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
+       }
+
+       /* Do we want to advertise 100 Mb Half Duplex? */
+       if (phy->autoneg_advertised & ADVERTISE_100_HALF) {
+               hw_dbg("Advertise 100mb Half duplex\n");
+               mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
+       }
+
+       /* Do we want to advertise 100 Mb Full Duplex? */
+       if (phy->autoneg_advertised & ADVERTISE_100_FULL) {
+               hw_dbg("Advertise 100mb Full duplex\n");
+               mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
+       }
+
+       /* We do not allow the Phy to advertise 1000 Mb Half Duplex */
+       if (phy->autoneg_advertised & ADVERTISE_1000_HALF)
+               hw_dbg("Advertise 1000mb Half duplex request denied!\n");
+
+       /* Do we want to advertise 1000 Mb Full Duplex? */
+       if (phy->autoneg_advertised & ADVERTISE_1000_FULL) {
+               hw_dbg("Advertise 1000mb Full duplex\n");
+               mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
+       }
+
+       /* We do not allow the Phy to advertise 2500 Mb Half Duplex */
+       if (phy->autoneg_advertised & ADVERTISE_2500_HALF)
+               hw_dbg("Advertise 2500mb Half duplex request denied!\n");
+
+       /* Do we want to advertise 2500 Mb Full Duplex? */
+       if (phy->autoneg_advertised & ADVERTISE_2500_FULL) {
+               hw_dbg("Advertise 2500mb Full duplex\n");
+               aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS;
+       } else {
+               aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS;
+       }
+
+       /* Check for a software override of the flow control settings, and
+        * setup the PHY advertisement registers accordingly.  If
+        * auto-negotiation is enabled, then software will have to set the
+        * "PAUSE" bits to the correct value in the Auto-Negotiation
+        * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-
+        * negotiation.
+        *
+        * The possible values of the "fc" parameter are:
+        *      0:  Flow control is completely disabled
+        *      1:  Rx flow control is enabled (we can receive pause frames
+        *          but not send pause frames).
+        *      2:  Tx flow control is enabled (we can send pause frames
+        *          but we do not support receiving pause frames).
+        *      3:  Both Rx and Tx flow control (symmetric) are enabled.
+        *  other:  No software override.  The flow control configuration
+        *          in the EEPROM is used.
+        */
+       switch (hw->fc.current_mode) {
+       case igc_fc_none:
+               /* Flow control (Rx & Tx) is completely disabled by a
+                * software over-ride.
+                */
+               mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+               break;
+       case igc_fc_rx_pause:
+               /* Rx Flow control is enabled, and Tx Flow control is
+                * disabled, by a software over-ride.
+                *
+                * Since there really isn't a way to advertise that we are
+                * capable of Rx Pause ONLY, we will advertise that we
+                * support both symmetric and asymmetric Rx PAUSE.  Later
+                * (in igc_config_fc_after_link_up) we will disable the
+                * hw's ability to send PAUSE frames.
+                */
+               mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+               break;
+       case igc_fc_tx_pause:
+               /* Tx Flow control is enabled, and Rx Flow control is
+                * disabled, by a software over-ride.
+                */
+               mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
+               mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
+               break;
+       case igc_fc_full:
+               /* Flow control (both Rx and Tx) is enabled by a software
+                * over-ride.
+                */
+               mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+               break;
+       default:
+               hw_dbg("Flow control param set incorrectly\n");
+               return -IGC_ERR_CONFIG;
+       }
+
+       ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg);
+       if (ret_val)
+               return ret_val;
+
+       hw_dbg("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
+
+       if (phy->autoneg_mask & ADVERTISE_1000_FULL)
+               ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL,
+                                            mii_1000t_ctrl_reg);
+
+       if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
+           hw->phy.id == I225_I_PHY_ID)
+               ret_val = phy->ops.write_reg(hw,
+                                            (STANDARD_AN_REG_MASK <<
+                                            MMD_DEVADDR_SHIFT) |
+                                            ANEG_MULTIGBT_AN_CTRL,
+                                            aneg_multigbt_an_ctrl);
+
+       return ret_val;
+}
+
+/**
+ * igc_setup_copper_link - Configure copper link settings
+ * @hw: pointer to the HW structure
+ *
+ * Calls the appropriate function to configure the link for auto-neg or forced
+ * speed and duplex.  Then we check for link, once link is established calls
+ * to configure collision distance and flow control are called.  If link is
+ * not established, we return -IGC_ERR_PHY (-2).
+ */
+s32 igc_setup_copper_link(struct igc_hw *hw)
+{
+       s32 ret_val = 0;
+       bool link;
+
+       if (hw->mac.autoneg) {
+               /* Setup autoneg and flow control advertisement and perform
+                * autonegotiation.
+                */
+               ret_val = igc_copper_link_autoneg(hw);
+               if (ret_val)
+                       goto out;
+       } else {
+               /* PHY will be set to 10H, 10F, 100H or 100F
+                * depending on user settings.
+                */
+               hw_dbg("Forcing Speed and Duplex\n");
+               ret_val = hw->phy.ops.force_speed_duplex(hw);
+               if (ret_val) {
+                       hw_dbg("Error Forcing Speed and Duplex\n");
+                       goto out;
+               }
+       }
+
+       /* Check link status. Wait up to 100 microseconds for link to become
+        * valid.
+        */
+       ret_val = igc_phy_has_link(hw, COPPER_LINK_UP_LIMIT, 10, &link);
+       if (ret_val)
+               goto out;
+
+       if (link) {
+               hw_dbg("Valid link established!!!\n");
+               igc_config_collision_dist(hw);
+               ret_val = igc_config_fc_after_link_up(hw);
+       } else {
+               hw_dbg("Unable to establish link!!!\n");
+       }
+
+out:
+       return ret_val;
+}
+
 /**
  * igc_read_phy_reg_mdic - Read MDI control register
  * @hw: pointer to the HW structure
index 6a62f381559d899ed7c85b864685e494698e3ae9..25cba33de7e2789de93442d8490b65151b524020 100644 (file)
@@ -12,6 +12,7 @@ s32 igc_get_phy_id(struct igc_hw *hw);
 s32 igc_phy_has_link(struct igc_hw *hw, u32 iterations,
                     u32 usec_interval, bool *success);
 s32 igc_check_downshift(struct igc_hw *hw);
+s32 igc_setup_copper_link(struct igc_hw *hw);
 void igc_power_up_phy_copper(struct igc_hw *hw);
 void igc_power_down_phy_copper(struct igc_hw *hw);
 s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data);