Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a1fbf40 authored by Yaniv Rosner's avatar Yaniv Rosner Committed by David S. Miller
Browse files

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: default avatarYaniv Rosner <yanivr@broadcom.com>
Signed-off-by: default avatarEilon Greenstein <eilong@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 55386fe8
Loading
Loading
Loading
Loading
+45 −46
Original line number Diff line number Diff line
@@ -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;