net/ngbe: fix external PHY power down
[dpdk.git] / drivers / net / ngbe / base / ngbe_phy_rtl.c
index 400fbe8..3a2d624 100644 (file)
@@ -4,8 +4,6 @@
 
 #include "ngbe_phy_rtl.h"
 
-#define RTL_PHY_RST_WAIT_PERIOD               5
-
 s32 ngbe_read_phy_reg_rtl(struct ngbe_hw *hw,
                u32 reg_addr, u32 device_type, u16 *phy_data)
 {
@@ -38,26 +36,302 @@ s32 ngbe_write_phy_reg_rtl(struct ngbe_hw *hw,
        return 0;
 }
 
+static void ngbe_phy_led_ctrl_rtl(struct ngbe_hw *hw)
+{
+       u16 value = 0;
+
+       if (hw->led_conf != 0xFFFF)
+               value = hw->led_conf & 0xFFFF;
+       else
+               value = 0x205B;
+
+       hw->phy.write_reg(hw, RTL_LCR, 0xd04, value);
+       hw->phy.write_reg(hw, RTL_EEELCR, 0xd04, 0);
+
+       hw->phy.read_reg(hw, RTL_LPCR, 0xd04, &value);
+       if (hw->led_conf != 0xFFFF) {
+               value &= ~0x73;
+               value |= hw->led_conf >> 16;
+       } else {
+               value &= 0xFFFC;
+               /*act led blinking mode set to 60ms*/
+               value |= 0x2;
+       }
+       hw->phy.write_reg(hw, RTL_LPCR, 0xd04, value);
+}
+
+s32 ngbe_init_phy_rtl(struct ngbe_hw *hw)
+{
+       int i;
+       u16 value = 0;
+
+       /* enable interrupts, only link status change and an done is allowed */
+       value = RTL_INER_LSC | RTL_INER_ANC;
+       hw->phy.write_reg(hw, RTL_INER, 0xa42, value);
+
+       hw->phy.read_reg(hw, RTL_INSR, 0xa43, &value);
+
+       for (i = 0; i < 15; i++) {
+               if (!rd32m(hw, NGBE_STAT,
+                       NGBE_STAT_GPHY_IN_RST(hw->bus.lan_id)))
+                       break;
+
+               msec_delay(10);
+       }
+       if (i == 15) {
+               DEBUGOUT("GPhy reset exceeds maximum times.");
+               return NGBE_ERR_PHY_TIMEOUT;
+       }
+
+       hw->phy.write_reg(hw, RTL_SCR, 0xa46, RTL_SCR_EFUSE);
+       hw->phy.read_reg(hw, RTL_SCR, 0xa46, &value);
+       if (!(value & RTL_SCR_EFUSE)) {
+               DEBUGOUT("Write EFUSE failed.");
+               return NGBE_ERR_PHY_TIMEOUT;
+       }
+
+       for (i = 0; i < 1000; i++) {
+               hw->phy.read_reg(hw, RTL_INSR, 0xa43, &value);
+               if (value & RTL_INSR_ACCESS)
+                       break;
+               msec_delay(1);
+       }
+       if (i == 1000)
+               DEBUGOUT("PHY wait mdio 1 access timeout.");
+
+
+       hw->phy.write_reg(hw, RTL_SCR, 0xa46, RTL_SCR_EXTINI);
+       hw->phy.read_reg(hw, RTL_SCR, 0xa46, &value);
+       if (!(value & RTL_SCR_EXTINI)) {
+               DEBUGOUT("Write EXIINI failed.");
+               return NGBE_ERR_PHY_TIMEOUT;
+       }
+
+       for (i = 0; i < 1000; i++) {
+               hw->phy.read_reg(hw, RTL_INSR, 0xa43, &value);
+               if (value & RTL_INSR_ACCESS)
+                       break;
+               msec_delay(1);
+       }
+       if (i == 1000)
+               DEBUGOUT("PHY wait mdio 2 access timeout.");
+
+       for (i = 0; i < 1000; i++) {
+               hw->phy.read_reg(hw, RTL_GSR, 0xa42, &value);
+               if ((value & RTL_GSR_ST) == RTL_GSR_ST_LANON)
+                       break;
+               msec_delay(1);
+       }
+       if (i == 1000)
+               return NGBE_ERR_PHY_TIMEOUT;
+
+       return 0;
+}
+
+/**
+ *  ngbe_setup_phy_link_rtl - Set and restart auto-neg
+ *  @hw: pointer to hardware structure
+ *
+ *  Restart auto-negotiation and PHY and waits for completion.
+ **/
+s32 ngbe_setup_phy_link_rtl(struct ngbe_hw *hw,
+               u32 speed, bool autoneg_wait_to_complete)
+{
+       u16 autoneg_reg = NGBE_MII_AUTONEG_REG;
+       u16 value = 0;
+
+       UNREFERENCED_PARAMETER(autoneg_wait_to_complete);
+
+       hw->phy.read_reg(hw, RTL_INSR, 0xa43, &autoneg_reg);
+
+       if (!hw->mac.autoneg) {
+               hw->phy.reset_hw(hw);
+
+               switch (speed) {
+               case NGBE_LINK_SPEED_1GB_FULL:
+                       value = RTL_BMCR_SPEED_SELECT1;
+                       break;
+               case NGBE_LINK_SPEED_100M_FULL:
+                       value = RTL_BMCR_SPEED_SELECT0;
+                       break;
+               case NGBE_LINK_SPEED_10M_FULL:
+                       value = 0;
+                       break;
+               default:
+                       value = RTL_BMCR_SPEED_SELECT1 | RTL_BMCR_SPEED_SELECT0;
+                       DEBUGOUT("unknown speed = 0x%x.", speed);
+                       break;
+               }
+               /* duplex full */
+               value |= RTL_BMCR_DUPLEX;
+               hw->phy.write_reg(hw, RTL_BMCR, RTL_DEV_ZERO, value);
+
+               goto skip_an;
+       }
+
+       /*
+        * Clear autoneg_advertised and set new values based on input link
+        * speed.
+        */
+       if (speed) {
+               hw->phy.autoneg_advertised = 0;
+
+               if (speed & NGBE_LINK_SPEED_1GB_FULL)
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_1GB_FULL;
+
+               if (speed & NGBE_LINK_SPEED_100M_FULL)
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_100M_FULL;
+
+               if (speed & NGBE_LINK_SPEED_10M_FULL)
+                       hw->phy.autoneg_advertised |= NGBE_LINK_SPEED_10M_FULL;
+       }
+
+       /* disable 10/100M Half Duplex */
+       hw->phy.read_reg(hw, RTL_ANAR, RTL_DEV_ZERO, &autoneg_reg);
+       autoneg_reg &= 0xFF5F;
+       hw->phy.write_reg(hw, RTL_ANAR, RTL_DEV_ZERO, autoneg_reg);
+
+       /* set advertise enable according to input speed */
+       if (!(speed & NGBE_LINK_SPEED_1GB_FULL)) {
+               hw->phy.read_reg(hw, RTL_GBCR,
+                       RTL_DEV_ZERO, &autoneg_reg);
+               autoneg_reg &= ~RTL_GBCR_1000F;
+               hw->phy.write_reg(hw, RTL_GBCR,
+                       RTL_DEV_ZERO, autoneg_reg);
+       } else {
+               hw->phy.read_reg(hw, RTL_GBCR,
+                       RTL_DEV_ZERO, &autoneg_reg);
+               autoneg_reg |= RTL_GBCR_1000F;
+               hw->phy.write_reg(hw, RTL_GBCR,
+                       RTL_DEV_ZERO, autoneg_reg);
+       }
+
+       if (!(speed & NGBE_LINK_SPEED_100M_FULL)) {
+               hw->phy.read_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, &autoneg_reg);
+               autoneg_reg &= ~RTL_ANAR_100F;
+               autoneg_reg &= ~RTL_ANAR_100H;
+               hw->phy.write_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, autoneg_reg);
+       } else {
+               hw->phy.read_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, &autoneg_reg);
+               autoneg_reg |= RTL_ANAR_100F;
+               hw->phy.write_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, autoneg_reg);
+       }
+
+       if (!(speed & NGBE_LINK_SPEED_10M_FULL)) {
+               hw->phy.read_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, &autoneg_reg);
+               autoneg_reg &= ~RTL_ANAR_10F;
+               autoneg_reg &= ~RTL_ANAR_10H;
+               hw->phy.write_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, autoneg_reg);
+       } else {
+               hw->phy.read_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, &autoneg_reg);
+               autoneg_reg |= RTL_ANAR_10F;
+               hw->phy.write_reg(hw, RTL_ANAR,
+                       RTL_DEV_ZERO, autoneg_reg);
+       }
+
+       /* restart AN and wait AN done interrupt */
+       autoneg_reg = RTL_BMCR_RESTART_AN | RTL_BMCR_ANE;
+       hw->phy.write_reg(hw, RTL_BMCR, RTL_DEV_ZERO, autoneg_reg);
+
+skip_an:
+       ngbe_phy_led_ctrl_rtl(hw);
+
+       return 0;
+}
+
 s32 ngbe_reset_phy_rtl(struct ngbe_hw *hw)
 {
-       u16 value = 0, i;
+       u16 value = 0;
        s32 status = 0;
 
-       DEBUGFUNC("ngbe_reset_phy_rtl");
-
        value |= RTL_BMCR_RESET;
        status = hw->phy.write_reg(hw, RTL_BMCR, RTL_DEV_ZERO, value);
 
-       for (i = 0; i < RTL_PHY_RST_WAIT_PERIOD; i++) {
-               status = hw->phy.read_reg(hw, RTL_BMCR, RTL_DEV_ZERO, &value);
-               if (!(value & RTL_BMCR_RESET))
-                       break;
-               msleep(1);
-       }
+       msec_delay(5);
+
+       return status;
+}
+
+s32 ngbe_get_phy_advertised_pause_rtl(struct ngbe_hw *hw, u8 *pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       status = hw->phy.read_reg(hw, RTL_ANAR, RTL_DEV_ZERO, &value);
+       value &= RTL_ANAR_APAUSE | RTL_ANAR_PAUSE;
+       *pause_bit = (u8)(value >> 10);
+       return status;
+}
+
+s32 ngbe_get_phy_lp_advertised_pause_rtl(struct ngbe_hw *hw, u8 *pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       status = hw->phy.read_reg(hw, RTL_INSR, 0xa43, &value);
+
+       status = hw->phy.read_reg(hw, RTL_BMSR, RTL_DEV_ZERO, &value);
+       value = value & RTL_BMSR_ANC;
+
+       /* if AN complete then check lp adv pause */
+       status = hw->phy.read_reg(hw, RTL_ANLPAR, RTL_DEV_ZERO, &value);
+       value &= RTL_ANLPAR_LP;
+       *pause_bit = (u8)(value >> 10);
+       return status;
+}
+
+s32 ngbe_set_phy_pause_adv_rtl(struct ngbe_hw *hw, u16 pause_bit)
+{
+       u16 value;
+       s32 status = 0;
+
+       status = hw->phy.read_reg(hw, RTL_ANAR, RTL_DEV_ZERO, &value);
+       value &= ~(RTL_ANAR_APAUSE | RTL_ANAR_PAUSE);
+       value |= pause_bit;
+
+       status = hw->phy.write_reg(hw, RTL_ANAR, RTL_DEV_ZERO, value);
+
+       return status;
+}
+
+s32 ngbe_check_phy_link_rtl(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;
+
+       hw->phy.read_reg(hw, RTL_INSR, 0xa43, &insr);
+
+       /* Initialize speed and link to default case */
+       *link_up = false;
+       *speed = NGBE_LINK_SPEED_UNKNOWN;
+
+       /*
+        * 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, RTL_PHYSR, 0xa43, &phy_data);
+       phy_link = phy_data & RTL_PHYSR_RTLS;
+       phy_speed = phy_data & (RTL_PHYSR_SPEED_MASK | RTL_PHYSR_DP);
+       if (phy_link == RTL_PHYSR_RTLS) {
+               *link_up = true;
 
-       if (i == RTL_PHY_RST_WAIT_PERIOD) {
-               DEBUGOUT("PHY reset polling failed to complete.\n");
-               return NGBE_ERR_RESET_FAILED;
+               if (phy_speed == (RTL_PHYSR_SPEED_1000M | RTL_PHYSR_DP))
+                       *speed = NGBE_LINK_SPEED_1GB_FULL;
+               else if (phy_speed == (RTL_PHYSR_SPEED_100M | RTL_PHYSR_DP))
+                       *speed = NGBE_LINK_SPEED_100M_FULL;
+               else if (phy_speed == (RTL_PHYSR_SPEED_10M | RTL_PHYSR_DP))
+                       *speed = NGBE_LINK_SPEED_10M_FULL;
        }
 
        return status;