diff options
author | Yaniv Rosner <yanivr@broadcom.com> | 2012-11-26 22:46:31 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-28 10:59:21 -0500 |
commit | 5a1fbf4046ea05b20811178cb1423e27e3260051 (patch) | |
tree | 5cbf751769d7b592d1aec1dd75755182169d0c89 | |
parent | 55386fe88349706ae570522180d89892883af2b5 (diff) |
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>
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 91 |
1 files changed, 45 insertions, 46 deletions
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index e054921d45d8..a5fe2b96bf9f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | |||
@@ -4414,6 +4414,27 @@ static void bnx2x_warpcore_config_sfi(struct bnx2x_phy *phy, | |||
4414 | } | 4414 | } |
4415 | } | 4415 | } |
4416 | 4416 | ||
4417 | static void bnx2x_sfp_e3_set_transmitter(struct link_params *params, | ||
4418 | struct bnx2x_phy *phy, | ||
4419 | u8 tx_en) | ||
4420 | { | ||
4421 | struct bnx2x *bp = params->bp; | ||
4422 | u32 cfg_pin; | ||
4423 | u8 port = params->port; | ||
4424 | |||
4425 | cfg_pin = REG_RD(bp, params->shmem_base + | ||
4426 | offsetof(struct shmem_region, | ||
4427 | dev_info.port_hw_config[port].e3_sfp_ctrl)) & | ||
4428 | PORT_HW_CFG_E3_TX_LASER_MASK; | ||
4429 | /* Set the !tx_en since this pin is DISABLE_TX_LASER */ | ||
4430 | DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en); | ||
4431 | |||
4432 | /* For 20G, the expected pin to be used is 3 pins after the current */ | ||
4433 | bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1); | ||
4434 | if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G) | ||
4435 | bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1); | ||
4436 | } | ||
4437 | |||
4417 | static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, | 4438 | static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, |
4418 | struct link_params *params, | 4439 | struct link_params *params, |
4419 | struct link_vars *vars) | 4440 | struct link_vars *vars) |
@@ -4474,9 +4495,14 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, | |||
4474 | break; | 4495 | break; |
4475 | 4496 | ||
4476 | case PORT_HW_CFG_NET_SERDES_IF_SFI: | 4497 | case PORT_HW_CFG_NET_SERDES_IF_SFI: |
4477 | /* Issue Module detection */ | 4498 | /* Issue Module detection if module is plugged, or |
4499 | * enabled transmitter to avoid current leakage in case | ||
4500 | * no module is connected | ||
4501 | */ | ||
4478 | if (bnx2x_is_sfp_module_plugged(phy, params)) | 4502 | if (bnx2x_is_sfp_module_plugged(phy, params)) |
4479 | bnx2x_sfp_module_detection(phy, params); | 4503 | bnx2x_sfp_module_detection(phy, params); |
4504 | else | ||
4505 | bnx2x_sfp_e3_set_transmitter(params, phy, 1); | ||
4480 | 4506 | ||
4481 | bnx2x_warpcore_config_sfi(phy, params); | 4507 | bnx2x_warpcore_config_sfi(phy, params); |
4482 | break; | 4508 | break; |
@@ -4513,27 +4539,6 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, | |||
4513 | DP(NETIF_MSG_LINK, "Exit config init\n"); | 4539 | DP(NETIF_MSG_LINK, "Exit config init\n"); |
4514 | } | 4540 | } |
4515 | 4541 | ||
4516 | static void bnx2x_sfp_e3_set_transmitter(struct link_params *params, | ||
4517 | struct bnx2x_phy *phy, | ||
4518 | u8 tx_en) | ||
4519 | { | ||
4520 | struct bnx2x *bp = params->bp; | ||
4521 | u32 cfg_pin; | ||
4522 | u8 port = params->port; | ||
4523 | |||
4524 | cfg_pin = REG_RD(bp, params->shmem_base + | ||
4525 | offsetof(struct shmem_region, | ||
4526 | dev_info.port_hw_config[port].e3_sfp_ctrl)) & | ||
4527 | PORT_HW_CFG_TX_LASER_MASK; | ||
4528 | /* Set the !tx_en since this pin is DISABLE_TX_LASER */ | ||
4529 | DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en); | ||
4530 | /* For 20G, the expected pin to be used is 3 pins after the current */ | ||
4531 | |||
4532 | bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1); | ||
4533 | if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G) | ||
4534 | bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1); | ||
4535 | } | ||
4536 | |||
4537 | static void bnx2x_warpcore_link_reset(struct bnx2x_phy *phy, | 4542 | static void bnx2x_warpcore_link_reset(struct bnx2x_phy *phy, |
4538 | struct link_params *params) | 4543 | struct link_params *params) |
4539 | { | 4544 | { |
@@ -7833,7 +7838,6 @@ static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy, | |||
7833 | } | 7838 | } |
7834 | 7839 | ||
7835 | static void bnx2x_warpcore_power_module(struct link_params *params, | 7840 | static void bnx2x_warpcore_power_module(struct link_params *params, |
7836 | struct bnx2x_phy *phy, | ||
7837 | u8 power) | 7841 | u8 power) |
7838 | { | 7842 | { |
7839 | u32 pin_cfg; | 7843 | u32 pin_cfg; |
@@ -7875,10 +7879,10 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy, | |||
7875 | addr32 = addr & (~0x3); | 7879 | addr32 = addr & (~0x3); |
7876 | do { | 7880 | do { |
7877 | if ((!is_init) && (cnt == I2C_WA_PWR_ITER)) { | 7881 | if ((!is_init) && (cnt == I2C_WA_PWR_ITER)) { |
7878 | bnx2x_warpcore_power_module(params, phy, 0); | 7882 | bnx2x_warpcore_power_module(params, 0); |
7879 | /* Note that 100us are not enough here */ | 7883 | /* Note that 100us are not enough here */ |
7880 | usleep_range(1000, 2000); | 7884 | usleep_range(1000, 2000); |
7881 | bnx2x_warpcore_power_module(params, phy, 1); | 7885 | bnx2x_warpcore_power_module(params, 1); |
7882 | } | 7886 | } |
7883 | rc = bnx2x_bsc_read(params, phy, 0xa0, addr32, 0, byte_cnt, | 7887 | rc = bnx2x_bsc_read(params, phy, 0xa0, addr32, 0, byte_cnt, |
7884 | data_array); | 7888 | data_array); |
@@ -8464,7 +8468,7 @@ static void bnx2x_warpcore_hw_reset(struct bnx2x_phy *phy, | |||
8464 | struct link_params *params) | 8468 | struct link_params *params) |
8465 | { | 8469 | { |
8466 | struct bnx2x *bp = params->bp; | 8470 | struct bnx2x *bp = params->bp; |
8467 | bnx2x_warpcore_power_module(params, phy, 0); | 8471 | bnx2x_warpcore_power_module(params, 0); |
8468 | /* Put Warpcore in low power mode */ | 8472 | /* Put Warpcore in low power mode */ |
8469 | REG_WR(bp, MISC_REG_WC0_RESET, 0x0c0e); | 8473 | REG_WR(bp, MISC_REG_WC0_RESET, 0x0c0e); |
8470 | 8474 | ||
@@ -8487,7 +8491,7 @@ static void bnx2x_power_sfp_module(struct link_params *params, | |||
8487 | bnx2x_8727_power_module(params->bp, phy, power); | 8491 | bnx2x_8727_power_module(params->bp, phy, power); |
8488 | break; | 8492 | break; |
8489 | case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: | 8493 | case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: |
8490 | bnx2x_warpcore_power_module(params, phy, power); | 8494 | bnx2x_warpcore_power_module(params, power); |
8491 | break; | 8495 | break; |
8492 | default: | 8496 | default: |
8493 | break; | 8497 | break; |
@@ -8560,7 +8564,8 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy, | |||
8560 | u32 val = REG_RD(bp, params->shmem_base + | 8564 | u32 val = REG_RD(bp, params->shmem_base + |
8561 | offsetof(struct shmem_region, dev_info. | 8565 | offsetof(struct shmem_region, dev_info. |
8562 | port_feature_config[params->port].config)); | 8566 | port_feature_config[params->port].config)); |
8563 | 8567 | /* Enabled transmitter by default */ | |
8568 | bnx2x_sfp_set_transmitter(params, phy, 1); | ||
8564 | DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n", | 8569 | DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n", |
8565 | params->port); | 8570 | params->port); |
8566 | /* Power up module */ | 8571 | /* Power up module */ |
@@ -8593,14 +8598,12 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy, | |||
8593 | */ | 8598 | */ |
8594 | bnx2x_set_limiting_mode(params, phy, edc_mode); | 8599 | bnx2x_set_limiting_mode(params, phy, edc_mode); |
8595 | 8600 | ||
8596 | /* Enable transmit for this module if the module is approved, or | 8601 | /* Disable transmit for this module if the module is not approved, and |
8597 | * if unapproved modules should also enable the Tx laser | 8602 | * laser needs to be disabled. |
8598 | */ | 8603 | */ |
8599 | if (rc == 0 || | 8604 | if ((rc) && |
8600 | (val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) != | 8605 | ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == |
8601 | PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) | 8606 | PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER)) |
8602 | bnx2x_sfp_set_transmitter(params, phy, 1); | ||
8603 | else | ||
8604 | bnx2x_sfp_set_transmitter(params, phy, 0); | 8607 | bnx2x_sfp_set_transmitter(params, phy, 0); |
8605 | 8608 | ||
8606 | return rc; | 8609 | return rc; |
@@ -8612,11 +8615,13 @@ void bnx2x_handle_module_detect_int(struct link_params *params) | |||
8612 | struct bnx2x_phy *phy; | 8615 | struct bnx2x_phy *phy; |
8613 | u32 gpio_val; | 8616 | u32 gpio_val; |
8614 | u8 gpio_num, gpio_port; | 8617 | u8 gpio_num, gpio_port; |
8615 | if (CHIP_IS_E3(bp)) | 8618 | if (CHIP_IS_E3(bp)) { |
8616 | phy = ¶ms->phy[INT_PHY]; | 8619 | phy = ¶ms->phy[INT_PHY]; |
8617 | else | 8620 | /* Always enable TX laser,will be disabled in case of fault */ |
8621 | bnx2x_sfp_set_transmitter(params, phy, 1); | ||
8622 | } else { | ||
8618 | phy = ¶ms->phy[EXT_PHY1]; | 8623 | phy = ¶ms->phy[EXT_PHY1]; |
8619 | 8624 | } | |
8620 | if (bnx2x_get_mod_abs_int_cfg(bp, params->chip_id, params->shmem_base, | 8625 | if (bnx2x_get_mod_abs_int_cfg(bp, params->chip_id, params->shmem_base, |
8621 | params->port, &gpio_num, &gpio_port) == | 8626 | params->port, &gpio_num, &gpio_port) == |
8622 | -EINVAL) { | 8627 | -EINVAL) { |
@@ -8661,10 +8666,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params) | |||
8661 | DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); | 8666 | DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); |
8662 | } | 8667 | } |
8663 | } else { | 8668 | } else { |
8664 | u32 val = REG_RD(bp, params->shmem_base + | ||
8665 | offsetof(struct shmem_region, dev_info. | ||
8666 | port_feature_config[params->port]. | ||
8667 | config)); | ||
8668 | bnx2x_set_gpio_int(bp, gpio_num, | 8669 | bnx2x_set_gpio_int(bp, gpio_num, |
8669 | MISC_REGISTERS_GPIO_INT_OUTPUT_SET, | 8670 | MISC_REGISTERS_GPIO_INT_OUTPUT_SET, |
8670 | gpio_port); | 8671 | gpio_port); |
@@ -8672,10 +8673,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params) | |||
8672 | * Disable transmit for this module | 8673 | * Disable transmit for this module |
8673 | */ | 8674 | */ |
8674 | phy->media_type = ETH_PHY_NOT_PRESENT; | 8675 | phy->media_type = ETH_PHY_NOT_PRESENT; |
8675 | if (((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == | ||
8676 | PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) || | ||
8677 | CHIP_IS_E3(bp)) | ||
8678 | bnx2x_sfp_set_transmitter(params, phy, 0); | ||
8679 | } | 8676 | } |
8680 | } | 8677 | } |
8681 | 8678 | ||
@@ -9415,6 +9412,7 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy, | |||
9415 | bnx2x_cl45_read(bp, phy, | 9412 | bnx2x_cl45_read(bp, phy, |
9416 | MDIO_PMA_DEVAD, | 9413 | MDIO_PMA_DEVAD, |
9417 | MDIO_PMA_LASI_RXSTAT, &rx_alarm_status); | 9414 | MDIO_PMA_LASI_RXSTAT, &rx_alarm_status); |
9415 | bnx2x_8727_power_module(params->bp, phy, 0); | ||
9418 | return 0; | 9416 | return 0; |
9419 | } | 9417 | } |
9420 | } /* Over current check */ | 9418 | } /* Over current check */ |
@@ -13194,6 +13192,7 @@ static void bnx2x_check_over_curr(struct link_params *params, | |||
13194 | " error.\n", | 13192 | " error.\n", |
13195 | params->port); | 13193 | params->port); |
13196 | vars->phy_flags |= PHY_OVER_CURRENT_FLAG; | 13194 | vars->phy_flags |= PHY_OVER_CURRENT_FLAG; |
13195 | bnx2x_warpcore_power_module(params, 0); | ||
13197 | } | 13196 | } |
13198 | } else | 13197 | } else |
13199 | vars->phy_flags &= ~PHY_OVER_CURRENT_FLAG; | 13198 | vars->phy_flags &= ~PHY_OVER_CURRENT_FLAG; |