+ * ixgbe_setup_m88 - setup m88 PHY
+ * @hw: pointer to hardware structure
+ */
+STATIC s32 ixgbe_setup_m88(struct ixgbe_hw *hw)
+{
+ u32 mask = hw->phy.phy_semaphore_mask | IXGBE_GSSR_TOKEN_SM;
+ u16 reg;
+ s32 rc;
+
+ if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
+ return IXGBE_SUCCESS;
+
+ rc = hw->mac.ops.acquire_swfw_sync(hw, mask);
+ if (rc)
+ return rc;
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, ®);
+ if (rc)
+ goto out;
+ if (reg & IXGBE_M88E1500_COPPER_CTRL_POWER_DOWN) {
+ reg &= ~IXGBE_M88E1500_COPPER_CTRL_POWER_DOWN;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0,
+ reg);
+ }
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_MAC_CTRL_1, 0, ®);
+ if (rc)
+ goto out;
+ if (reg & IXGBE_M88E1500_MAC_CTRL_1_POWER_DOWN) {
+ reg &= ~IXGBE_M88E1500_MAC_CTRL_1_POWER_DOWN;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_MAC_CTRL_1, 0,
+ reg);
+ }
+
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 2);
+ if (rc)
+ goto out;
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_MAC_SPEC_CTRL, 0,
+ ®);
+ if (rc)
+ goto out;
+ if (reg & IXGBE_M88E1500_MAC_SPEC_CTRL_POWER_DOWN) {
+ reg &= ~IXGBE_M88E1500_MAC_SPEC_CTRL_POWER_DOWN;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_MAC_SPEC_CTRL, 0,
+ reg);
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0,
+ 0);
+ if (rc)
+ goto out;
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0,
+ ®);
+ if (rc)
+ goto out;
+ reg |= IXGBE_M88E1500_COPPER_CTRL_RESET;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0,
+ reg);
+ usec_delay(50);
+ } else {
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0,
+ 0);
+ if (rc)
+ goto out;
+ }
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, ®);
+ if (rc)
+ goto out;
+
+ if (!(reg & IXGBE_M88E1500_COPPER_CTRL_AN_EN)) {
+ reg |= IXGBE_M88E1500_COPPER_CTRL_AN_EN;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0,
+ reg);
+ }
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_1000T_CTRL, 0, ®);
+ if (rc)
+ goto out;
+ reg &= ~IXGBE_M88E1500_1000T_CTRL_HALF_DUPLEX;
+ reg &= ~IXGBE_M88E1500_1000T_CTRL_FULL_DUPLEX;
+ if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL)
+ reg |= IXGBE_M88E1500_1000T_CTRL_FULL_DUPLEX;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_1000T_CTRL, 0, reg);
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_COPPER_AN, 0, ®);
+ if (rc)
+ goto out;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_AS_PAUSE;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_PAUSE;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_T4;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_100TX_FD;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_100TX_HD;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_10TX_FD;
+ reg &= ~IXGBE_M88E1500_COPPER_AN_10TX_HD;
+ switch (hw->fc.current_mode) {
+ case ixgbe_fc_full:
+ reg |= IXGBE_M88E1500_COPPER_AN_PAUSE;
+ break;
+ case ixgbe_fc_rx_pause:
+ reg |= IXGBE_M88E1500_COPPER_AN_PAUSE;
+ reg |= IXGBE_M88E1500_COPPER_AN_AS_PAUSE;
+ break;
+ case ixgbe_fc_tx_pause:
+ reg |= IXGBE_M88E1500_COPPER_AN_AS_PAUSE;
+ break;
+ default:
+ break;
+ }
+
+ if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
+ reg |= IXGBE_M88E1500_COPPER_AN_100TX_FD;
+ if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10_FULL)
+ reg |= IXGBE_M88E1500_COPPER_AN_10TX_FD;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_AN, 0, reg);
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, ®);
+ if (rc)
+ goto out;
+ reg |= IXGBE_M88E1500_COPPER_CTRL_RESTART_AN;
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, reg);
+
+
+ hw->mac.ops.release_swfw_sync(hw, mask);
+ return rc;
+
+out:
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 0);
+ hw->mac.ops.release_swfw_sync(hw, mask);
+ return rc;
+}
+
+/**
+ * ixgbe_reset_phy_m88e1500 - Reset m88e1500 PHY
+ * @hw: pointer to hardware structure
+ *
+ * The PHY token must be held when calling this function.
+ */
+static s32 ixgbe_reset_phy_m88e1500(struct ixgbe_hw *hw)
+{
+ u16 reg;
+ s32 rc;
+
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 0);
+ if (rc)
+ return rc;
+
+ rc = hw->phy.ops.read_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, ®);
+ if (rc)
+ return rc;
+
+ reg |= IXGBE_M88E1500_COPPER_CTRL_RESET;
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, reg);
+
+ usec_delay(10);
+
+ return rc;
+}
+
+/**
+ * ixgbe_reset_phy_m88e1543 - Reset m88e1543 PHY
+ * @hw: pointer to hardware structure
+ *
+ * The PHY token must be held when calling this function.
+ */
+static s32 ixgbe_reset_phy_m88e1543(struct ixgbe_hw *hw)
+{
+ return hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 0);
+}
+
+/**
+ * ixgbe_reset_phy_m88 - Reset m88 PHY
+ * @hw: pointer to hardware structure
+ */
+STATIC s32 ixgbe_reset_phy_m88(struct ixgbe_hw *hw)
+{
+ u32 mask = hw->phy.phy_semaphore_mask | IXGBE_GSSR_TOKEN_SM;
+ u16 reg;
+ s32 rc;
+
+ if (hw->phy.reset_disable || ixgbe_check_reset_blocked(hw))
+ return IXGBE_SUCCESS;
+
+ rc = hw->mac.ops.acquire_swfw_sync(hw, mask);
+ if (rc)
+ return rc;
+
+ switch (hw->phy.id) {
+ case IXGBE_M88E1500_E_PHY_ID:
+ rc = ixgbe_reset_phy_m88e1500(hw);
+ break;
+ case IXGBE_M88E1543_E_PHY_ID:
+ rc = ixgbe_reset_phy_m88e1543(hw);
+ break;
+ default:
+ rc = IXGBE_ERR_PHY;
+ break;
+ }
+
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 1);
+ if (rc)
+ goto out;
+
+ reg = IXGBE_M88E1500_FIBER_CTRL_RESET |
+ IXGBE_M88E1500_FIBER_CTRL_DUPLEX_FULL |
+ IXGBE_M88E1500_FIBER_CTRL_SPEED_MSB;
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_FIBER_CTRL, 0, reg);
+ if (rc)
+ goto out;
+
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 18);
+ if (rc)
+ goto out;
+
+ reg = IXGBE_M88E1500_GEN_CTRL_RESET |
+ IXGBE_M88E1500_GEN_CTRL_MODE_SGMII_COPPER;
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_GEN_CTRL, 0, reg);
+ if (rc)
+ goto out;
+
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 1);
+ if (rc)
+ goto out;
+
+ reg = IXGBE_M88E1500_FIBER_CTRL_RESET |
+ IXGBE_M88E1500_FIBER_CTRL_AN_EN |
+ IXGBE_M88E1500_FIBER_CTRL_DUPLEX_FULL |
+ IXGBE_M88E1500_FIBER_CTRL_SPEED_MSB;
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_FIBER_CTRL, 0, reg);
+ if (rc)
+ goto out;
+
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 0);
+ if (rc)
+ goto out;
+
+ reg = (IXGBE_M88E1500_MAC_CTRL_1_DWN_4X <<
+ IXGBE_M88E1500_MAC_CTRL_1_DWN_SHIFT) |
+ (IXGBE_M88E1500_MAC_CTRL_1_ED_TM <<
+ IXGBE_M88E1500_MAC_CTRL_1_ED_SHIFT) |
+ (IXGBE_M88E1500_MAC_CTRL_1_MDIX_AUTO <<
+ IXGBE_M88E1500_MAC_CTRL_1_MDIX_SHIFT);
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_MAC_CTRL_1, 0, reg);
+ if (rc)
+ goto out;
+
+ reg = IXGBE_M88E1500_COPPER_CTRL_RESET |
+ IXGBE_M88E1500_COPPER_CTRL_AN_EN |
+ IXGBE_M88E1500_COPPER_CTRL_RESTART_AN |
+ IXGBE_M88E1500_COPPER_CTRL_FULL_DUPLEX |
+ IXGBE_M88E1500_COPPER_CTRL_SPEED_MSB;
+ rc = hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_COPPER_CTRL, 0, reg);
+ if (rc)
+ goto out;
+
+ hw->mac.ops.release_swfw_sync(hw, mask);
+
+ return ixgbe_setup_m88(hw);
+
+out:
+ hw->phy.ops.write_reg_mdi(hw, IXGBE_M88E1500_PAGE_ADDR, 0, 0);
+ hw->mac.ops.release_swfw_sync(hw, mask);
+ return rc;
+}
+
+/**
+ * ixgbe_read_mng_if_sel_x550em - Read NW_MNG_IF_SEL register
+ * @hw: pointer to hardware structure
+ *
+ * Read NW_MNG_IF_SEL register and save field values, and check for valid field
+ * values.
+ **/
+STATIC s32 ixgbe_read_mng_if_sel_x550em(struct ixgbe_hw *hw)
+{
+ /* Save NW management interface connected on board. This is used
+ * to determine internal PHY mode.
+ */
+ hw->phy.nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
+
+ /* If X552 (X550EM_a) and MDIO is connected to external PHY, then set
+ * PHY address. This register field was has only been used for X552.
+ */
+ if (hw->mac.type == ixgbe_mac_X550EM_a &&
+ hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_MDIO_ACT) {
+ hw->phy.addr = (hw->phy.nw_mng_if_sel &
+ IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD) >>
+ IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT;
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_init_phy_ops_X550em - PHY/SFP specific init
+ * @hw: pointer to hardware structure
+ *
+ * Initialize any function pointers that were not able to be
+ * set during init_shared_code because the PHY/SFP type was
+ * not known. Perform the SFP init if necessary.
+ */
+s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
+{
+ struct ixgbe_phy_info *phy = &hw->phy;
+ s32 ret_val;
+
+ DEBUGFUNC("ixgbe_init_phy_ops_X550em");
+
+ hw->mac.ops.set_lan_id(hw);
+
+ ixgbe_read_mng_if_sel_x550em(hw);
+
+ if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) {
+ phy->phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM;
+ ixgbe_setup_mux_ctl(hw);
+ phy->ops.identify_sfp = ixgbe_identify_sfp_module_X550em;
+ }
+
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_X550EM_A_1G_T:
+ case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+ phy->ops.read_reg_mdi = ixgbe_read_phy_reg_mdi_22;
+ phy->ops.write_reg_mdi = ixgbe_write_phy_reg_mdi_22;
+ break;
+ default:
+ break;
+ }
+
+ /* Identify the PHY or SFP module */
+ ret_val = phy->ops.identify(hw);
+ if (ret_val == IXGBE_ERR_SFP_NOT_SUPPORTED)
+ return ret_val;
+
+ /* Setup function pointers based on detected hardware */
+ ixgbe_init_mac_link_ops_X550em(hw);
+ if (phy->sfp_type != ixgbe_sfp_type_unknown)
+ phy->ops.reset = NULL;
+
+ /* Set functions pointers based on phy type */
+ switch (hw->phy.type) {
+ case ixgbe_phy_x550em_kx4:
+ phy->ops.setup_link = NULL;
+ phy->ops.read_reg = ixgbe_read_phy_reg_x550em;
+ phy->ops.write_reg = ixgbe_write_phy_reg_x550em;
+ break;
+ case ixgbe_phy_x550em_kr:
+ phy->ops.setup_link = ixgbe_setup_kr_x550em;
+ phy->ops.read_reg = ixgbe_read_phy_reg_x550em;
+ phy->ops.write_reg = ixgbe_write_phy_reg_x550em;
+ break;
+ case ixgbe_phy_x550em_ext_t:
+ /* If internal link mode is XFI, then setup iXFI internal link,
+ * else setup KR now.
+ */
+ phy->ops.setup_internal_link =
+ ixgbe_setup_internal_phy_t_x550em;
+
+ /* setup SW LPLU only for first revision of X550EM_x */
+ if ((hw->mac.type == ixgbe_mac_X550EM_x) &&
+ !(IXGBE_FUSES0_REV_MASK &
+ IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0))))
+ phy->ops.enter_lplu = ixgbe_enter_lplu_t_x550em;
+
+ phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
+ phy->ops.reset = ixgbe_reset_phy_t_X550em;
+ break;
+ case ixgbe_phy_sgmii:
+ phy->ops.setup_link = NULL;
+ break;
+ case ixgbe_phy_m88:
+ phy->ops.setup_link = ixgbe_setup_m88;
+ phy->ops.reset = ixgbe_reset_phy_m88;
+ break;
+ default:
+ break;
+ }
+ return ret_val;
+}
+
+/**
+ * ixgbe_set_mdio_speed - Set MDIO clock speed
+ * @hw: pointer to hardware structure
+ */
+STATIC void ixgbe_set_mdio_speed(struct ixgbe_hw *hw)
+{
+ u32 hlreg0;
+
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_X550EM_X_10G_T:
+ case IXGBE_DEV_ID_X550EM_A_SGMII:
+ case IXGBE_DEV_ID_X550EM_A_SGMII_L:
+ case IXGBE_DEV_ID_X550EM_A_1G_T:
+ case IXGBE_DEV_ID_X550EM_A_1G_T_L:
+ case IXGBE_DEV_ID_X550EM_A_10G_T:
+ case IXGBE_DEV_ID_X550EM_A_SFP:
+ case IXGBE_DEV_ID_X550EM_A_QSFP:
+ /* Config MDIO clock speed before the first MDIO PHY access */
+ hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+ hlreg0 &= ~IXGBE_HLREG0_MDCSPD;
+ IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * ixgbe_reset_hw_X550em - Perform hardware reset
+ * @hw: pointer to hardware structure