net/ngbe: support flow control
[dpdk.git] / drivers / net / ngbe / base / ngbe_phy_mvl.c
index 1248478..2eb351d 100644 (file)
@@ -48,6 +48,128 @@ s32 ngbe_write_phy_reg_mvl(struct ngbe_hw *hw,
        return 0;
 }
 
+s32 ngbe_init_phy_mvl(struct ngbe_hw *hw)
+{
+       s32 ret_val = 0;
+       u16 value = 0;
+       int i;
+
+       DEBUGFUNC("ngbe_init_phy_mvl");
+
+       /* enable interrupts, only link status change and an done is allowed */
+       ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 2);
+       ngbe_read_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, &value);
+       value &= ~MVL_RGM_CTL2_TTC;
+       value |= MVL_RGM_CTL2_RTC;
+       ngbe_write_phy_reg_mdi(hw, MVL_RGM_CTL2, 0, value);
+
+       hw->phy.write_reg(hw, MVL_CTRL, 0, MVL_CTRL_RESET);
+       for (i = 0; i < 15; i++) {
+               ngbe_read_phy_reg_mdi(hw, MVL_CTRL, 0, &value);
+               if (value & MVL_CTRL_RESET)
+                       msleep(1);
+               else
+                       break;
+       }
+
+       if (i == 15) {
+               DEBUGOUT("phy reset exceeds maximum waiting period.\n");
+               return NGBE_ERR_TIMEOUT;
+       }
+
+       ret_val = hw->phy.reset_hw(hw);
+       if (ret_val)
+               return ret_val;
+
+       /* set LED2 to interrupt output and INTn active low */
+       ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
+       ngbe_read_phy_reg_mdi(hw, MVL_LEDTCR, 0, &value);
+       value |= MVL_LEDTCR_INTR_EN;
+       value &= ~(MVL_LEDTCR_INTR_POL);
+       ngbe_write_phy_reg_mdi(hw, MVL_LEDTCR, 0, value);
+
+       if (hw->phy.type == ngbe_phy_mvl_sfi) {
+               hw->phy.read_reg(hw, MVL_CTRL1, 0, &value);
+               value &= ~MVL_CTRL1_INTR_POL;
+               ngbe_write_phy_reg_mdi(hw, MVL_CTRL1, 0, value);
+       }
+
+       /* enable link status change and AN complete interrupts */
+       value = MVL_INTR_EN_ANC | MVL_INTR_EN_LSC;
+       hw->phy.write_reg(hw, MVL_INTR_EN, 0, value);
+
+       /* LED control */
+       ngbe_write_phy_reg_mdi(hw, MVL_PAGE_SEL, 0, 3);
+       ngbe_read_phy_reg_mdi(hw, MVL_LEDFCR, 0, &value);
+       value &= ~(MVL_LEDFCR_CTL0 | MVL_LEDFCR_CTL1);
+       value |= MVL_LEDFCR_CTL0_CONF | MVL_LEDFCR_CTL1_CONF;
+       ngbe_write_phy_reg_mdi(hw, MVL_LEDFCR, 0, value);
+       ngbe_read_phy_reg_mdi(hw, MVL_LEDPCR, 0, &value);
+       value &= ~(MVL_LEDPCR_CTL0 | MVL_LEDPCR_CTL1);
+       value |= MVL_LEDPCR_CTL0_CONF | MVL_LEDPCR_CTL1_CONF;
+       ngbe_write_phy_reg_mdi(hw, MVL_LEDPCR, 0, value);
+
+       return ret_val;
+}
+
+s32 ngbe_setup_phy_link_mvl(struct ngbe_hw *hw, u32 speed,
+                               bool autoneg_wait_to_complete)
+{
+       u16 value_r4 = 0;
+       u16 value_r9 = 0;
+       u16 value;
+
+       DEBUGFUNC("ngbe_setup_phy_link_mvl");
+       UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
+
+       hw->phy.autoneg_advertised = 0;
+
+       if (hw->phy.type == ngbe_phy_mvl) {
+               if (speed & NGBE_LINK_SPEED_1GB_FULL) {
+                       value_r9 |= MVL_PHY_1000BASET_FULL;
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
+               }
+
+               if (speed & NGBE_LINK_SPEED_100M_FULL) {
+                       value_r4 |= MVL_PHY_100BASET_FULL;
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL;
+               }
+
+               if (speed & NGBE_LINK_SPEED_10M_FULL) {
+                       value_r4 |= MVL_PHY_10BASET_FULL;
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL;
+               }
+
+               hw->phy.read_reg(hw, MVL_ANA, 0, &value);
+               value &= ~(MVL_PHY_100BASET_FULL |
+                          MVL_PHY_100BASET_HALF |
+                          MVL_PHY_10BASET_FULL |
+                          MVL_PHY_10BASET_HALF);
+               value_r4 |= value;
+               hw->phy.write_reg(hw, MVL_ANA, 0, value_r4);
+
+               hw->phy.read_reg(hw, MVL_PHY_1000BASET, 0, &value);
+               value &= ~(MVL_PHY_1000BASET_FULL |
+                          MVL_PHY_1000BASET_HALF);
+               value_r9 |= value;
+               hw->phy.write_reg(hw, MVL_PHY_1000BASET, 0, value_r9);
+       } else {
+               hw->phy.autoneg_advertised = 1;
+
+               hw->phy.read_reg(hw, MVL_ANA, 0, &value);
+               value &= ~(MVL_PHY_1000BASEX_HALF | MVL_PHY_1000BASEX_FULL);
+               value |= MVL_PHY_1000BASEX_FULL;
+               hw->phy.write_reg(hw, MVL_ANA, 0, value);
+       }
+
+       value = MVL_CTRL_RESTART_AN | MVL_CTRL_ANE;
+       ngbe_write_phy_reg_mdi(hw, MVL_CTRL, 0, value);
+
+       hw->phy.read_reg(hw, MVL_INTR, 0, &value);
+
+       return 0;
+}
+
 s32 ngbe_reset_phy_mvl(struct ngbe_hw *hw)
 {
        u32 i;
@@ -87,3 +209,100 @@ s32 ngbe_reset_phy_mvl(struct ngbe_hw *hw)
        return status;
 }
 
+s32 ngbe_get_phy_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       if (hw->phy.type == ngbe_phy_mvl) {
+               status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
+               value &= MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE;
+               *pause_bit = (u8)(value >> 10);
+       } else {
+               status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
+               value &= MVL_FANA_PAUSE_MASK;
+               *pause_bit = (u8)(value >> 7);
+       }
+
+       return status;
+}
+
+s32 ngbe_get_phy_lp_advertised_pause_mvl(struct ngbe_hw *hw, u8 *pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       if (hw->phy.type == ngbe_phy_mvl) {
+               status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
+               value &= MVL_CLPAR_ASM_PAUSE | MVL_CLPAR_PAUSE;
+               *pause_bit = (u8)(value >> 10);
+       } else {
+               status = hw->phy.read_reg(hw, MVL_LPAR, 0, &value);
+               value &= MVL_FLPAR_PAUSE_MASK;
+               *pause_bit = (u8)(value >> 7);
+       }
+
+       return status;
+}
+
+s32 ngbe_set_phy_pause_adv_mvl(struct ngbe_hw *hw, u16 pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       DEBUGFUNC("ngbe_set_phy_pause_adv_mvl");
+
+       if (hw->phy.type == ngbe_phy_mvl) {
+               status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
+               value &= ~(MVL_CANA_ASM_PAUSE | MVL_CANA_PAUSE);
+       } else {
+               status = hw->phy.read_reg(hw, MVL_ANA, 0, &value);
+               value &= ~MVL_FANA_PAUSE_MASK;
+       }
+
+       value |= pause_bit;
+       status = hw->phy.write_reg(hw, MVL_ANA, 0, value);
+
+       return status;
+}
+
+s32 ngbe_check_phy_link_mvl(struct ngbe_hw *hw,
+               u32 *speed, bool *link_up)
+{
+       s32 status = 0;
+       u16 phy_link = 0;
+       u16 phy_speed = 0;
+       u16 phy_data = 0;
+       u16 insr = 0;
+
+       DEBUGFUNC("ngbe_check_phy_link_mvl");
+
+       /* Initialize speed and link to default case */
+       *link_up = false;
+       *speed = NGBE_LINK_SPEED_UNKNOWN;
+
+       hw->phy.read_reg(hw, MVL_INTR, 0, &insr);
+
+       /*
+        * Check current speed and link status of the PHY register.
+        * This is a vendor specific register and may have to
+        * be changed for other copper PHYs.
+        */
+       status = hw->phy.read_reg(hw, MVL_PHYSR, 0, &phy_data);
+       phy_link = phy_data & MVL_PHYSR_LINK;
+       phy_speed = phy_data & MVL_PHYSR_SPEED_MASK;
+
+       if (phy_link == MVL_PHYSR_LINK) {
+               *link_up = true;
+
+               if (phy_speed == MVL_PHYSR_SPEED_1000M)
+                       *speed = NGBE_LINK_SPEED_1GB_FULL;
+               else if (phy_speed == MVL_PHYSR_SPEED_100M)
+                       *speed = NGBE_LINK_SPEED_100M_FULL;
+               else if (phy_speed == MVL_PHYSR_SPEED_10M)
+                       *speed = NGBE_LINK_SPEED_10M_FULL;
+       }
+
+       return status;
+}
+