net/ngbe: fix external PHY power down
[dpdk.git] / drivers / net / ngbe / base / ngbe_phy_yt.c
index 4a44414..f46121b 100644 (file)
@@ -98,6 +98,19 @@ s32 ngbe_write_phy_reg_sds_ext_yt(struct ngbe_hw *hw,
        return 0;
 }
 
+s32 ngbe_init_phy_yt(struct ngbe_hw *hw)
+{
+       /* close sds area register */
+       ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, 0, 0);
+       /* enable interrupts */
+       ngbe_write_phy_reg_mdi(hw, YT_INTR, 0,
+                               YT_INTR_ENA_MASK | YT_SDS_INTR_ENA_MASK);
+
+       hw->phy.set_phy_power(hw, false);
+
+       return 0;
+}
+
 s32 ngbe_setup_phy_link_yt(struct ngbe_hw *hw, u32 speed,
                                bool autoneg_wait_to_complete)
 {
@@ -105,20 +118,48 @@ s32 ngbe_setup_phy_link_yt(struct ngbe_hw *hw, u32 speed,
        u16 value_r9 = 0;
        u16 value;
 
-       DEBUGFUNC("ngbe_setup_phy_link_yt");
        UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
 
        hw->phy.autoneg_advertised = 0;
 
-       if (hw->phy.type == ngbe_phy_yt8521s) {
+       /* check chip_mode first */
+       ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &value);
+       if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(0)) {
+               /* UTP to rgmii */
+               if (!hw->mac.autoneg) {
+                       switch (speed) {
+                       case NGBE_LINK_SPEED_1GB_FULL:
+                               value = YT_BCR_SPEED_SELECT1;
+                               break;
+                       case NGBE_LINK_SPEED_100M_FULL:
+                               value = YT_BCR_SPEED_SELECT0;
+                               break;
+                       case NGBE_LINK_SPEED_10M_FULL:
+                               value = 0;
+                               break;
+                       default:
+                               value = YT_BCR_SPEED_SELECT0 |
+                                       YT_BCR_SPEED_SELECT1;
+                               DEBUGOUT("unknown speed = 0x%x.",
+                                       speed);
+                               break;
+                       }
+                       /* duplex full */
+                       value |= YT_BCR_DUPLEX | YT_BCR_RESET;
+                       hw->phy.write_reg(hw, YT_BCR, 0, value);
+
+                       goto skip_an;
+               }
+
                /*disable 100/10base-T Self-negotiation ability*/
                hw->phy.read_reg(hw, YT_ANA, 0, &value);
-               value &= ~(YT_ANA_100BASET_FULL | YT_ANA_10BASET_FULL);
+               value &= ~(YT_ANA_100BASET_FULL | YT_ANA_100BASET_HALF |
+                       YT_ANA_10BASET_FULL | YT_ANA_10BASET_HALF);
                hw->phy.write_reg(hw, YT_ANA, 0, value);
 
                /*disable 1000base-T Self-negotiation ability*/
                hw->phy.read_reg(hw, YT_MS_CTRL, 0, &value);
-               value &= ~YT_MS_1000BASET_FULL;
+               value &= ~(YT_MS_1000BASET_FULL | YT_MS_1000BASET_HALF);
                hw->phy.write_reg(hw, YT_MS_CTRL, 0, value);
 
                if (speed & NGBE_LINK_SPEED_1GB_FULL) {
@@ -146,9 +187,12 @@ s32 ngbe_setup_phy_link_yt(struct ngbe_hw *hw, u32 speed,
 
                /* software reset to make the above configuration take effect*/
                hw->phy.read_reg(hw, YT_BCR, 0, &value);
-               value |= YT_BCR_RESET;
+               value |= YT_BCR_RESET | YT_BCR_ANE | YT_BCR_RESTART_AN;
                hw->phy.write_reg(hw, YT_BCR, 0, value);
-       } else {
+skip_an:
+               hw->phy.set_phy_power(hw, true);
+       } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(1)) {
+               /* fiber to rgmii */
                hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
 
                /* RGMII_Config1 : Config rx and tx training delay */
@@ -164,10 +208,79 @@ s32 ngbe_setup_phy_link_yt(struct ngbe_hw *hw, u32 speed,
                /* software reset */
                ngbe_write_phy_reg_sds_ext_yt(hw, 0x0, 0, 0x9140);
 
-               /* power on phy */
-               hw->phy.read_reg(hw, YT_BCR, 0, &value);
-               value &= ~YT_BCR_PWDN;
-               hw->phy.write_reg(hw, YT_BCR, 0, value);
+               hw->phy.set_phy_power(hw, true);
+       } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(2)) {
+               hw->phy.set_phy_power(hw, true);
+
+               hw->phy.read_reg(hw, YT_SPST, 0, &value);
+               if (value & YT_SPST_LINK) {
+                       /* fiber up */
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
+               } else {
+                       /* utp up */
+                       /*disable 100/10base-T Self-negotiation ability*/
+                       hw->phy.read_reg(hw, YT_ANA, 0, &value);
+                       value &= ~(YT_ANA_100BASET_FULL | YT_ANA_100BASET_HALF |
+                               YT_ANA_10BASET_FULL | YT_ANA_10BASET_HALF);
+                       hw->phy.write_reg(hw, YT_ANA, 0, value);
+
+                       /*disable 1000base-T Self-negotiation ability*/
+                       hw->phy.read_reg(hw, YT_MS_CTRL, 0, &value);
+                       value &= ~(YT_MS_1000BASET_FULL | YT_MS_1000BASET_HALF);
+                       hw->phy.write_reg(hw, YT_MS_CTRL, 0, value);
+
+                       if (speed & NGBE_LINK_SPEED_1GB_FULL) {
+                               hw->phy.autoneg_advertised |=
+                                               NGBE_LINK_SPEED_1GB_FULL;
+                               value_r9 |= YT_MS_1000BASET_FULL;
+                       }
+                       if (speed & NGBE_LINK_SPEED_100M_FULL) {
+                               hw->phy.autoneg_advertised |=
+                                               NGBE_LINK_SPEED_100M_FULL;
+                               value_r4 |= YT_ANA_100BASET_FULL;
+                       }
+                       if (speed & NGBE_LINK_SPEED_10M_FULL) {
+                               hw->phy.autoneg_advertised |=
+                                               NGBE_LINK_SPEED_10M_FULL;
+                               value_r4 |= YT_ANA_10BASET_FULL;
+                       }
+
+                       /* enable 1000base-T Self-negotiation ability */
+                       hw->phy.read_reg(hw, YT_MS_CTRL, 0, &value);
+                       value |= value_r9;
+                       hw->phy.write_reg(hw, YT_MS_CTRL, 0, value);
+
+                       /* enable 100/10base-T Self-negotiation ability */
+                       hw->phy.read_reg(hw, YT_ANA, 0, &value);
+                       value |= value_r4;
+                       hw->phy.write_reg(hw, YT_ANA, 0, value);
+
+                       /* software reset to make the above configuration
+                        * take effect
+                        */
+                       hw->phy.read_reg(hw, YT_BCR, 0, &value);
+                       value |= YT_BCR_RESET;
+                       hw->phy.write_reg(hw, YT_BCR, 0, value);
+               }
+       } else if ((value & YT_CHIP_MODE_MASK) == YT_CHIP_MODE_SEL(4)) {
+               hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
+
+               ngbe_read_phy_reg_ext_yt(hw, YT_RGMII_CONF1, 0, &value);
+               value |= YT_RGMII_CONF1_MODE;
+               ngbe_write_phy_reg_ext_yt(hw, YT_RGMII_CONF1, 0, value);
+
+               ngbe_read_phy_reg_ext_yt(hw, YT_RGMII_CONF2, 0, &value);
+               value &= ~(YT_RGMII_CONF2_SPEED_MASK | YT_RGMII_CONF2_DUPLEX |
+                       YT_RGMII_CONF2_LINKUP);
+               value |= YT_RGMII_CONF2_SPEED(2) | YT_RGMII_CONF2_DUPLEX |
+                       YT_RGMII_CONF2_LINKUP;
+               ngbe_write_phy_reg_ext_yt(hw, YT_RGMII_CONF2, 0, value);
+
+               ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &value);
+               value &= ~YT_SMI_PHY_SW_RST;
+               ngbe_write_phy_reg_ext_yt(hw, YT_CHIP, 0, value);
+
+               hw->phy.set_phy_power(hw, true);
        }
 
        ngbe_write_phy_reg_ext_yt(hw, YT_SMI_PHY, 0, 0);
@@ -182,32 +295,85 @@ s32 ngbe_reset_phy_yt(struct ngbe_hw *hw)
        u16 ctrl = 0;
        s32 status = 0;
 
-       DEBUGFUNC("ngbe_reset_phy_yt");
-
        if (hw->phy.type != ngbe_phy_yt8521s &&
                hw->phy.type != ngbe_phy_yt8521s_sfi)
                return NGBE_ERR_PHY_TYPE;
 
-       status = hw->phy.read_reg(hw, YT_BCR, 0, &ctrl);
-       /* sds software reset */
-       ctrl |= YT_BCR_RESET;
-       status = hw->phy.write_reg(hw, YT_BCR, 0, ctrl);
-
-       for (i = 0; i < YT_PHY_RST_WAIT_PERIOD; i++) {
+       /* check chip_mode first */
+       ngbe_read_phy_reg_ext_yt(hw, YT_CHIP, 0, &ctrl);
+       if (ctrl & YT_CHIP_MODE_MASK) {
+               /* fiber to rgmii */
                status = hw->phy.read_reg(hw, YT_BCR, 0, &ctrl);
-               if (!(ctrl & YT_BCR_RESET))
-                       break;
-               msleep(1);
+               /* sds software reset */
+               ctrl |= YT_BCR_RESET;
+               status = hw->phy.write_reg(hw, YT_BCR, 0, ctrl);
+
+               for (i = 0; i < YT_PHY_RST_WAIT_PERIOD; i++) {
+                       status = hw->phy.read_reg(hw, YT_BCR, 0, &ctrl);
+                       if (!(ctrl & YT_BCR_RESET))
+                               break;
+                       msleep(1);
+               }
+       } else {
+               /* UTP to rgmii */
+               status = ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &ctrl);
+               /* sds software reset */
+               ctrl |= YT_BCR_RESET;
+               status = ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, ctrl);
+
+               for (i = 0; i < YT_PHY_RST_WAIT_PERIOD; i++) {
+                       status = ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &ctrl);
+                       if (!(ctrl & YT_BCR_RESET))
+                               break;
+                       msleep(1);
+               }
        }
 
        if (i == YT_PHY_RST_WAIT_PERIOD) {
-               DEBUGOUT("PHY reset polling failed to complete.\n");
+               DEBUGOUT("PHY reset polling failed to complete.");
                return NGBE_ERR_RESET_FAILED;
        }
 
        return status;
 }
 
+s32 ngbe_get_phy_advertised_pause_yt(struct ngbe_hw *hw, u8 *pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       status = hw->phy.read_reg(hw, YT_ANA, 0, &value);
+       value &= YT_FANA_PAUSE_MASK;
+       *pause_bit = (u8)(value >> 7);
+
+       return status;
+}
+
+s32 ngbe_get_phy_lp_advertised_pause_yt(struct ngbe_hw *hw, u8 *pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       status = hw->phy.read_reg(hw, YT_LPAR, 0, &value);
+       value &= YT_FLPAR_PAUSE_MASK;
+       *pause_bit = (u8)(value >> 7);
+
+       return status;
+}
+
+s32 ngbe_set_phy_pause_adv_yt(struct ngbe_hw *hw, u16 pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       status = hw->phy.read_reg(hw, YT_ANA, 0, &value);
+       value &= ~YT_FANA_PAUSE_MASK;
+       value |= pause_bit;
+       status = hw->phy.write_reg(hw, YT_ANA, 0, value);
+
+       return status;
+}
+
 s32 ngbe_check_phy_link_yt(struct ngbe_hw *hw,
                u32 *speed, bool *link_up)
 {
@@ -217,8 +383,6 @@ s32 ngbe_check_phy_link_yt(struct ngbe_hw *hw,
        u16 phy_data = 0;
        u16 insr = 0;
 
-       DEBUGFUNC("ngbe_check_phy_link_yt");
-
        /* Initialize speed and link to default case */
        *link_up = false;
        *speed = NGBE_LINK_SPEED_UNKNOWN;
@@ -244,3 +408,26 @@ s32 ngbe_check_phy_link_yt(struct ngbe_hw *hw,
        return status;
 }
 
+s32 ngbe_set_phy_power_yt(struct ngbe_hw *hw, bool on)
+{
+       u16 value = 0;
+
+       /* power down/up in fiber mode */
+       hw->phy.read_reg(hw, YT_BCR, 0, &value);
+       if (on)
+               value &= ~YT_BCR_PWDN;
+       else
+               value |= YT_BCR_PWDN;
+       hw->phy.write_reg(hw, YT_BCR, 0, value);
+
+       value = 0;
+       /* power down/up in UTP mode */
+       ngbe_read_phy_reg_mdi(hw, YT_BCR, 0, &value);
+       if (on)
+               value &= ~YT_BCR_PWDN;
+       else
+               value |= YT_BCR_PWDN;
+       ngbe_write_phy_reg_mdi(hw, YT_BCR, 0, value);
+
+       return 0;
+}