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

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
...@@ -401,12 +401,82 @@ static s32 igb_init_mac_params_82575(struct e1000_hw *hw) ...@@ -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) ...@@ -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) {
case E1000_CTRL_EXT_LINK_MODE_SGMII: link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK;
dev_spec->sgmii_active = true; switch (link_mode) {
break;
case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
hw->phy.media_type = e1000_media_type_internal_serdes; hw->phy.media_type = e1000_media_type_internal_serdes;
break;
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;
break;
}
/* fall through for I2C based SGMII */
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;
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;
......
...@@ -61,20 +61,22 @@ ...@@ -61,20 +61,22 @@
/* Clear Interrupt timers after IMS clear */ /* Clear Interrupt timers after IMS clear */
/* packet buffer parity error detection enabled */ /* packet buffer parity error detection enabled */
/* descriptor FIFO parity error detection enable */ /* descriptor FIFO parity error detection enable */
#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ #define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */
#define E1000_I2CCMD_REG_ADDR_SHIFT 16 #define E1000_I2CCMD_REG_ADDR_SHIFT 16
#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 #define E1000_I2CCMD_PHY_ADDR_SHIFT 24
#define E1000_I2CCMD_OPCODE_READ 0x08000000 #define E1000_I2CCMD_OPCODE_READ 0x08000000
#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_MAX_SGMII_PHY_REG_ADDR 255 #define E1000_I2CCMD_SFP_DATA_ADDR(a) (0x0000 + (a))
#define E1000_I2CCMD_PHY_TIMEOUT 200 #define E1000_I2CCMD_SFP_DIAG_ADDR(a) (0x0100 + (a))
#define E1000_IVAR_VALID 0x80 #define E1000_MAX_SGMII_PHY_REG_ADDR 255
#define E1000_GPIE_NSICR 0x00000001 #define E1000_I2CCMD_PHY_TIMEOUT 200
#define E1000_GPIE_MSIX_MODE 0x00000010 #define E1000_IVAR_VALID 0x80
#define E1000_GPIE_EIAME 0x40000000 #define E1000_GPIE_NSICR 0x00000001
#define E1000_GPIE_PBA 0x80000000 #define E1000_GPIE_MSIX_MODE 0x00000010
#define E1000_GPIE_EIAME 0x40000000
#define E1000_GPIE_PBA 0x80000000
/* Receive Descriptor bit definitions */ /* Receive Descriptor bit definitions */
#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ #define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
......
...@@ -528,6 +528,8 @@ struct e1000_dev_spec_82575 { ...@@ -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 {
......
...@@ -340,6 +340,130 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) ...@@ -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
......
...@@ -69,6 +69,8 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); ...@@ -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); ...@@ -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
...@@ -142,6 +142,8 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ...@@ -142,6 +142,8 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{ {
struct igb_adapter *adapter = netdev_priv(netdev); struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw; struct e1000_hw *hw = &adapter->hw;
struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
u32 status; u32 status;
if (hw->phy.media_type == e1000_media_type_copper) { if (hw->phy.media_type == e1000_media_type_copper) {
...@@ -181,30 +183,22 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ...@@ -181,30 +183,22 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
ecmd->phy_address = hw->phy.addr; ecmd->phy_address = hw->phy.addr;
ecmd->transceiver = XCVR_INTERNAL; ecmd->transceiver = XCVR_INTERNAL;
} else { } else {
ecmd->supported = (SUPPORTED_1000baseT_Full | ecmd->supported = (SUPPORTED_FIBRE |
SUPPORTED_100baseT_Full |
SUPPORTED_FIBRE |
SUPPORTED_Autoneg | SUPPORTED_Autoneg |
SUPPORTED_Pause); SUPPORTED_Pause);
if (hw->mac.type == e1000_i354)
ecmd->supported |= SUPPORTED_2500baseX_Full;
ecmd->advertising = ADVERTISED_FIBRE; ecmd->advertising = ADVERTISED_FIBRE;
if (hw->mac.type == e1000_i354) {
switch (adapter->link_speed) { ecmd->supported |= SUPPORTED_2500baseX_Full;
case SPEED_2500: ecmd->advertising |= ADVERTISED_2500baseX_Full;
ecmd->advertising = ADVERTISED_2500baseX_Full; }
break; if ((eth_flags->e1000_base_lx) || (eth_flags->e1000_base_sx)) {
case SPEED_1000: ecmd->supported |= SUPPORTED_1000baseT_Full;
ecmd->advertising = ADVERTISED_1000baseT_Full; ecmd->advertising |= ADVERTISED_1000baseT_Full;
break; }
case SPEED_100: if (eth_flags->e100_base_fx) {
ecmd->advertising = ADVERTISED_100baseT_Full; ecmd->supported |= SUPPORTED_100baseT_Full;
break; ecmd->advertising |= ADVERTISED_100baseT_Full;
default:
break;
} }
if (hw->mac.autoneg == 1) if (hw->mac.autoneg == 1)
ecmd->advertising |= ADVERTISED_Autoneg; ecmd->advertising |= ADVERTISED_Autoneg;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment