bnx2x: Fix SFP+ current leakage

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>
This commit is contained in:
Yaniv Rosner 2012-11-27 03:46:31 +00:00 коммит произвёл David S. Miller
Родитель 55386fe883
Коммит 5a1fbf4046
1 изменённых файлов: 45 добавлений и 46 удалений

Просмотреть файл

@ -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;