bnx2x: Fix SFP+ current leakage
authorYaniv Rosner <yanivr@broadcom.com>
Tue, 27 Nov 2012 03:46:31 +0000 (03:46 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 28 Nov 2012 15:59:21 +0000 (10:59 -0500)
Per measurements, the SFP+ suffered from small current leakage in two cases:
 - When no module was plugged and TX laser was disabled. The fix was to enable
   it, and when module is plugged in, check if it needs to be disabled.
 - When over-current event occurs due to invalid SFP+ module, the HW basically
   shuts down the current for this module, but the SW needs to complete this
   by issuing a power down via a GPIO.

Signed-off-by: Yaniv Rosner <yanivr@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c

index e054921..a5fe2b9 100644 (file)
@@ -4414,6 +4414,27 @@ static void bnx2x_warpcore_config_sfi(struct bnx2x_phy *phy,
        }
 }
 
+static void bnx2x_sfp_e3_set_transmitter(struct link_params *params,
+                                        struct bnx2x_phy *phy,
+                                        u8 tx_en)
+{
+       struct bnx2x *bp = params->bp;
+       u32 cfg_pin;
+       u8 port = params->port;
+
+       cfg_pin = REG_RD(bp, params->shmem_base +
+                        offsetof(struct shmem_region,
+                                 dev_info.port_hw_config[port].e3_sfp_ctrl)) &
+               PORT_HW_CFG_E3_TX_LASER_MASK;
+       /* Set the !tx_en since this pin is DISABLE_TX_LASER */
+       DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en);
+
+       /* For 20G, the expected pin to be used is 3 pins after the current */
+       bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1);
+       if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G)
+               bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1);
+}
+
 static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy,
                                       struct link_params *params,
                                       struct link_vars *vars)
@@ -4474,9 +4495,14 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy,
                        break;
 
                case PORT_HW_CFG_NET_SERDES_IF_SFI:
-                       /* Issue Module detection */
+                       /* Issue Module detection if module is plugged, or
+                        * enabled transmitter to avoid current leakage in case
+                        * no module is connected
+                        */
                        if (bnx2x_is_sfp_module_plugged(phy, params))
                                bnx2x_sfp_module_detection(phy, params);
+                       else
+                               bnx2x_sfp_e3_set_transmitter(params, phy, 1);
 
                        bnx2x_warpcore_config_sfi(phy, params);
                        break;
@@ -4513,27 +4539,6 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy,
        DP(NETIF_MSG_LINK, "Exit config init\n");
 }
 
-static void bnx2x_sfp_e3_set_transmitter(struct link_params *params,
-                                        struct bnx2x_phy *phy,
-                                        u8 tx_en)
-{
-       struct bnx2x *bp = params->bp;
-       u32 cfg_pin;
-       u8 port = params->port;
-
-       cfg_pin = REG_RD(bp, params->shmem_base +
-                               offsetof(struct shmem_region,
-                               dev_info.port_hw_config[port].e3_sfp_ctrl)) &
-                               PORT_HW_CFG_TX_LASER_MASK;
-       /* Set the !tx_en since this pin is DISABLE_TX_LASER */
-       DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en);
-       /* For 20G, the expected pin to be used is 3 pins after the current */
-
-       bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1);
-       if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G)
-               bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1);
-}
-
 static void bnx2x_warpcore_link_reset(struct bnx2x_phy *phy,
                                      struct link_params *params)
 {
@@ -7833,7 +7838,6 @@ static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy,
 }
 
 static void bnx2x_warpcore_power_module(struct link_params *params,
-                                       struct bnx2x_phy *phy,
                                        u8 power)
 {
        u32 pin_cfg;
@@ -7875,10 +7879,10 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy,
        addr32 = addr & (~0x3);
        do {
                if ((!is_init) && (cnt == I2C_WA_PWR_ITER)) {
-                       bnx2x_warpcore_power_module(params, phy, 0);
+                       bnx2x_warpcore_power_module(params, 0);
                        /* Note that 100us are not enough here */
                        usleep_range(1000, 2000);
-                       bnx2x_warpcore_power_module(params, phy, 1);
+                       bnx2x_warpcore_power_module(params, 1);
                }
                rc = bnx2x_bsc_read(params, phy, 0xa0, addr32, 0, byte_cnt,
                                    data_array);
@@ -8464,7 +8468,7 @@ static void bnx2x_warpcore_hw_reset(struct bnx2x_phy *phy,
                                    struct link_params *params)
 {
        struct bnx2x *bp = params->bp;
-       bnx2x_warpcore_power_module(params, phy, 0);
+       bnx2x_warpcore_power_module(params, 0);
        /* Put Warpcore in low power mode */
        REG_WR(bp, MISC_REG_WC0_RESET, 0x0c0e);
 
@@ -8487,7 +8491,7 @@ static void bnx2x_power_sfp_module(struct link_params *params,
                bnx2x_8727_power_module(params->bp, phy, power);
                break;
        case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
-               bnx2x_warpcore_power_module(params, phy, power);
+               bnx2x_warpcore_power_module(params, power);
                break;
        default:
                break;
@@ -8560,7 +8564,8 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
        u32 val = REG_RD(bp, params->shmem_base +
                             offsetof(struct shmem_region, dev_info.
                                     port_feature_config[params->port].config));
-
+       /* Enabled transmitter by default */
+       bnx2x_sfp_set_transmitter(params, phy, 1);
        DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n",
                 params->port);
        /* Power up module */
@@ -8593,14 +8598,12 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy,
         */
        bnx2x_set_limiting_mode(params, phy, edc_mode);
 
-       /* Enable transmit for this module if the module is approved, or
-        * if unapproved modules should also enable the Tx laser
+       /* Disable transmit for this module if the module is not approved, and
+        * laser needs to be disabled.
         */
-       if (rc == 0 ||
-           (val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) !=
-           PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER)
-               bnx2x_sfp_set_transmitter(params, phy, 1);
-       else
+       if ((rc) &&
+           ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
+            PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER))
                bnx2x_sfp_set_transmitter(params, phy, 0);
 
        return rc;
@@ -8612,11 +8615,13 @@ void bnx2x_handle_module_detect_int(struct link_params *params)
        struct bnx2x_phy *phy;
        u32 gpio_val;
        u8 gpio_num, gpio_port;
-       if (CHIP_IS_E3(bp))
+       if (CHIP_IS_E3(bp)) {
                phy = &params->phy[INT_PHY];
-       else
+               /* Always enable TX laser,will be disabled in case of fault */
+               bnx2x_sfp_set_transmitter(params, phy, 1);
+       } else {
                phy = &params->phy[EXT_PHY1];
-
+       }
        if (bnx2x_get_mod_abs_int_cfg(bp, params->chip_id, params->shmem_base,
                                      params->port, &gpio_num, &gpio_port) ==
            -EINVAL) {
@@ -8661,10 +8666,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params)
                        DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n");
                }
        } else {
-               u32 val = REG_RD(bp, params->shmem_base +
-                                offsetof(struct shmem_region, dev_info.
-                                         port_feature_config[params->port].
-                                         config));
                bnx2x_set_gpio_int(bp, gpio_num,
                                   MISC_REGISTERS_GPIO_INT_OUTPUT_SET,
                                   gpio_port);
@@ -8672,10 +8673,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params)
                 * Disable transmit for this module
                 */
                phy->media_type = ETH_PHY_NOT_PRESENT;
-               if (((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
-                    PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) ||
-                   CHIP_IS_E3(bp))
-                       bnx2x_sfp_set_transmitter(params, phy, 0);
        }
 }
 
@@ -9415,6 +9412,7 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy,
                        bnx2x_cl45_read(bp, phy,
                                MDIO_PMA_DEVAD,
                                MDIO_PMA_LASI_RXSTAT, &rx_alarm_status);
+                       bnx2x_8727_power_module(params->bp, phy, 0);
                        return 0;
                }
        } /* Over current check */
@@ -13194,6 +13192,7 @@ static void bnx2x_check_over_curr(struct link_params *params,
                                            " error.\n",
                         params->port);
                        vars->phy_flags |= PHY_OVER_CURRENT_FLAG;
+                       bnx2x_warpcore_power_module(params, 0);
                }
        } else
                vars->phy_flags &= ~PHY_OVER_CURRENT_FLAG;