This patch adds X550a flow control auto negotiation support.
ixgbe_setup_fc_x550a and ixgbe_fc_autoneg_X550a functions where
added to setup and enable flow control. MAC ops function pointer
fc_autoneg was added so that hardware specific fc autoneg functions
can be called from ixgbe_fc_enable_generic.

Signed-off-by: Beilei Xing <beilei.xing at intel.com>
---
 drivers/net/ixgbe/base/ixgbe_api.h    |   2 +
 drivers/net/ixgbe/base/ixgbe_common.c |   5 +-
 drivers/net/ixgbe/base/ixgbe_type.h   |   6 ++
 drivers/net/ixgbe/base/ixgbe_x550.c   | 181 ++++++++++++++++++++++++++++++++++
 drivers/net/ixgbe/base/ixgbe_x550.h   |   2 +
 5 files changed, 194 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ixgbe/base/ixgbe_api.h 
b/drivers/net/ixgbe/base/ixgbe_api.h
index c126982..3aad1da 100644
--- a/drivers/net/ixgbe/base/ixgbe_api.h
+++ b/drivers/net/ixgbe/base/ixgbe_api.h
@@ -217,5 +217,7 @@ s32 ixgbe_handle_lasi(struct ixgbe_hw *hw);
 void ixgbe_set_rate_select_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed);
 void ixgbe_disable_rx(struct ixgbe_hw *hw);
 void ixgbe_enable_rx(struct ixgbe_hw *hw);
+s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
+                       u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm);

 #endif /* _IXGBE_API_H_ */
diff --git a/drivers/net/ixgbe/base/ixgbe_common.c 
b/drivers/net/ixgbe/base/ixgbe_common.c
index e1d09e2..811875a 100644
--- a/drivers/net/ixgbe/base/ixgbe_common.c
+++ b/drivers/net/ixgbe/base/ixgbe_common.c
@@ -135,6 +135,7 @@ s32 ixgbe_init_ops_generic(struct ixgbe_hw *hw)
        /* Flow Control */
        mac->ops.fc_enable = ixgbe_fc_enable_generic;
        mac->ops.setup_fc = ixgbe_setup_fc_generic;
+       mac->ops.fc_autoneg = ixgbe_fc_autoneg;

        /* Link */
        mac->ops.get_link_capabilities = NULL;
@@ -2739,7 +2740,7 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
        }

        /* Negotiate the fc mode to use */
-       ixgbe_fc_autoneg(hw);
+       hw->mac.ops.fc_autoneg(hw);

        /* Disable any previous flow control settings */
        mflcn_reg = IXGBE_READ_REG(hw, IXGBE_MFLCN);
@@ -2849,7 +2850,7 @@ out:
  *  Find the intersection between advertised settings and link partner's
  *  advertised settings
  **/
-STATIC s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
+s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
                              u32 adv_sym, u32 adv_asm, u32 lp_sym, u32 lp_asm)
 {
        if ((!(adv_reg)) ||  (!(lp_reg))) {
diff --git a/drivers/net/ixgbe/base/ixgbe_type.h 
b/drivers/net/ixgbe/base/ixgbe_type.h
index 5d76c72..be51bee 100644
--- a/drivers/net/ixgbe/base/ixgbe_type.h
+++ b/drivers/net/ixgbe/base/ixgbe_type.h
@@ -3886,6 +3886,7 @@ struct ixgbe_mac_operations {
        /* Flow Control */
        s32 (*fc_enable)(struct ixgbe_hw *);
        s32 (*setup_fc)(struct ixgbe_hw *);
+       void (*fc_autoneg)(struct ixgbe_hw *);

        /* Manageability interface */
        s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8);
@@ -4131,10 +4132,12 @@ struct ixgbe_hw {
 #define IXGBE_FUSES0_REV_MASK          (3 << 6)

 #define IXGBE_KRM_PORT_CAR_GEN_CTRL(P) ((P) ? 0x8010 : 0x4010)
+#define IXGBE_KRM_LINK_S1(P)           ((P) ? 0x8200 : 0x4200)
 #define IXGBE_KRM_LINK_CTRL_1(P)       ((P) ? 0x820C : 0x420C)
 #define IXGBE_KRM_AN_CNTL_1(P)         ((P) ? 0x822C : 0x422C)
 #define IXGBE_KRM_AN_CNTL_8(P)         ((P) ? 0x8248 : 0x4248)
 #define IXGBE_KRM_SGMII_CTRL(P)                ((P) ? 0x82A0 : 0x42A0)
+#define IXGBE_KRM_LP_BASE_PAGE_HIGH(P) ((P) ? 0x836C : 0x436C)
 #define IXGBE_KRM_DSP_TXFFE_STATE_4(P) ((P) ? 0x8634 : 0x4634)
 #define IXGBE_KRM_DSP_TXFFE_STATE_5(P) ((P) ? 0x8638 : 0x4638)
 #define IXGBE_KRM_RX_TRN_LINKUP_CTRL(P)        ((P) ? 0x8B00 : 0x4B00)
@@ -4156,6 +4159,7 @@ struct ixgbe_hw {
 #define IXGBE_KRM_LINK_CTRL_1_TETH_AN_CAP_KR           (1 << 18)
 #define IXGBE_KRM_LINK_CTRL_1_TETH_EEE_CAP_KX          (1 << 24)
 #define IXGBE_KRM_LINK_CTRL_1_TETH_EEE_CAP_KR          (1 << 26)
+#define IXGBE_KRM_LINK_S1_MAC_AN_COMPLETE              (1 << 28)
 #define IXGBE_KRM_LINK_CTRL_1_TETH_AN_ENABLE           (1 << 29)
 #define IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART          (1 << 31)

@@ -4164,6 +4168,8 @@ struct ixgbe_hw {

 #define IXGBE_KRM_AN_CNTL_8_LINEAR                     (1 << 0)
 #define IXGBE_KRM_AN_CNTL_8_LIMITING                   (1 << 1)
+#define IXGBE_KRM_LP_BASE_PAGE_HIGH_SYM_PAUSE          (1 << 10)
+#define IXGBE_KRM_LP_BASE_PAGE_HIGH_ASM_PAUSE          (1 << 11)

 #define IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_100_D       (1 << 12)
 #define IXGBE_KRM_SGMII_CTRL_MAC_TAR_FORCE_10_D                (1 << 19)
diff --git a/drivers/net/ixgbe/base/ixgbe_x550.c 
b/drivers/net/ixgbe/base/ixgbe_x550.c
index 99023e9..ae6b79f 100644
--- a/drivers/net/ixgbe/base/ixgbe_x550.c
+++ b/drivers/net/ixgbe/base/ixgbe_x550.c
@@ -666,6 +666,10 @@ s32 ixgbe_init_ops_X550EM(struct ixgbe_hw *hw)

        if (mac->ops.get_media_type(hw) == ixgbe_media_type_copper)
                mac->ops.setup_fc = ixgbe_setup_fc_generic;
+       else if (hw->mac.type == ixgbe_mac_X550EM_a) {
+               mac->ops.setup_fc = ixgbe_setup_fc_x550a;
+               mac->ops.fc_autoneg = ixgbe_fc_autoneg_x550a;
+       }
        else
                mac->ops.setup_fc = ixgbe_setup_fc_X550em;

@@ -3759,6 +3763,183 @@ out:
 }

 /**
+ *  ixgbe_fc_autoneg_x550a - Enable flow control IEEE clause 37
+ *  @hw: pointer to hardware structure
+ *
+ *  Enable flow control according to IEEE clause 37.
+ **/
+void ixgbe_fc_autoneg_x550a(struct ixgbe_hw *hw)
+{
+       u32 link_s1, lp_an_page_low, an_cntl_1;
+       s32 status = IXGBE_ERR_FC_NOT_NEGOTIATED;
+       ixgbe_link_speed speed;
+       bool link_up;
+
+       /* AN should have completed when the cable was plugged in.
+        * Look for reasons to bail out.  Bail out if:
+        * - FC autoneg is disabled, or if
+        * - link is not up.
+        */
+       if (hw->fc.disable_fc_autoneg) {
+               ERROR_REPORT1(IXGBE_ERROR_UNSUPPORTED,
+                             "Flow control autoneg is disabled");
+               goto out;
+       }
+
+       hw->mac.ops.check_link(hw, &speed, &link_up, false);
+       if (!link_up) {
+               ERROR_REPORT1(IXGBE_ERROR_SOFTWARE, "The link is down");
+               goto out;
+       }
+
+       /* Check at auto-negotiation has completed */
+       status = hw->mac.ops.read_iosf_sb_reg(hw,
+                                     IXGBE_KRM_LINK_S1(hw->bus.lan_id),
+                                     IXGBE_SB_IOSF_TARGET_KR_PHY, &link_s1);
+
+       if (status != IXGBE_SUCCESS ||
+           (link_s1 & IXGBE_KRM_LINK_S1_MAC_AN_COMPLETE) == 0) {
+               DEBUGOUT("Auto-Negotiation did not complete\n");
+               goto out;
+       }
+
+       /* Read the 10g AN autoc and LP ability registers and resolve
+        * local flow control settings accordingly
+        */
+       status = hw->mac.ops.read_iosf_sb_reg(hw,
+                                     IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id),
+                                     IXGBE_SB_IOSF_TARGET_KR_PHY, &an_cntl_1);
+
+       if (status != IXGBE_SUCCESS) {
+               DEBUGOUT("Auto-Negotiation did not complete\n");
+               goto out;
+       }
+
+       status = hw->mac.ops.read_iosf_sb_reg(hw,
+                             IXGBE_KRM_LP_BASE_PAGE_HIGH(hw->bus.lan_id),
+                             IXGBE_SB_IOSF_TARGET_KR_PHY, &lp_an_page_low);
+
+       if (status != IXGBE_SUCCESS) {
+               DEBUGOUT("Auto-Negotiation did not complete\n");
+               goto out;
+       }
+
+       status = ixgbe_negotiate_fc(hw, an_cntl_1, lp_an_page_low,
+                                   IXGBE_KRM_AN_CNTL_1_SYM_PAUSE,
+                                   IXGBE_KRM_AN_CNTL_1_ASM_PAUSE,
+                                   IXGBE_KRM_LP_BASE_PAGE_HIGH_SYM_PAUSE,
+                                   IXGBE_KRM_LP_BASE_PAGE_HIGH_ASM_PAUSE);
+
+out:
+       if (status == IXGBE_SUCCESS) {
+               hw->fc.fc_was_autonegged = true;
+       } else {
+               hw->fc.fc_was_autonegged = false;
+               hw->fc.current_mode = hw->fc.requested_mode;
+       }
+}
+
+/**
+ *  ixgbe_setup_fc_x550em - Set up flow control
+ *  @hw: pointer to hardware structure
+ *
+ *  Called at init time to set up flow control.
+ **/
+s32 ixgbe_setup_fc_x550a(struct ixgbe_hw *hw)
+{
+       s32 status = IXGBE_SUCCESS;
+       u32 an_cntl, link_ctrl = 0;
+
+       DEBUGFUNC("ixgbe_setup_fc_x550em");
+
+       /* Validate the requested mode */
+       if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) {
+               ERROR_REPORT1(IXGBE_ERROR_UNSUPPORTED,
+                     "ixgbe_fc_rx_pause not valid in strict IEEE mode\n");
+               return IXGBE_ERR_INVALID_LINK_SETTINGS;
+       }
+
+       if (hw->fc.requested_mode == ixgbe_fc_default)
+               hw->fc.requested_mode = ixgbe_fc_full;
+
+       /* Set up the 1G and 10G flow control advertisement registers so the
+        * HW will be able to do FC autoneg once the cable is plugged in.  If
+        * we link at 10G, the 1G advertisement is harmless and vice versa.
+        */
+       status = hw->mac.ops.read_iosf_sb_reg(hw,
+                                     IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id),
+                                     IXGBE_SB_IOSF_TARGET_KR_PHY, &an_cntl);
+
+       if (status != IXGBE_SUCCESS) {
+               DEBUGOUT("Auto-Negotiation did not complete\n");
+               return status;
+       }
+
+       /* The possible values of fc.requested_mode 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: Invalid.
+        */
+       switch (hw->fc.requested_mode) {
+       case ixgbe_fc_none:
+               /* Flow control completely disabled by software override. */
+               an_cntl &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE |
+                            IXGBE_KRM_AN_CNTL_1_ASM_PAUSE);
+               break;
+       case ixgbe_fc_tx_pause:
+               /* Tx Flow control is enabled, and Rx Flow control is
+                * disabled by software override.
+                */
+               an_cntl |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE;
+               an_cntl &= ~IXGBE_KRM_AN_CNTL_1_SYM_PAUSE;
+               break;
+       case ixgbe_fc_rx_pause:
+               /* Rx Flow control is enabled and Tx Flow control is
+                * disabled by software override. 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, as such we fall
+                * through to the fc_full statement.  Later, we will
+                * disable the adapter's ability to send PAUSE frames.
+                */
+       case ixgbe_fc_full:
+               /* Flow control (both Rx and Tx) is enabled by SW override. */
+               an_cntl |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE |
+                       IXGBE_KRM_AN_CNTL_1_ASM_PAUSE;
+               break;
+       default:
+               ERROR_REPORT1(IXGBE_ERROR_ARGUMENT,
+                             "Flow control param set incorrectly\n");
+               return IXGBE_ERR_CONFIG;
+       }
+
+       status = hw->mac.ops.write_iosf_sb_reg(hw,
+                                      IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id),
+                                      IXGBE_SB_IOSF_TARGET_KR_PHY, an_cntl);
+
+       /* Restart auto-negotiation. */
+       status = hw->mac.ops.read_iosf_sb_reg(hw,
+                                     IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                     IXGBE_SB_IOSF_TARGET_KR_PHY, &link_ctrl);
+
+       if (status != IXGBE_SUCCESS) {
+               DEBUGOUT("Auto-Negotiation did not complete\n");
+               return status;
+       }
+
+       link_ctrl |= IXGBE_KRM_LINK_CTRL_1_TETH_AN_RESTART;
+       status = hw->mac.ops.write_iosf_sb_reg(hw,
+                                      IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id),
+                                      IXGBE_SB_IOSF_TARGET_KR_PHY, link_ctrl);
+
+       return status;
+}
+
+/**
  * ixgbe_set_mux - Set mux for port 1 access with CS4227
  * @hw: pointer to hardware structure
  * @state: set mux if 1, clear if 0
diff --git a/drivers/net/ixgbe/base/ixgbe_x550.h 
b/drivers/net/ixgbe/base/ixgbe_x550.h
index b597b50..27d5d02 100644
--- a/drivers/net/ixgbe/base/ixgbe_x550.h
+++ b/drivers/net/ixgbe/base/ixgbe_x550.h
@@ -150,6 +150,8 @@ s32 ixgbe_read_phy_reg_x550a(struct ixgbe_hw *hw, u32 
reg_addr,
                             u32 device_type, u16 *phy_data);
 s32 ixgbe_write_phy_reg_x550a(struct ixgbe_hw *hw, u32 reg_addr,
                              u32 device_type, u16 phy_data);
+s32 ixgbe_setup_fc_x550a(struct ixgbe_hw *hw);
+void ixgbe_fc_autoneg_x550a(struct ixgbe_hw *hw);
 s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw);
 s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw,
                                  ixgbe_link_speed speed,
-- 
2.5.0

Reply via email to