bnx2x: Added EEE support
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / net / ethernet / broadcom / bnx2x / bnx2x_link.c
index a3fb7215cd8910846606e8b9ddeaee7ba0c28b89..c7c814db027da01df111ffac5e278a9ffe11d30b 100644 (file)
@@ -1305,6 +1305,94 @@ int bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos)
 
        return 0;
 }
+
+/******************************************************************/
+/*                     EEE section                                */
+/******************************************************************/
+static u8 bnx2x_eee_has_cap(struct link_params *params)
+{
+       struct bnx2x *bp = params->bp;
+
+       if (REG_RD(bp, params->shmem2_base) <=
+                  offsetof(struct shmem2_region, eee_status[params->port]))
+               return 0;
+
+       return 1;
+}
+
+static int bnx2x_eee_nvram_to_time(u32 nvram_mode, u32 *idle_timer)
+{
+       switch (nvram_mode) {
+       case PORT_FEAT_CFG_EEE_POWER_MODE_BALANCED:
+               *idle_timer = EEE_MODE_NVRAM_BALANCED_TIME;
+               break;
+       case PORT_FEAT_CFG_EEE_POWER_MODE_AGGRESSIVE:
+               *idle_timer = EEE_MODE_NVRAM_AGGRESSIVE_TIME;
+               break;
+       case PORT_FEAT_CFG_EEE_POWER_MODE_LOW_LATENCY:
+               *idle_timer = EEE_MODE_NVRAM_LATENCY_TIME;
+               break;
+       default:
+               *idle_timer = 0;
+               break;
+       }
+
+       return 0;
+}
+
+static int bnx2x_eee_time_to_nvram(u32 idle_timer, u32 *nvram_mode)
+{
+       switch (idle_timer) {
+       case EEE_MODE_NVRAM_BALANCED_TIME:
+               *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_BALANCED;
+               break;
+       case EEE_MODE_NVRAM_AGGRESSIVE_TIME:
+               *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_AGGRESSIVE;
+               break;
+       case EEE_MODE_NVRAM_LATENCY_TIME:
+               *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_LOW_LATENCY;
+               break;
+       default:
+               *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_DISABLED;
+               break;
+       }
+
+       return 0;
+}
+
+static u32 bnx2x_eee_calc_timer(struct link_params *params)
+{
+       u32 eee_mode, eee_idle;
+       struct bnx2x *bp = params->bp;
+
+       if (params->eee_mode & EEE_MODE_OVERRIDE_NVRAM) {
+               if (params->eee_mode & EEE_MODE_OUTPUT_TIME) {
+                       /* time value in eee_mode --> used directly*/
+                       eee_idle = params->eee_mode & EEE_MODE_TIMER_MASK;
+               } else {
+                       /* hsi value in eee_mode --> time */
+                       if (bnx2x_eee_nvram_to_time(params->eee_mode &
+                                                   EEE_MODE_NVRAM_MASK,
+                                                   &eee_idle))
+                               return 0;
+               }
+       } else {
+               /* hsi values in nvram --> time*/
+               eee_mode = ((REG_RD(bp, params->shmem_base +
+                                   offsetof(struct shmem_region, dev_info.
+                                   port_feature_config[params->port].
+                                   eee_power_mode)) &
+                            PORT_FEAT_CFG_EEE_POWER_MODE_MASK) >>
+                           PORT_FEAT_CFG_EEE_POWER_MODE_SHIFT);
+
+               if (bnx2x_eee_nvram_to_time(eee_mode, &eee_idle))
+                       return 0;
+       }
+
+       return eee_idle;
+}
+
+
 /******************************************************************/
 /*                     PFC section                               */
 /******************************************************************/
@@ -1729,6 +1817,14 @@ static int bnx2x_xmac_enable(struct link_params *params,
        /* update PFC */
        bnx2x_update_pfc_xmac(params, vars, 0);
 
+       if (vars->eee_status & SHMEM_EEE_ADV_STATUS_MASK) {
+               DP(NETIF_MSG_LINK, "Setting XMAC for EEE\n");
+               REG_WR(bp, xmac_base + XMAC_REG_EEE_TIMERS_HI, 0x1380008);
+               REG_WR(bp, xmac_base + XMAC_REG_EEE_CTRL, 0x1);
+       } else {
+               REG_WR(bp, xmac_base + XMAC_REG_EEE_CTRL, 0x0);
+       }
+
        /* Enable TX and RX */
        val = XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN;
 
@@ -2439,6 +2535,16 @@ static void bnx2x_update_mng(struct link_params *params, u32 link_status)
                        port_mb[params->port].link_status), link_status);
 }
 
+static void bnx2x_update_mng_eee(struct link_params *params, u32 eee_status)
+{
+       struct bnx2x *bp = params->bp;
+
+       if (bnx2x_eee_has_cap(params))
+               REG_WR(bp, params->shmem2_base +
+                      offsetof(struct shmem2_region,
+                               eee_status[params->port]), eee_status);
+}
+
 static void bnx2x_update_pfc_nig(struct link_params *params,
                struct link_vars *vars,
                struct bnx2x_nig_brb_pfc_port_params *nig_params)
@@ -3950,6 +4056,20 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy,
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                         MDIO_WC_REG_DIGITAL4_MISC3, val | 0x8080);
 
+       /* Enable LPI pass through */
+       if ((params->eee_mode & EEE_MODE_ADV_LPI) &&
+           (phy->flags & FLAGS_EEE_10GBT) &&
+           (!(params->eee_mode & EEE_MODE_ENABLE_LPI) ||
+             bnx2x_eee_calc_timer(params)) &&
+           (params->req_duplex[bnx2x_phy_selection(params)] == DUPLEX_FULL)) {
+               DP(NETIF_MSG_LINK, "Configure WC for LPI pass through\n");
+               bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
+                                MDIO_WC_REG_EEE_COMBO_CONTROL0,
+                                0x7c);
+               bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD,
+                                        MDIO_WC_REG_DIGITAL4_MISC5, 0xc000);
+       }
+
        /* 10G XFI Full Duplex */
        bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                         MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x100);
@@ -6462,6 +6582,15 @@ static int bnx2x_update_link_down(struct link_params *params,
               (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
        }
        if (CHIP_IS_E3(bp)) {
+               REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 + (params->port << 2),
+                      0);
+               REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 0);
+               REG_WR(bp, MISC_REG_CPMU_LP_MASK_ENT_P0 + (params->port << 2),
+                      0);
+               vars->eee_status &= ~(SHMEM_EEE_LP_ADV_STATUS_MASK |
+                                     SHMEM_EEE_ACTIVE_BIT);
+
+               bnx2x_update_mng_eee(params, vars->eee_status);
                bnx2x_xmac_disable(params);
                bnx2x_umac_disable(params);
        }
@@ -6501,6 +6630,16 @@ static int bnx2x_update_link_up(struct link_params *params,
                        bnx2x_umac_enable(params, vars, 0);
                bnx2x_set_led(params, vars,
                              LED_MODE_OPER, vars->line_speed);
+
+               if ((vars->eee_status & SHMEM_EEE_ACTIVE_BIT) &&
+                   (vars->eee_status & SHMEM_EEE_LPI_REQUESTED_BIT)) {
+                       DP(NETIF_MSG_LINK, "Enabling LPI assertion\n");
+                       REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 +
+                              (params->port << 2), 1);
+                       REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 1);
+                       REG_WR(bp, MISC_REG_CPMU_LP_MASK_ENT_P0 +
+                              (params->port << 2), 0xfc20);
+               }
        }
        if ((CHIP_IS_E1x(bp) ||
             CHIP_IS_E2(bp))) {
@@ -6538,7 +6677,7 @@ static int bnx2x_update_link_up(struct link_params *params,
 
        /* update shared memory */
        bnx2x_update_mng(params, vars->link_status);
-
+       bnx2x_update_mng_eee(params, vars->eee_status);
        /* Check remote fault */
        for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) {
                if (params->phy[phy_idx].flags & FLAGS_TX_ERROR_CHECK) {
@@ -6582,6 +6721,8 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars)
                phy_vars[phy_index].phy_link_up = 0;
                phy_vars[phy_index].link_up = 0;
                phy_vars[phy_index].fault_detected = 0;
+               /* different consideration, since vars holds inner state */
+               phy_vars[phy_index].eee_status = vars->eee_status;
        }
 
        if (USES_WARPCORE(bp))
@@ -6711,6 +6852,9 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars)
                        vars->link_status |= LINK_STATUS_SERDES_LINK;
                else
                        vars->link_status &= ~LINK_STATUS_SERDES_LINK;
+
+               vars->eee_status = phy_vars[active_external_phy].eee_status;
+
                DP(NETIF_MSG_LINK, "Active external phy selected: %x\n",
                           active_external_phy);
        }
@@ -9579,9 +9723,9 @@ static int bnx2x_8481_config_init(struct bnx2x_phy *phy,
 static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy,
                                   struct link_params *params,
                   u16 fw_cmd,
-                  u16 cmd_args[])
+                  u16 cmd_args[], int argc)
 {
-       u32 idx;
+       int idx;
        u16 val;
        struct bnx2x *bp = params->bp;
        /* Write CMD_OPEN_OVERRIDE to STATUS reg */
@@ -9601,7 +9745,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy,
        }
 
        /* Prepare argument(s) and issue command */
-       for (idx = 0; idx < PHY84833_CMDHDLR_MAX_ARGS; idx++) {
+       for (idx = 0; idx < argc; idx++) {
                bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD,
                                MDIO_84833_CMD_HDLR_DATA1 + idx,
                                cmd_args[idx]);
@@ -9622,7 +9766,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy,
                return -EINVAL;
        }
        /* Gather returning data */
-       for (idx = 0; idx < PHY84833_CMDHDLR_MAX_ARGS; idx++) {
+       for (idx = 0; idx < argc; idx++) {
                bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
                                MDIO_84833_CMD_HDLR_DATA1 + idx,
                                &cmd_args[idx]);
@@ -9656,7 +9800,7 @@ static int bnx2x_84833_pair_swap_cfg(struct bnx2x_phy *phy,
        data[1] = (u16)pair_swap;
 
        status = bnx2x_84833_cmd_hdlr(phy, params,
-               PHY84833_CMD_SET_PAIR_SWAP, data);
+               PHY84833_CMD_SET_PAIR_SWAP, data, PHY84833_CMDHDLR_MAX_ARGS);
        if (status == 0)
                DP(NETIF_MSG_LINK, "Pairswap OK, val=0x%x\n", data[1]);
 
@@ -9734,6 +9878,95 @@ static int bnx2x_84833_hw_reset_phy(struct bnx2x_phy *phy,
        return 0;
 }
 
+static int bnx2x_8483x_eee_timers(struct link_params *params,
+                                  struct link_vars *vars)
+{
+       u32 eee_idle = 0, eee_mode;
+       struct bnx2x *bp = params->bp;
+
+       eee_idle = bnx2x_eee_calc_timer(params);
+
+       if (eee_idle) {
+               REG_WR(bp, MISC_REG_CPMU_LP_IDLE_THR_P0 + (params->port << 2),
+                      eee_idle);
+       } else if ((params->eee_mode & EEE_MODE_ENABLE_LPI) &&
+                  (params->eee_mode & EEE_MODE_OVERRIDE_NVRAM) &&
+                  (params->eee_mode & EEE_MODE_OUTPUT_TIME)) {
+               DP(NETIF_MSG_LINK, "Error: Tx LPI is enabled with timer 0\n");
+               return -EINVAL;
+       }
+
+       vars->eee_status &= ~(SHMEM_EEE_TIMER_MASK | SHMEM_EEE_TIME_OUTPUT_BIT);
+       if (params->eee_mode & EEE_MODE_OUTPUT_TIME) {
+               /* eee_idle in 1u --> eee_status in 16u */
+               eee_idle >>= 4;
+               vars->eee_status |= (eee_idle & SHMEM_EEE_TIMER_MASK) |
+                                   SHMEM_EEE_TIME_OUTPUT_BIT;
+       } else {
+               if (bnx2x_eee_time_to_nvram(eee_idle, &eee_mode))
+                       return -EINVAL;
+               vars->eee_status |= eee_mode;
+       }
+
+       return 0;
+}
+
+static int bnx2x_8483x_disable_eee(struct bnx2x_phy *phy,
+                                  struct link_params *params,
+                                  struct link_vars *vars)
+{
+       int rc;
+       struct bnx2x *bp = params->bp;
+       u16 cmd_args = 0;
+
+       DP(NETIF_MSG_LINK, "Don't Advertise 10GBase-T EEE\n");
+
+       /* Make Certain LPI is disabled */
+       REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 + (params->port << 2), 0);
+       REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 0);
+
+       /* Prevent Phy from working in EEE and advertising it */
+       rc = bnx2x_84833_cmd_hdlr(phy, params,
+               PHY84833_CMD_SET_EEE_MODE, &cmd_args, 1);
+       if (rc != 0) {
+               DP(NETIF_MSG_LINK, "EEE disable failed.\n");
+               return rc;
+       }
+
+       bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_EEE_ADV, 0);
+       vars->eee_status &= ~SHMEM_EEE_ADV_STATUS_MASK;
+
+       return 0;
+}
+
+static int bnx2x_8483x_enable_eee(struct bnx2x_phy *phy,
+                                  struct link_params *params,
+                                  struct link_vars *vars)
+{
+       int rc;
+       struct bnx2x *bp = params->bp;
+       u16 cmd_args = 1;
+
+       DP(NETIF_MSG_LINK, "Advertise 10GBase-T EEE\n");
+
+       rc = bnx2x_84833_cmd_hdlr(phy, params,
+               PHY84833_CMD_SET_EEE_MODE, &cmd_args, 1);
+       if (rc != 0) {
+               DP(NETIF_MSG_LINK, "EEE enable failed.\n");
+               return rc;
+       }
+
+       bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_EEE_ADV, 0x8);
+
+       /* Mask events preventing LPI generation */
+       REG_WR(bp, MISC_REG_CPMU_LP_MASK_EXT_P0 + (params->port << 2), 0xfc20);
+
+       vars->eee_status &= ~SHMEM_EEE_ADV_STATUS_MASK;
+       vars->eee_status |= (SHMEM_EEE_10G_ADV << SHMEM_EEE_ADV_STATUS_SHIFT);
+
+       return 0;
+}
+
 #define PHY84833_CONSTANT_LATENCY 1193
 static int bnx2x_848x3_config_init(struct bnx2x_phy *phy,
                                   struct link_params *params,
@@ -9833,7 +10066,8 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy,
                cmd_args[2] = PHY84833_CONSTANT_LATENCY + 1;
                cmd_args[3] = PHY84833_CONSTANT_LATENCY;
                rc = bnx2x_84833_cmd_hdlr(phy, params,
-                       PHY84833_CMD_SET_EEE_MODE, cmd_args);
+                       PHY84833_CMD_SET_EEE_MODE, cmd_args,
+                       PHY84833_CMDHDLR_MAX_ARGS);
                if (rc != 0)
                        DP(NETIF_MSG_LINK, "Cfg AutogrEEEn failed.\n");
        }
@@ -9858,6 +10092,48 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy,
                                 MDIO_CTL_REG_84823_USER_CTRL_REG, val);
        }
 
+       bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD,
+                       MDIO_84833_TOP_CFG_FW_REV, &val);
+
+       /* Configure EEE support */
+       if ((val >= MDIO_84833_TOP_CFG_FW_EEE) && bnx2x_eee_has_cap(params)) {
+               phy->flags |= FLAGS_EEE_10GBT;
+               vars->eee_status |= SHMEM_EEE_10G_ADV <<
+                                   SHMEM_EEE_SUPPORTED_SHIFT;
+               /* Propogate params' bits --> vars (for migration exposure) */
+               if (params->eee_mode & EEE_MODE_ENABLE_LPI)
+                       vars->eee_status |= SHMEM_EEE_LPI_REQUESTED_BIT;
+               else
+                       vars->eee_status &= ~SHMEM_EEE_LPI_REQUESTED_BIT;
+
+               if (params->eee_mode & EEE_MODE_ADV_LPI)
+                       vars->eee_status |= SHMEM_EEE_REQUESTED_BIT;
+               else
+                       vars->eee_status &= ~SHMEM_EEE_REQUESTED_BIT;
+
+               rc = bnx2x_8483x_eee_timers(params, vars);
+               if (rc != 0) {
+                       DP(NETIF_MSG_LINK, "Failed to configure EEE timers\n");
+                       bnx2x_8483x_disable_eee(phy, params, vars);
+                       return rc;
+               }
+
+               if ((params->req_duplex[actual_phy_selection] == DUPLEX_FULL) &&
+                   (params->eee_mode & EEE_MODE_ADV_LPI) &&
+                   (bnx2x_eee_calc_timer(params) ||
+                    !(params->eee_mode & EEE_MODE_ENABLE_LPI)))
+                       rc = bnx2x_8483x_enable_eee(phy, params, vars);
+               else
+                       rc = bnx2x_8483x_disable_eee(phy, params, vars);
+               if (rc != 0) {
+                       DP(NETIF_MSG_LINK, "Failed to set EEE advertisment\n");
+                       return rc;
+               }
+       } else {
+               phy->flags &= ~FLAGS_EEE_10GBT;
+               vars->eee_status &= ~SHMEM_EEE_SUPPORTED_MASK;
+       }
+
        if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) {
                /* Bring PHY out of super isolate mode as the final step. */
                bnx2x_cl45_read(bp, phy,
@@ -9989,6 +10265,31 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy,
                if (val & (1<<11))
                        vars->link_status |=
                                LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE;
+
+               /* Determine if EEE was negotiated */
+               if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) {
+                       u32 eee_shmem = 0;
+
+                       bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD,
+                                       MDIO_AN_REG_EEE_ADV, &val1);
+                       bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD,
+                                       MDIO_AN_REG_LP_EEE_ADV, &val2);
+                       if ((val1 & val2) & 0x8) {
+                               DP(NETIF_MSG_LINK, "EEE negotiated\n");
+                               vars->eee_status |= SHMEM_EEE_ACTIVE_BIT;
+                       }
+
+                       if (val2 & 0x12)
+                               eee_shmem |= SHMEM_EEE_100M_ADV;
+                       if (val2 & 0x4)
+                               eee_shmem |= SHMEM_EEE_1G_ADV;
+                       if (val2 & 0x68)
+                               eee_shmem |= SHMEM_EEE_10G_ADV;
+
+                       vars->eee_status &= ~SHMEM_EEE_LP_ADV_STATUS_MASK;
+                       vars->eee_status |= (eee_shmem <<
+                                            SHMEM_EEE_LP_ADV_STATUS_SHIFT);
+               }
        }
 
        return link_up;
@@ -11243,7 +11544,8 @@ static struct bnx2x_phy phy_84833 = {
        .def_md_devad   = 0,
        .flags          = (FLAGS_FAN_FAILURE_DET_REQ |
                           FLAGS_REARM_LATCH_SIGNAL |
-                          FLAGS_TX_ERROR_CHECK),
+                          FLAGS_TX_ERROR_CHECK |
+                          FLAGS_EEE_10GBT),
        .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .mdio_ctrl      = 0,
@@ -12011,6 +12313,8 @@ int bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
                break;
        }
        bnx2x_update_mng(params, vars->link_status);
+
+       bnx2x_update_mng_eee(params, vars->eee_status);
        return 0;
 }
 
@@ -12023,6 +12327,9 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
        /* disable attentions */
        vars->link_status = 0;
        bnx2x_update_mng(params, vars->link_status);
+       vars->eee_status &= ~(SHMEM_EEE_LP_ADV_STATUS_MASK |
+                             SHMEM_EEE_ACTIVE_BIT);
+       bnx2x_update_mng_eee(params, vars->eee_status);
        bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4,
                       (NIG_MASK_XGXS0_LINK_STATUS |
                        NIG_MASK_XGXS0_LINK10G |