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

Commit 641ac5c0 authored by Akeem G. Abodunrin's avatar Akeem G. Abodunrin Committed by Jeff Kirsher
Browse files

igb: Support for SFP modules discovery



This patch adds support for SFP modules media type discovery for
SGMII, which will enable driver to detect supported external PHYs,
including 100baseFXSFP module.

Signed-off-by: default avatarAkeem G Abodunrin <akeem.g.abodunrin@intel.com>
Signed-off-by: default avatarCarolyn Wyborny <carolyn.wyborny@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 20a48412
Loading
Loading
Loading
Loading
+115 −5
Original line number Original line Diff line number Diff line
@@ -401,12 +401,82 @@ static s32 igb_init_mac_params_82575(struct e1000_hw *hw)
	return 0;
	return 0;
}
}


/**
 *  igb_set_sfp_media_type_82575 - derives SFP module media type.
 *  @hw: pointer to the HW structure
 *
 *  The media type is chosen based on SFP module.
 *  compatibility flags retrieved from SFP ID EEPROM.
 **/
static s32 igb_set_sfp_media_type_82575(struct e1000_hw *hw)
{
	s32 ret_val = E1000_ERR_CONFIG;
	u32 ctrl_ext = 0;
	struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
	struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
	u8 tranceiver_type = 0;
	s32 timeout = 3;

	/* Turn I2C interface ON and power on sfp cage */
	ctrl_ext = rd32(E1000_CTRL_EXT);
	ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
	wr32(E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA);

	wrfl();

	/* Read SFP module data */
	while (timeout) {
		ret_val = igb_read_sfp_data_byte(hw,
			E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET),
			&tranceiver_type);
		if (ret_val == 0)
			break;
		msleep(100);
		timeout--;
	}
	if (ret_val != 0)
		goto out;

	ret_val = igb_read_sfp_data_byte(hw,
			E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET),
			(u8 *)eth_flags);
	if (ret_val != 0)
		goto out;

	/* Check if there is some SFP module plugged and powered */
	if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) ||
	    (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) {
		dev_spec->module_plugged = true;
		if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) {
			hw->phy.media_type = e1000_media_type_internal_serdes;
		} else if (eth_flags->e100_base_fx) {
			dev_spec->sgmii_active = true;
			hw->phy.media_type = e1000_media_type_internal_serdes;
		} else if (eth_flags->e1000_base_t) {
			dev_spec->sgmii_active = true;
			hw->phy.media_type = e1000_media_type_copper;
		} else {
			hw->phy.media_type = e1000_media_type_unknown;
			hw_dbg("PHY module has not been recognized\n");
			goto out;
		}
	} else {
		hw->phy.media_type = e1000_media_type_unknown;
	}
	ret_val = 0;
out:
	/* Restore I2C interface setting */
	wr32(E1000_CTRL_EXT, ctrl_ext);
	return ret_val;
}

static s32 igb_get_invariants_82575(struct e1000_hw *hw)
static s32 igb_get_invariants_82575(struct e1000_hw *hw)
{
{
	struct e1000_mac_info *mac = &hw->mac;
	struct e1000_mac_info *mac = &hw->mac;
	struct e1000_dev_spec_82575 * dev_spec = &hw->dev_spec._82575;
	struct e1000_dev_spec_82575 * dev_spec = &hw->dev_spec._82575;
	s32 ret_val;
	s32 ret_val;
	u32 ctrl_ext = 0;
	u32 ctrl_ext = 0;
	u32 link_mode = 0;


	switch (hw->device_id) {
	switch (hw->device_id) {
	case E1000_DEV_ID_82575EB_COPPER:
	case E1000_DEV_ID_82575EB_COPPER:
@@ -470,15 +540,55 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
	 */
	 */
	hw->phy.media_type = e1000_media_type_copper;
	hw->phy.media_type = e1000_media_type_copper;
	dev_spec->sgmii_active = false;
	dev_spec->sgmii_active = false;
	dev_spec->module_plugged = false;


	ctrl_ext = rd32(E1000_CTRL_EXT);
	ctrl_ext = rd32(E1000_CTRL_EXT);
	switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {

	link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK;
	switch (link_mode) {
	case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
		hw->phy.media_type = e1000_media_type_internal_serdes;
		break;
	case E1000_CTRL_EXT_LINK_MODE_SGMII:
	case E1000_CTRL_EXT_LINK_MODE_SGMII:
		/* Get phy control interface type set (MDIO vs. I2C)*/
		if (igb_sgmii_uses_mdio_82575(hw)) {
			hw->phy.media_type = e1000_media_type_copper;
			dev_spec->sgmii_active = true;
			dev_spec->sgmii_active = true;
			break;
			break;
	case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
		}
		/* fall through for I2C based SGMII */
	case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
	case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
		/* read media type from SFP EEPROM */
		ret_val = igb_set_sfp_media_type_82575(hw);
		if ((ret_val != 0) ||
		    (hw->phy.media_type == e1000_media_type_unknown)) {
			/* If media type was not identified then return media
			 * type defined by the CTRL_EXT settings.
			 */
			hw->phy.media_type = e1000_media_type_internal_serdes;
			hw->phy.media_type = e1000_media_type_internal_serdes;

			if (link_mode == E1000_CTRL_EXT_LINK_MODE_SGMII) {
				hw->phy.media_type = e1000_media_type_copper;
				dev_spec->sgmii_active = true;
			}

			break;
		}

		/* do not change link mode for 100BaseFX */
		if (dev_spec->eth_flags.e100_base_fx)
			break;

		/* change current link mode setting */
		ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK;

		if (hw->phy.media_type == e1000_media_type_copper)
			ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII;
		else
			ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES;

		wr32(E1000_CTRL_EXT, ctrl_ext);

		break;
		break;
	default:
	default:
		break;
		break;
+16 −14
Original line number Original line Diff line number Diff line
@@ -68,6 +68,8 @@
#define E1000_I2CCMD_OPCODE_WRITE	0x00000000
#define E1000_I2CCMD_OPCODE_WRITE	0x00000000
#define E1000_I2CCMD_READY		0x20000000
#define E1000_I2CCMD_READY		0x20000000
#define E1000_I2CCMD_ERROR		0x80000000
#define E1000_I2CCMD_ERROR		0x80000000
#define E1000_I2CCMD_SFP_DATA_ADDR(a)	(0x0000 + (a))
#define E1000_I2CCMD_SFP_DIAG_ADDR(a)	(0x0100 + (a))
#define E1000_MAX_SGMII_PHY_REG_ADDR	255
#define E1000_MAX_SGMII_PHY_REG_ADDR	255
#define E1000_I2CCMD_PHY_TIMEOUT	200
#define E1000_I2CCMD_PHY_TIMEOUT	200
#define E1000_IVAR_VALID		0x80
#define E1000_IVAR_VALID		0x80
+2 −0
Original line number Original line Diff line number Diff line
@@ -528,6 +528,8 @@ struct e1000_dev_spec_82575 {
	bool global_device_reset;
	bool global_device_reset;
	bool eee_disable;
	bool eee_disable;
	bool clear_semaphore_once;
	bool clear_semaphore_once;
	struct e1000_sfp_flags eth_flags;
	bool module_plugged;
};
};


struct e1000_hw {
struct e1000_hw {
+124 −0
Original line number Original line Diff line number Diff line
@@ -340,6 +340,130 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data)
	return 0;
	return 0;
}
}


/**
 *  igb_read_sfp_data_byte - Reads SFP module data.
 *  @hw: pointer to the HW structure
 *  @offset: byte location offset to be read
 *  @data: read data buffer pointer
 *
 *  Reads one byte from SFP module data stored
 *  in SFP resided EEPROM memory or SFP diagnostic area.
 *  Function should be called with
 *  E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
 *  E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
 *  access
 **/
s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data)
{
	u32 i = 0;
	u32 i2ccmd = 0;
	u32 data_local = 0;

	if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
		hw_dbg("I2CCMD command address exceeds upper limit\n");
		return -E1000_ERR_PHY;
	}

	/* Set up Op-code, EEPROM Address,in the I2CCMD
	 * register. The MAC will take care of interfacing with the
	 * EEPROM to retrieve the desired data.
	 */
	i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
		  E1000_I2CCMD_OPCODE_READ);

	wr32(E1000_I2CCMD, i2ccmd);

	/* Poll the ready bit to see if the I2C read completed */
	for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
		udelay(50);
		data_local = rd32(E1000_I2CCMD);
		if (data_local & E1000_I2CCMD_READY)
			break;
	}
	if (!(data_local & E1000_I2CCMD_READY)) {
		hw_dbg("I2CCMD Read did not complete\n");
		return -E1000_ERR_PHY;
	}
	if (data_local & E1000_I2CCMD_ERROR) {
		hw_dbg("I2CCMD Error bit set\n");
		return -E1000_ERR_PHY;
	}
	*data = (u8) data_local & 0xFF;

	return 0;
}

/**
 *  e1000_write_sfp_data_byte - Writes SFP module data.
 *  @hw: pointer to the HW structure
 *  @offset: byte location offset to write to
 *  @data: data to write
 *
 *  Writes one byte to SFP module data stored
 *  in SFP resided EEPROM memory or SFP diagnostic area.
 *  Function should be called with
 *  E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
 *  E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
 *  access
 **/
s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data)
{
	u32 i = 0;
	u32 i2ccmd = 0;
	u32 data_local = 0;

	if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
		hw_dbg("I2CCMD command address exceeds upper limit\n");
		return -E1000_ERR_PHY;
	}
	/* The programming interface is 16 bits wide
	 * so we need to read the whole word first
	 * then update appropriate byte lane and write
	 * the updated word back.
	 */
	/* Set up Op-code, EEPROM Address,in the I2CCMD
	 * register. The MAC will take care of interfacing
	 * with an EEPROM to write the data given.
	 */
	i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
		  E1000_I2CCMD_OPCODE_READ);
	/* Set a command to read single word */
	wr32(E1000_I2CCMD, i2ccmd);
	for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
		udelay(50);
		/* Poll the ready bit to see if lastly
		 * launched I2C operation completed
		 */
		i2ccmd = rd32(E1000_I2CCMD);
		if (i2ccmd & E1000_I2CCMD_READY) {
			/* Check if this is READ or WRITE phase */
			if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) ==
			    E1000_I2CCMD_OPCODE_READ) {
				/* Write the selected byte
				 * lane and update whole word
				 */
				data_local = i2ccmd & 0xFF00;
				data_local |= data;
				i2ccmd = ((offset <<
					E1000_I2CCMD_REG_ADDR_SHIFT) |
					E1000_I2CCMD_OPCODE_WRITE | data_local);
				wr32(E1000_I2CCMD, i2ccmd);
			} else {
				break;
			}
		}
	}
	if (!(i2ccmd & E1000_I2CCMD_READY)) {
		hw_dbg("I2CCMD Write did not complete\n");
		return -E1000_ERR_PHY;
	}
	if (i2ccmd & E1000_I2CCMD_ERROR) {
		hw_dbg("I2CCMD Error bit set\n");
		return -E1000_ERR_PHY;
	}
	return 0;
}

/**
/**
 *  igb_read_phy_reg_igp - Read igp PHY register
 *  igb_read_phy_reg_igp - Read igp PHY register
 *  @hw: pointer to the HW structure
 *  @hw: pointer to the HW structure
+20 −0
Original line number Original line Diff line number Diff line
@@ -69,6 +69,8 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32  igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
s32  igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
s32  igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
s32  igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
s32  igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
s32  igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
s32  igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data);
s32  e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data);
s32  igb_copper_link_setup_82580(struct e1000_hw *hw);
s32  igb_copper_link_setup_82580(struct e1000_hw *hw);
s32  igb_get_phy_info_82580(struct e1000_hw *hw);
s32  igb_get_phy_info_82580(struct e1000_hw *hw);
s32  igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
s32  igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
@@ -157,4 +159,22 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw);
#define GS40G_CS_POWER_DOWN		0x0002
#define GS40G_CS_POWER_DOWN		0x0002
#define GS40G_LINE_LB			0x4000
#define GS40G_LINE_LB			0x4000


/* SFP modules ID memory locations */
#define E1000_SFF_IDENTIFIER_OFFSET	0x00
#define E1000_SFF_IDENTIFIER_SFF	0x02
#define E1000_SFF_IDENTIFIER_SFP	0x03

#define E1000_SFF_ETH_FLAGS_OFFSET	0x06
/* Flags for SFP modules compatible with ETH up to 1Gb */
struct e1000_sfp_flags {
	u8 e1000_base_sx:1;
	u8 e1000_base_lx:1;
	u8 e1000_base_cx:1;
	u8 e1000_base_t:1;
	u8 e100_base_lx:1;
	u8 e100_base_fx:1;
	u8 e10_base_bx10:1;
	u8 e10_base_px:1;
};

#endif
#endif
Loading